UART的介绍
UART(Universal Asynchronous Receiver/Transmitter,通用异步收发器),俗称串口。
通常计算机与外部设备通信的端口分为并行与串行:
a. 并行端口是指数据的各个位同时进行传送,其特点是传输速度快,但是当传输距离远、位数多时,通信线路变得复杂且成本提高;
b. 串行通信是指数据一位位地顺序传送,其特点是适合于远距离通信,通信线路简单,只要一对传输线就可以实现全双工通信,从而大大降低成本。
串行通信又分为异步与同步两种类型,两者之间最大的差别是:异步通信以一个字符为单位传输,同步通信以一个字符序列为单位传输。
UART的数据传输
目前常用的串口有9针和25针,最简单且常用的是三线制接法,即信号地、接收数据和发送数据三引脚相连。
数据线以“位”为最小单位传输数据;帧(frame)由具有完整意义的、不可分割的若干位组成,包括开始位、数据位、校验位(可有可无)和停止位。
发送数据之前,收发双方要约定好数据的传输速率(即传送一位所需的时间,其倒数成为波特率)和数据的传输格式(即多少个数据位、是否校验、有多少个停止位)。
注意:
1)数据线上没有数据传送时处于“空闲”状态,对应高电平,即1状态;
2)当要发送数据时,串口改变发送数据线的状态为低电平,即0状态;
3)串口一帧中可以有4、5、6、7或8位的数据,发送端一位位地改变数据线的电平状态将其发送,最低位先发送;
4)数据位加上校验位后,使得“1”的位数为偶数(偶校验)或奇数(奇校验),以此来校验数据传输的正确性;
5)最后发送停止位,数据线恢复到“空闲”状态,即1状态。停止位的长度有3种:1位、1.5位和2位。
S5PV210的串口介绍
S5PV210有4个串口模块,提供了4个独立的异步串行输入/输出端口。它们可工作于中断模式或DMA模式,支持3Mbps的位速率。
每个串口通道包含2个FIFO缓存,用于接收和发送数据,其中通道0的FIFO支持256个字节,通道1支持64个字节,通道2和通道3支持16个字节。
串口还包含可编程的波特率、红外收发、1或2个停止位、5~8位数据位、校验位,并且每个串口模块都由波特率产生装置、发送装置、接收装置和控制单元组成。其中,波特率可由外部时钟或系统时钟提供,发送/接收装置分别由各自的FIFO和数据移位寄存器组成。
S5PV210的串口使用
本例使用串口通道0和通道1,具体操作步骤如下:
1)将串口通道的引脚配置为串口功能
所谓引脚配置,即上一章所讲的GPIO引脚设置,这里需要将GPA_0、GPA_1设置为串口的接收功能(RXD0)和发送功能(TXD0)。
2)时钟源选择及工作模式设置
通过配置UCON0寄存器(起始地址为0xE290_0004)选择时钟源和工作模式。
其中,UCON0[10]:S5PV210的时钟源可由外部时钟PCLK(0)或系统时钟SCLK_UART(1)提供。
3)设置波特率
根据设置的波特率(bps)和选择的时钟频率,利用以下公式确定UBRDIV0寄存器的值:
DIV_VAL = (所选时钟频率/(bps×16)-1),
该公式计算出的UBRDIV0寄存器的值未必是整数,UBRDIV0寄存器取其整数部分;
DIV_VAL小数部分*16,取整,再查表,就得UDIVSLOT0的值。
4)设置数据传输格式
通过ULCON0寄存器(地址为0xE290_0000)设置红外模式、校验模式、停止位宽度、数据位宽度,如下表所示:
问:数据位8位,停止位1位,奇校验,正常串口,怎么设置?
ULCON0=(3<<0)|(0<<2)|(4<<3)|(0<<6)
5)启用或禁用FIFO
配置寄存器 UFCONn实现是否使用FIFO、设置FIFO触发阈值、FIFO复位功能。
FIFO触发阈值:发送FIFO中有多少个数据时产生中断;接收FIFO中有多少个数据时产生中断。
6)收发数据(UTXH0寄存器,URXH0寄存器)
发送数据:CPU将数据写入寄存器 UTXHn ,UARTn将数据保存到缓冲区中,并自动发送出去;
接收数据:CPU读取URXHn寄存器即可获得数据。
7)收发数据状态的控制(UTRSTAT0寄存器)
由寄存器 UTRSTATn 可知数据是否收发完毕。
发送:while(!(UTRSTAT0 & (1<<2)){}
接受:while(!(UTRSTAT0 & (1<<0)){}
8)数据传输时的错误控制
由bit[0]~bit[3]分别表示是否溢出、是否校验错误、是否帧错误、是否检测到break信号。错误状态被读取后,寄存器自动清零。
实验实例
实验目的:通过串口接收数据“1”使LED1亮,“2”使LED1灭,“3”使LED2亮,“4”使LED2灭。
实验原理:UART0控制器与一个RS-232的电平转换模块相连,配置好UART0后,CPU发送给UART0的数据可以通过串口输出到PC机上(PC机上需要接收工具,比如SecureCRT)。
程序包括三部分:
启动代码UART设置主程序(点亮LED)
默认PCLK为时钟源,且为66MHz。
1.启动代码
参考上一章GPIO实例,实现代码一致。
2.UART设置
(1)UART初始化
// GPIO、UART寄存器地址#define GPA0CON*((volatile unsigned int *)0xE0200000)#define ULCON0 *((volatile unsigned int *)0xE2900000)#define UCON0 *((volatile unsigned int *)0xE2900004)#define UFCON0 *((volatile unsigned int *)0xE2900008)#define UTRSTAT0 *((volatile unsigned int *)0xE2900010)#define UTXH0 *((volatile unsigned int *)0xE2900020)#define URXH0 *((volatile unsigned int *)0xE2900024)#define UBRDIV0 *((volatile unsigned int *)0xE2900028)#define UDIVSLOT0*((volatile unsigned int *)0xE290002C)// 初始化UART0void uart0_init(){// 配置GPA0_0为UART_0_RXD,GPA0_1为UART_0_TXDGPA0CON &= ~0xFF;//清零GPA0CON |= 0x22;// bit[3:0]=0b0010,bit[7:4]=0b0010/* 配置串口的数据传输格式* 8位数据位 bit[1:0]=0x3* 1位停止位 bit[2]=0* 无校验 bit[5:3]=0xx* 正常模式 bit[6]=0*/ULCON0 = (3<<0)|(0<<2)|(0<<3)|(0<<6);/* 配置UCON0* 接收模式为中断或轮询 bit[1:0]=1* 发送模式为中断或轮询 bit[3:2]=1* 允许产生异常中断bit[6]=1* 时钟选择外部时钟bit[10]=0*/UCON0 = (1<<0) | (1<<2) | (1<<6) | (0<<10); // 不使用FIFOUFCON0 = 0;/* 波特率设置为115200* 已知PCLK=66MHz* DIV_VAL = (66000000/(115200 x 16))-1 = 35.8 - 1 = 34.8* UBRDIV0 = 34(DIV_VAL的整数部分)* (num of 1's in UDIVSLOTn)/16 = 0.8* (num of 1's in UDIVSLOTn) = 13* UDIVSLOT0 = 0xDFDD (参考手册查表)*/UBRDIV0 = 34;UDIVSLOT0 = 0XDDDD;return;}
(2)发送数据
本实验禁用FIFO,因此,在发送字符前,要判断上一个字符是否已经被发送出去。
当UTRSTAT0寄存器的bit[2]为1时,表示已经发送完毕,这样就可以向UTXH0寄存器中写入要发送的字符了。
void putc(unsigned char c){// 查询状态寄存器,等待发送缓存为空while (! (UTRSTAT0 & (1<<2))); //课本97页,表7-3UTXH0 = c; // 写入发送寄存器return;}
(3)接收数据
读数据之前要先查询UTRSTAT0寄存器的bit[0],当它为1时表示接收缓存中有数据,也就可以读取URXH0中的数据了。
unsigned char getc(void){// 查询状态寄存器,等待接收缓存有数据while (!(UTRSTAT0 & (1<<0)));return (URXH0);}
(4)发送字符串数据
这是一个扩展功能,通过一个while循环将字符串拆分成一个一个的字符发送出去。
void puts(char *str){char *p = str;while (*p)putc(*p++);}
3.主程序
#define GPC0CON*((volatile unsigned int *)0xE0200060)#define GPC0DAT*((volatile unsigned int *)0xE0200064)#defineGPC0_3_out(1<<(3*4))#defineGPC0_4_out(1<<(4*4))#defineGPC0_3_MASK(0xF<<(3*4))#defineGPC0_4_MASK(0xF<<(4*4))extern void uart0_init(void);int main(void){char c;uart0_init(); // 初始化uart0GPC0CON &= ~(GPC0_3_MASK | GPC0_4_MASK); // 清bit[15:12]和bit[19:16]GPC0CON |= (GPC0_3_out | GPC0_4_out);// 配置GPC0_3和GPC0_4为输出引脚GPC0DAT &= ~(0x3<<3); // 向 bit[4:3]写入0熄灭LED1、LED2puts("=============================\r\n");puts("S5PV210 UART Test:\r\n");puts("1.LED1 on\r\n");puts("2.LED1 off\r\n");puts("3.LED2 on\r\n");puts("4.LED2 off\r\n");puts("=============================\r\n");while (1){c = getc();// 从串口终端获取一个字符putc(c);// 回显if (c == '1')GPC0DAT |= 1 << 3;// LED1亮else if (c == '2')GPC0DAT &= ~(1 << 3);// LED1灭else if (c == '3')GPC0DAT |= 1 << 4;// LED2亮else if (c == '4')GPC0DAT &= ~(1 << 4); // LED2灭}return 0;}