UART(通用异步传输收发器)简介
- 串行通信简介
我们的实际生活中,常见的通信方式有很多,例如:蓝牙,WIFI,网线,红外,HDMI
VGA,USB,I2C,SP1,UART等,每种都有自己的特点以及常用的场合。今天,我们就来看看我们的串行通信。通用异步收发器简称UART,即“Universal
Asynchronous Receiver Transmitter”,它串行通信是指计算机与I/O
设备之间数据传输的各位是按顺序依次一位接一位进行传送。通常数据在一根数据线上传输。具体情况如下:用来传输串行数据:发送数据时,CPU将并行数据写入UART,UART按照一定的格式在一根电线上串行发出;接收数据时,UART检测另一根电线上的信号,将串行收集放在缓冲区中,CPU即可读取UART获得这些数据。UART之间以全双工方式传输数据,最精简的连线方法只有三根电线:TxD用于发送数据,RxD用于接收数据,Gnd用于给双方提供参考电平,连线如图7.1所示:transmission recvfrom
我们通俗所使用的串口硬件格式为:
实质上,大部分我们只是使用了里面的3根线。
UART使用标准的TTL/CMOS逻辑电平(0~5V、0~3.3V、0~2.5V或0~1.8V四种)来表示数据,高电平表示1,低电平表示0。为了增强数据的抗干扰能力、提高传输长度,通常将TTL/CMOS逻辑电平转换为RS-232逻辑电平,3~12V表示0,-3~-12V表示1。
TxD、RxD数据线以“位”为最小单位传输数据。帧(frame)由具有完整意义的、不可分割的若干位组成,它包含开始位、数据位、较验位(需要的话)和停止位。发送数据之前,UART之间要约定好数据的传输速率(即每位所占据的时间,其倒数称为波特率)、数据的传输格式(即有多少个数据位、是否使用较验位、是奇较验还是偶较验、有多少个停止位)。
电脑的电平—RS232 单片机的电平—TTL
发送形式如下:
上图演示了UART使用7个数据位、偶校验、2个停止位的格式传输字符‘A’(二进制值为0b1000001)时,TTL/COM逻辑电平和RS-232逻辑电平对应的波形。
传输流程如下:’A’===>1bytes=8bit65(614+ 1)= 1000001
(1)平时数据线处于空闲状态(1状态)
(2)当要发送数据时,UART改变TxD数据线的状态(变为0状态)并维持1位的时间,这样接收方检测到开始位后,在等待1.5位的时间就开始一位一位地检测数据线的状态得到所传输的数据。
(3)UART一帧中可以存放有5、6、7或8位的数据,发送方一位一位地改变数据线的状态将他们发送出去,首先发送最低位,这个我们叫做发送数据位。在发送的过程中,我们需要约定好传输的速率,这个速率我们叫做波特率-----每秒传输的数据位数。
(4)如果使用校验功能,UART在发送完数据后,还要发送1位校验位。有两种校验方法:奇校验、偶校验——数据位连同校验位中,1的数据等于奇数或偶数。
(5)最后,发送停止位,数据线恢复到空闲状态(1状态)。停止位的长度有3种:1位、1.5位、2位。
- 串行通信简介
要求:使用UART2连接PC板和开发板,输出调试信息。实现数据的收发。
[1]阅读原理图—
<<FS4412-DevBoard-V5.pdf>>,我们开发板上使用的COM2,COM2的左侧面
写着CON7,我们搜索CON7这个标号即可。
如上图所示,COM2分别与SP232 的13、14 引脚相连,通过SP3232
的BUF_XuTXD2/UART_AUDIO_TXD和BUF_XuRXD2/UART_AUDIO_RXD 引脚实现TTl 3.3V
电平转换,3.3V 电平转换再通过U8 转变为1.8V电平和CPU
通讯。这样对EXYNOS4412就可以实现在PC 串口上显示数据。SP3232 起到变压器的作用。
再次阅读我们的核心板,通过网络标号-----UART_AUDIO_TXD,找到我们的信息。
通过阅读原理图,我们知道,我们的COM2收通过转换芯片,然后接到我们的4412芯片的
AC20和AC25接口,我们根据网络标号,我们知道我们要使用GPA1_0和GPA1_1.
[2]阅读芯片手册,查询这个两个寄存器的用途,然后在查看我们关于uart的章节讲解。
<1>阅读这几个寄存器的信息,我们发现内容如下:
即
GPA1CON[0] = 0X2 GPA1_0设置为UART_2_RXD
GPA1CON[1] = 0X2 GPA1_1 设置为UART_2_TXD
基本原理讲解:
Exynos4412 中UART,有4 个独立的通道,每个通道都可以工作于中断模式或DMA
模式,即 UART 可以发出中断或 DMA 请求以便在UART 、CPU 间传输数据。UART
由波特率发生器、发送器、接收器和控制逻辑组成。
** 使用系统时钟时,Exynos4412 的 UART 波特率可以达到 4Mbps
。波特率可以通过编程进行 。**
** Exynos4412 UART 的通道 0有 256 字节的发送 FIFO 和 256 字节的接收FIFO
;通道 1、4有 64 字节的发送 FIFO 和 64 字节的接收FIFO;通道 2、3有 16
字节的发送FIFO 和 16 字节 的接收 FIFO 。发送数据时, CPU 先将数据写入发送FIFO
中,然后 UART 会自动将FIFO 中的数据复制到“发送移位器” (Transmit Shifter
)中,发送移位器将数据一位一位地发送到 TxDn 数据线上 (根据设定的格式,插入开始位
、较验和停止)。接收数据时,“移位器” (Receive Shifter )将 RxDn
数据线上的数据一位一位的接收进来,然后复制到FIFO 中, CPU即可从中读取数据。**
** Exynos4412 UART的每个通道支持停止位有 1位、 2位,数据位有 5、6、7或
8位,支持校验功能,另外还有红外发送 /接收功能。**
Exynos4412 UART结构图:
<2>UART时钟源的设置
Exynos4412 UART的时钟源有八种选择: XXTI 、XusbXTI 、SCLK_HDMI24M 、SCLK_USBPHY0
、 SCLK_HDMIPHY 、SCLKMPLL_USER_T 、SCLKEPLL 、SCLKVPLL ,由 CLK_SRC_PERIL0
寄存器控制。选择好时钟源后,由于频率太高,需要通过 DIVUART0 ~4设置分频系数
设置分频系数 ,由 CLK_DIV_PERIL0 寄存器控制。 从分频器得到的时钟被称为SCLK UART
。 SCLK UART 经过上图中的“ UCLK Generator”后,得到UCLK ,它的频率就是UART
的波特率。“ Generator UCLK Generator ”通过这 2个寄存器来设置: UBRDIVn
、UFRACVALn (在下面描述)。
我们这里最终得到的SCLK_UART的大小为100MHZ.----(在uboot.bin启动的时候可以看到)。
我们使用的是COM2,即UART2,所以关于波特率所涉及到的寄存器是UBRDEV2和UFRACVAL2。
关于波特率和这两个寄存器之间的计算关系,datasheet写的很明白:
根据给定的波特率、所选择时钟源频率,可以通过以下公式计算 UBRDIVn 寄存器 (n 为
0~4,对应 5个 UART 通道 )的值。
** UBRDIVn = (int)( UART clock / ( buad rate x 16) ) – 1**
上式计算出来的 UBRDIVn 寄存器值不一定是整数, UBRDIVn
寄存器取其整数部分,小部分由 UFRACVALn 寄存器设置, UFRACVALn
寄存器的引入,使产生波特率更加精确。
我们这里 UART clock为100MHz,要求波特率为115200 bps,则:
100000000/(115200 x 16) – 1 = 54.25 – 1 = 53.25
UBRDIVn = 整数部分 = 53
UFRACVALn/16 = 小数部分 = 0.25
UFRACVALn = 4
所以设置寄存器 UBRDIV2 = 53 ,UFRACVAL2 = 4
<3>设置uart相关参数
ULCON2[6] 红外线模式还是普通模式 ,我们选择普通模式 0
ULCON2[1:0] 数据位数 这里我们设置为8bit ,0x3
ULCON2[2] 停止位位数, 我们设置为1bit,0x0
ULCON2[5::3] 校验位 我们设置为无 0x0
综上,ULCON2 = 0b11 = 0x03
- 设置uart 收发模式 UCONn
UCON2 [1:0] = 01, 接收 poll模式
UCON2[3:2] = 01, 发送 poll模式
- 关闭发送接收FIFO
UFCON2 [0] = 0 关闭发送接收FIFO
- 实现发送数据
发送数据
将要发送的8bit数据放入UTXHn寄存器低8位
UTXH2 = txdata
检测数据是否发出完成
检测是否发送完毕,检测寄存器UTRSTAT2的bit 1是否置1,置1表示发送完成。
发送代码:
// write fifo
UTXH[0:7] = data;
// wait finish
while (!(UTRSTAT[1] == 0b1));
8.接收数据
将寄存器URXHn的低8位数据取出来。
但是要等到receive buffer data
ready,也即是有数据可读。查看是否有数据可读可以查看寄存器UTRSTAT2的bit
0,置1表示有数据可读。
接收代码:
// wait for data
while (!(UTRSTAT[0] == 0b1));
// read fifo
data = URXH[0:7];
代码如下:
Uart.c
#define GPA1CON 0x11400020
#define UBRDIV 0x13820028
#define UFRACVAL 0x1382002C
#define ULCON 0x13820000
#define UCON 0x13820004
#define UFCON 0x13820008
#define UTRSTAT 0x13820010
#define UTXH 0x13820020 //发送数据
#define URXH 0x13820024 //接收数据
//==============LED3=
#define GPX1CON 0x11000C20 //[3:0] 0001
#define GPX1DAT 0x11000C24 // [0]—1表示高电平,0表示低电平
//把改地址里面存储的数据,返回来。
#define reg_read(addr) (*(unsigned int *)(addr))
//把var这个值,写到addr这个地址对应的内单元中
#define reg_write(addr,var) (*(unsigned int *)(addr)=(var))
void delay_ms(int time)
{
int i, j;
while(time–)
{
for (i = 0; i < 5; i++)
for (j = 0; j < 514; j++);
}
}
void uart_init()
{
//1.配置功能寄存器–收发功能GPA1.CON [0-7]为设置为0x22
unsigned int con = reg_read(GPA1CON);
reg_write(GPA1CON,(con &~(0xff))|(0x22)); //设置为UART_2_RXD
//2.配置波特率。通过UBRDIV2配置整数,通过UFRACVAL2配置小数
//100M晶振
reg_write(UBRDIV,53) ; //设置波特率
reg_write(UFRACVAL,4) ;
//3.关闭校验位,1bit停止位,8bit数据位
con = reg_read(ULCON);
reg_write(ULCON,(con & ~(0xff)) | 0x3);
//3.选择发送和接收为轮询或则中断模式
con = reg_read(UCON);
reg_write(UCON,(con &(~0Xf))|0X5);
//4.关闭FIFO队列
con = reg_read(UFCON);
reg_write(UFCON,con&(~1));
}
void uart_send(unsigned char data)
{
//1.先写数据,在判断上一次是否发送
//完毕,若是发送完毕,就发送该次
reg_write(UTXH,data);
//若是没有发送完毕,一直等待发送。
while((reg_read(UTRSTAT) & 0x2) == 0);
return ;
}
unsigned char uart_recv()
{
unsigned char data;
//若是没有接受到数据,一直等待接收
while(((reg_read(UTRSTAT)&0x1)==0));
//由于是低8bit有效,我们只取一个字节的大小.
data= (unsigned char )(reg_read(URXH)&0xff);
return data;
}
//=======================================
void led3_init()
{
unsigned int con ;
//LED3;
con = *((unsigned int *)GPX1CON);
//先清0,在 把对应的4位设置为0001 [31:28]全部变成0
con = con & ~0xf;
con = con | 0x1;
*(unsigned int *)GPX1CON = con;
}
void led3_on()
{
unsigned int con = 0;
con = *((unsigned int *)GPX1DAT);
con = con | (0x1);
*((unsigned int *)GPX1DAT) = con;
return ;
}
void led3_off()
{
unsigned int con = 0;
con = *((unsigned int *)GPX1DAT);
con = con & ~(0x1);
*((unsigned int *)GPX1DAT) = con;
return ;
}
int main()
{
uart_init();
led3_init();
unsigned char ch;
while(1)
{
ch = uart_recv();
switch(ch)
{
case ‘0’:
led3_off();
break;
case ‘1’:
led3_on();
break;
}
uart_send(ch);
delay_ms(5);
}
}
Makfile文件如下:
all:start.s uart.c
arm-none-linux-gnueabi-gcc -g -c -O0 start.s -o start.o
arm-none-linux-gnueabi-gcc -g -c -O0 uart.c -o uart.o
arm-none-linux-gnueabi-ld start.o uart.o -Ttext 0x40008000 -o test.elf
arm-none-linux-gnueabi-objcopy -O binary test.elf uart.bin
clean:
rm *.elf *.o *.bin