以下内容源于韦东山课程的学习与整理,如有侵权请告知删除。
一、UART硬件简介
UART,全称是“Universal Asynchronous Receiver Transmitter”,即“通用异步收发器”,也就是我们日常说的“串口”。
它在嵌入式中用途非常广泛,主要包括:
(1)打印调试信息。
(2)外接各种模块,比如GPS、蓝牙等模块。
UART广受欢迎,因为其结构简单、稳定可靠,通过三根线即可,即发送线、接收线、接地线。
二、数据传输流程
2.1 串口的参数
2.2 数据传输流程
2.3 举例说明(传输字符'A')
这里先简单说一下 TTL/CMOS 电平标准、RS232电平标准:
电平信号是用信号线电平减去参考线电平得到电压差,再由这个电压差决定传输值是1还是0。但是在电平信号时多少V代表1,多少V代表0不是固定的,取决于电平标准。有两个电平标准,即RS232 电平标准和 TTL/CMOS 电平标准。
TTL/CMOS电平标准中,+5V表示1,0V表示0(其实应该是一个电压范围表示1,比如+5V~+3V,而不是只有+5V才表示1,同理0也是)。
RS232电平标准中,-3V~-15V(-12V?)表示1,+3~+15V(+12V)表示0。
电平标准 | 如何表示"1" | 如何表示“0” |
TTL/CMOS电平 | +5V ~ X | 0V ~ Y |
RS232 | -3V ~ -15V(-12V) | +3~+15V(+12V) |
RS232的电平比TTL/CMOS电平高,抗干扰性更强,能传输更远的距离,因此在工业上用的比较多。
比如字符'A',其对应的编码是0x41,或者0b01000001。采用不同电平标准,其传输的流程如下:
步骤细述如下:
三、看原理图确定硬件如何连接
(1)ARM芯片上的串口都是TTL电平的,然后通过板子上或者外接的电平转换芯片,转换为RS232接口,连接到电脑的RS232串口上。
比如MINI2440开发板,其串口连接方式如下:
(2)现在的电脑很少配置有采用RS232电平的串口接口 ,但几乎都配置有usb口。因此可以使用USB串口芯片,将ARM芯片上的TTL电平,转换为USB串口协议。也就是开发板可以通过USB口与电脑进行通信。
比如JZ2440,串口连接方式如下:
由原理图可以看出,JZ2440开发板上将三个串口全部引出,其中UART0设置了板载的USB转串口电路,只需连接板上的USB口就可以,所以接下来我们使用UART0进行数据收发实验。
四、看芯片手册进行寄存器设置
4.1 设置引脚为UART功能(GPHCON)、开启片内上拉(GPHUP)
由原理可知,UART0的TXD0对应着GPH2引脚,RXD0对应着GPH3引脚。
这两个引脚都是普通的GPIO引脚,因此需要设置引脚复用功能,设置为UART0的引脚:
由于串口的两根信号线在空闲的时候需要保持高电平,所以要开启这两个引脚的片内上拉电阻:
4.2 设置串口数据帧的格式(ULCONn寄存器)
我们可以通过ULCONn寄存器,来设置串口数据帧的格式。
比如是否采用红外模式、校验模式、停止位宽度、数据位宽度等内容。
这里的n=0,表示设置串口0的数据帧格式,红框圈出的是编程时采用的设置。
4.3 设置波特率(UBRDIVn寄存器)
比如已知串口所用的时钟源 PCLK=50Mhz
,想要设置串口的波特率为115200bit/s
,则UBRDIVn的值根据公式可以设置为26,如下所示:
UBRDIVn = (int)(50000000/(115200*16)) - 1
= (int)(50000000/1843200) - 1
= (int)(27.13) - 1
= 27 - 1
= 26
4.4 设置时钟源、接收/发送模式(UCONn寄存器)
在JZ2440开发板——S3C2440的时钟体系中,我们设置了时钟PCLK=50Mhz,
所以这里在此基础上选择PCLK作为UART0的波特率发生器的时钟来源:
为了简单起见,不使用中断模式和DMA模式,直接采用查询模式(polling mode
):
4.5 发送 / 接收状态寄存器(UTRSTATn)
如下图所示,发送器包括发送缓冲寄存器、发送移位寄存器。UTRSTATn[2]=1时,表示发送器空,说明不仅发送缓冲寄存器为空,发送移位寄存器中也没有数据;UTRSTATn[1]=1时,只表示发送缓冲寄存器为空。因此我们一般通过判别 UTRSTATn[2] 是否为1,来判断是否发送完成(发送移位寄存器中没有数据,才说明发送完成)。另外我们通过 UTRSTATn[0] 是否为1,来判断是否收到数据。
4.6 发送缓冲寄存器(UTXHn)、接收缓冲寄存器(URXHn)
值得注意的是,在使用指针访问这两个寄存器时,不能使用int型指针,
因为int型指针访问的是4个字节的数据,而此处只能访问 1 个字节数据,所以要使用char型指针
:
#define UTXH0 (*(volatile unsigned char *)(0x50000020)) //UART 0 transmission hold
#define URXH0 (*(volatile unsigned char *)(0x50000024)) //UART 0 receive buffer
五、编程实践
5.1 相关代码文件
1、start.S文件
内容与JZ2440开发板——S3C2440的时钟体系中的start.S文件完全一致。
2、main.c文件
文件内容如下:
#include "s3c2440_soc.h"
#include "uart.h"
int main(void)
{
unsigned char c;
uart0_init();
puts("Hello, world!\n\r");
while(1)
{
c = getchar();
//有些系统回车可能是'\r',则输出'\n'来换行
if (c == '\r')
{
putchar('\n');
}
//有些系统回车可能是'\n',则输出'\r'来换行
if (c == '\n')
{
putchar('\r');
}
putchar(c);
}
return 0;
}
3、uart.c文件
对GPHCON、GPHUP等寄存器进行的是位操作;对UCON0等寄存器,则是利用寄存器位查看小工具计算好之后对其进行赋值。这两者有什么区别或者优劣之分吗?
#include "s3c2440_soc.h"
/* 115200,8n1 */
void uart0_init()
{
/* 设置引脚用于串口 */
/* GPH2,3用于TxD0, RxD0 */
GPHCON &= ~((3<<4) | (3<<6));
GPHCON |= ((2<<4) | (2<<6));
/* 使能内部上拉 */
GPHUP &= ~((1<<2) | (1<<3));
/* PCLK,中断/查询模式 */
UCON0 = 0x00000005;
/* 设置波特率 */
/* UBRDIVn = (int)( UART clock / ( buad rate x 16) ) –1
* UART clock = 50M
* UBRDIVn = (int)( 50000000 / ( 115200 x 16) ) –1 = 26
*/
UBRDIV0 = 26;
/* 设置数据格式 */
ULCON0 = 0x00000003; /* 8n1: 8个数据位, 无较验位, 1个停止位 */
}
//通过串口0输出显示数字c对应的字符
int putchar(int c)
{
/* UTRSTAT0、UTXH0 */
//UTRSTAT0[2]为0时(表示发送器不为空),这个while会一直循环下去
//这意味着发送器还在忙着其他发送任务,你要等它把其他数据发送结束,
//才能接受你这次的数据传输
while (!(UTRSTAT0 & (1<<2)));//某个数字m的第n位,其表达式是 m&(1<<n)
//退出循环,表示上一次数据发送完毕了,可以开始你这次的数据传输了
UTXH0 = (unsigned char)c; //将数值写入UTXH0即可,会自动发送出去
//UTXH0是 unsigned char 类型,所以要转换
}
//通过串口0获取字符
int getchar(void)
{
while (!(UTRSTAT0 & (1<<0)));//注意这个分号不能少(其实可以换一行写分号)
return URXH0;
}
int puts(const char *s)
{
while (*s)
{
putchar(*s);//这里*s是一个字符,即char类型,但putchar的参数是int类型
//传递时会自动转换?
s++;
}
}
5.2 烧写与运行
在uboot的shell界面选择n菜单,通过dnw+usb下载线的方式,(要想编译成功,Makefile文件中arm-linux-gcc命令后要添加-nostdlib选项)将上面编译生成的uart.bin文件烧写到NandFlash中,然后以NandFlash方式启动,显示如下:
Hello, world! #固定输出这行内容。
abcdefg #下面是你敲什么内容就显示什么内容。
123
38324ajlkdjalk
dasdjakdakjd
六、总结
1、理解了串口通信的协议(串口的参数、数据传输流程);
2、掌握S3C2440这颗芯片的串口使用方法(如何编程使用串口);
3、通过这个案例,掌握了分析某个芯片模块方法,即:
(1)看原理图确定硬件如何连接;
(2)看芯片手册进行寄存器设置。