通用异步收发器UART

《嵌入式Linux应用完全开发手册》第2篇第11章总结归纳

本章目标

  1. 了解UART的原理
  2. 掌握S3C2410/S3C2440中UART的使用

UART原理以及UART部件使用方法

UART原理说明

通用异步收发器简称UART(Universal Asynchronous Reciver Transmitter),它用来传输串行数据。发送数据时,CPU将并行数据写入UART,UART按照一定的格式在一根电线上发出;接收数据时,UART检测另一根电线上的信号,将串行收集放在缓冲区中,CPU即可读取UART获得这些数据。UART之间以全双工方式传输数据,最精简的连线方法只有三根电线,TxD用于发送数据,RxD用于接收数据,GnD用于给双方提供参考电平。
img not found
UART使用标准的TTL/CMOS逻辑电平(0-5V、0-3.3V、0-2.5V、0-1.8V)来表示数据,高电平表示1,低电平表示0。为了增强数据的抗干扰能力、提高传输长度,通常将TTL/CMOS逻辑电平转换为RS-232逻辑电平,312V表示0,-3-12V表示1。
TxD、RxD数据线以“位”为最小单位传输数据。帧(Frame)由具有完整意义、不可分割的若干位组成,它包含开始位、数据位、校验位(需要的话)和停止位。发送数据之前,UART之间要约定好数据的传输速率(即每位所占据的时间,其倒数称为波特率)、数据的传输格式(即有多少个数据位、是否使用校验位、奇校验还是偶校验、有多少个停止位)。
数据传输流程如下:

  1. 平时数据线处于空闭状态(1状态)。
  2. 当要发送数据时,UART改变TxD数据线的状态(变为0状态),并维持1位的时间,这样接收方检测到开始位之后,再等待1.5位的时间就开始一位一位检测数据线的状态得到所传输的数据。
  3. UART一帧中可以有5、6、7或8位得数据,发送方一位一位地改变数据线的状态将它们发送出去,首先发送最低位。
  4. 如果使用校验功能,UART在发送完数据位之后,还要发送一个校验位。有两种校验方法:奇校验、偶校验—数据位连同校验位中“1”的数目等于奇数还是偶数。
  5. 最后,发送停止位,数据线恢复到空闭状态(1状态)。停止位的长度有3种:1位、1.5位、2位。

下图演示了UART使用7个数据位、偶校验、2个停止位的格式传输字符’A‘(二进制格式为0b1000001)时,TTL/CMOS逻辑电平、RS232逻辑电平对应的波形。
img not found

S3C2410/S3C2440 UART的特性

S3C2410/S3C2440 中UART的特性相似,有3个独立的通道,每个通道都可以工作于中断模式或DMA模式,即UART可以发出中断或DMA请求以便在CPU、UART之间传输数据。S3C2410/S3C2440 UART由波特率发生器、发送器、接收器和控制逻辑组成。
使用系统时钟时,S3C2410的UART波特率可以达到230.4Kbit/s,S3C2440则可以达到115.2Kbit/s;如果使用UEXTCLK引脚提供的外部时钟,则可以达到更高的波特率。波特率可以通过编程控制。
S3C2410 UART的每个控制通道都有16字节的发送FIFO和16字节的接收FIFO,S3C2440 UART的FIFO深度为64。发送数据时,CPU先将数据写入发送FIFO中,然后UART控制器会自动将FIFO中的数据复制到“发送移位器(Transmit Shifter)”中,发送移位器将数据一位一位的发送到TxD数据线上。接收数据时,“接收移位器(Receive Shifter)”,将RxD数据线上的数据一位一位的接收进来,然后复制到接收FIFO中,CPU即可从中读取数据。
S3C2410/S3C2440 UART的每个通道支持的停止位有1位、2位,数据位有5、6、7或8位,支持校验功能,另外还有红外发送/接收功能。
S3C2410/S3C2440 UART结构如下图所示:
img not found

S3C2410/S3C2440 UART的使用

在使用UART之前需要设置波特率、传输格式;对于S3C2410/S3C2440,还要选择所涉及管脚为UART功能,选择UART通道的工作模式为中断模式还是DMA模式。设置好之后,往某个寄存器写入数据即可发送。可以通过查询状态寄存器或设置中断来获知数据是否已经发送完毕、是否已经接收到数据。

将所涉及的UART通道管脚设为UART功能

比如UART通道0中,GPH2、GPH3分别用作TXD0、RXD0,要使用UART通道0时,先设置GPHCON寄存器将GPH2、GPH3引脚的功能设为TXD0、RXD0。

UBRDIVn寄存器(UART BAUD RATE DIVISOR):设置波特率

S3C2410 UART的时钟源有两种选择:PCLK、UEXTCLK;S3C2440的时钟源有三种选择:PCLK、UEXTCLK、FCLK/n,其中n通过UCON0UCON2联合设置。
根据给定的波特率、所选择的时钟源的频率,可以通过以下公式计算UBRDIVn寄存器(n为0
2,对应3个UART通道)。

1
UBRDIVn = (int)(UART clock/(baud rate x 16)) - 1

上述公式计算出来的UBRDIVn寄存器值不一定是整数,只要误差在1.87%之内即可。误差计算公式如下:

1
2
3
tUPCLK = (UBRDIVn + 1) x 16 x 1Frame / (UART clock)     //tUPCLK 实际的UART时钟
tUEXACT = 1Frame / baud rate //tUEXACT 理论的UART时钟
UART error = (tUPCLK - tUEXACT) / tUEXACT x 100% //误差

ULCONn寄存器(UART LINE CONTROL):设置传输格式

ULCONn寄存器(n为0~2)格式如下表所示:

功能 说明
数据位宽度 [1:0] 0b00:5位
0b01:6位
0b10:7位
0b11:8位
停止位宽度 [2] 0:一帧中有一个停止位
1:一帧中有两个停止位
校验模式 [5:3] 设置校验位的产生方法、检验方法:
0b0xx:无校验
0b100:奇校验
0b101:偶校验
0b110:发送数据时强制设为1,接收数据时检查是否为1
0b111:发送数据时强制设为0,接收数据时检查是否为0
红外模式 [6] 0:正常模式
1:红外模式

UART通道被设为红外模式时,其串行数据的波形与正常模式稍有不同。

ULCONn寄存器(UART CONTROL)

ULCONn寄存器用于选择UART时钟源、设置UART中断方式等。S3C2410 UART的时钟源有两种选择:PCLK、UEXTCLK;S3C2440的时钟源有三种选择:PCLK、UEXTCLK、FCLK/n。所以在时钟源的选择与设置方面稍有不同。
S3C2410的ULCONn寄存器格式如下表所示:

功能 说明
接收模式 [1:0] 选择如何从UART接收缓冲区中读取数据。
0b00:禁止接收数据
0b01:中断方式或者查询方式
0b10:DMA0请求(UART0) DMA3请求(UART2)
0b11:DMA1请求(UART1)
发送模式 [3:2] 选择如何将数据发送到UART发送缓存区。
0b00:禁止发送数据
0b01:中断方式或者查询方式
0b10:DMA0请求(UART0) DMA3请求(UART2)
0b11:DMA1请求(UART1)
自环模式 [5] 自环模式就是将TxDn和RxDn在内部相连,用于自发自收。
0:正常模式 1:自环模式
接收错误状态中断使能 [6] 用于使能当发生错误时(帧错误、溢出)时,产生中断。
0:出错时不产生中断
1:出错时产生中断
接收超时使能 [7] 当使用UART FIFO时,用于使能/禁止接收超时的中断。
0=禁止 1=使能
接收中断方式 [8] 如下情况发生时,将产生接收中断。
不使用FIFO时,接收到一个数据;
使用FIFO时,FIFO中的数据达到RxFIFO的触发阈值。
中断方式如下设置。
0:脉冲
1:电平
发送中断方式 [9] 如下情况发生时,将产生发送中断。
不使用FIFO时,发送缓冲区变空;
使用FIFO时,FIFO中的数据达到TxFIFO的触发阈值。
中断方式如下设置。
0:脉冲
1:电平
时钟选择 [10] 选择UART时钟源。
0:PCLK
UEXTCLK

S3C2440的UCONn寄存器在UART时钟的选择方面与S3C2410有所不同,从位[10]往上的位含义不一样,并且原来的位[4]用于选择是否发出“break”信号,这些位的含义如下表所示:

功能 说明
“break”信号 [4] 设置此位时,UART会在一帧的时间内发出一个“break”信号。
0:正常发送 1:发出“break”信号
时钟选择 [11:10] 选择UART时钟源。
0b00/0b10:PCLK
0b01:UEXTVLK
0b11:FCLK/n
FCLK分频率系数 [15:12] 用来设置“FCLK/n”中的n值

ULCON0、ULCON1、ULCON2这3个寄存器的位[15:12]一起用来确定n值,它们的意义如下。

  1. ULCON2[15]:“FCLK/n”使能位。
    它等于0时,禁止使用“FCLK/n”作为UART时钟源;等于1时,可以用作UART时钟源。
  2. n值的设置。
    ULCON0[15:12]、ULCON1[15:12]、ULCON2[14:12]三者用于设置n值,当其中一个被设置为非0值时,其他两个必须为0。
    1. n值处于7~12时,UART时钟=FCLK/(divider + 6),divider为ULCON0[15:12]的值,大于0。
    2. n值处于22~36时,UART时钟=FCLK/(divider + 21),divider为ULCON1[15:12]的值,大于0。
    3. n值处于37~43时,UART时钟=FCLK/(divider + 36),divider为ULCON2[14:12]的值,大于0。
    4. ULCON0[15:12]、ULCON1[15:12]、ULCON2[14:12]都等于0时,UART时钟:FCLK/44。

UFCONn寄存器(UART FIFO CONTROL)、UFSTATn寄存器(UART FIFO STATUS)

UFCONn寄存器用于设置是否使用FIFO,设置各FIFO的触发阈值,即发送FIFO中有多少个数据产生中断、接收FIFO中有多少个数据产生中断。并可以通过设置UFCONn寄存器来复位各个FIFO。
读取UFSTATn寄存器可以知道各个FIFO是否已经满、其中有多少个数据。
不适用FIFO时,可以认为FIFO的深度是1,使用FIFO时,S3C2410的FIFO深度是16,S3C2440的深度是64。

UMCONn寄存器(UART MODEM CONTROL)、UMSTATn寄存器(UART MODEM STATUS)

这两类寄存器用于流量控制。

UTRSTATn寄存器(UART TX/RX STATUS)

UTRSTATn寄存器用来表明数据是否已经发送完毕、是否已经接收到数据。格式如下表所示。缓冲区其实就是FIFO,只不过不适用FIFO时,FIFO的深度为1。

功能 说明
接收缓冲区数据就绪 [0] 当接收到数据时,此位被自动设为1
发送缓冲区空 [1] 当发送缓冲区没有数据时,此位被自动设为1
发送器空 [2] 当发送缓冲区中没有数据,并且最后一个数据也已经发送出去,此位被自动设为1

UERSTATn寄存器(UART ERROR STATUS)

用来表示各种错误是否发生,位[0]~位[3]为1时分别表示溢出错误、校验错误、帧错误、检测到“break”信号。读取这个寄存器时,它会自动清0。
需要注意的是,接收数据时如果使用FIFO,则UART内部会使用一个“错误FIFO”来表明接收FIFO中哪个数据在接收过程中发生了错误。CPU只有在读出这个错误的数据时,才会察觉到发生了错误。要想清除“错误FIFO”,则必须读出错误的数据,并读出UERSTATn寄存器。

UTXHn寄存器(UART TRANSMIT BUFFER REGISTER)

CPU将数据写入这个寄存器,UART即会将它保存到缓冲区,并自动发送出去。

URXHn寄存器(UART RECEIVER BUFFER REGISTER)

当UART接收到数据,CPU读取这个寄存器,即可获得数据。

UART操作实例

代码详解

本示例代码的目的是在串口上输出一串字符,单板接收到后将它的ASCII码加1后从串口输出。
首先设置MPLL提高系统时钟,令PCLK为50MHz,UART将选择PCLK为时钟源。将代码复制到SDRAM中之后,调用main函数。重点在于UART0的初始化、收发数据,这由3个函数来实现:uart0_init、getc和putc。

UART初始化

uart0_init函数代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#define PCLK                        50000000            //init.c中的clock_init函数设置PCLK为50MHz
#define UART_CLK PCLK //UART0的时钟源设置为PCLK
#define UART_BAUD_RATE 115200 //波特率
#define UART_BRD ((UART_CLK / (UART_BAUD_RATE * 16 )) - 1)

/*
初始化UART0
115200 8N1 无流控
*/
void uart0_init(void)
{
GPHCON |= 0xa0; //GPH2、GPH3用作TXD0、RXD0
GPHUP = 0x0c; //GPH2、GPH3内部上拉

ULCON0 = 0x03; //波特率为115200,数据格式为:8个数据位、没有流控、1个停止位
UCON0 = 0x05; //查询方式,UART时钟源位PLCK
UFCON0 = 0x00; //不使用FIFO
UMCON0 = 0x00; //不使用流控
UBRDIV0 = UART_BRD; //波特率位115200
}

发送字符的函数

本实例不使用FIFO,发送字符前,首先判断上一个字符是否已经被发送出去。如果没有,则不断查询UTRSTAT0寄存器的位[2],当它为1时表示已经发送完毕。于是,即可向UTXH0寄存器中写入当前要发送的字符。代码如下(宏TXD0READY被定义为(1 << 2)):

1
2
3
4
5
6
7
8
9
10
11
/*
发送一个字符
*/
void putc(unsigned char c)
{
//等待,直到发送缓区中的数据已经全部发送出去
while(!(UTRSTAT0 & TXD0READY));

//向UTXH0寄存器中写入数据,UART即自动将它发送出去
UTXH0 = c;
}

接收字符的函数

试图读取数据前,先查询UTRSTAT0寄存器的位[1],当它为1时表示接收缓冲区有数据,于是,即可读取URXH0得到数据。

1
2
3
4
5
6
7
8
9
10
11
12
/*
接收一个字符
*/

unsigned char getc(void)
{
//等待,直到接收缓存区有数据
while(!(UTRSTAT0 & RXD0READY));

//直接读取URXH0寄存器,即可获得接收到的数据
return URXH0;
}

主函数

在main函数中,初始化UART0之后,即不断读取串口数据,并判断它是否为数字或字母。如果是的话,就将它加1后从串口输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include "serial.h"

int main(void)
{
unsigned char c;

uart0_init(); //波特率115200,8N1(8个数据位,无校验位,1个停止位)

while(1)
{
//从串口接收到数据之后,判断其是否为数字或字母,若是则加1后输出
c = getc();
if(isDigital(c) || isLetter(c))
putc(c+1);
}

return 0;
}

测试方式

首先使用串口将开发板的COM0和PC的串口相连,打开PC上的串口工具(推荐使用SecureCRT),设置其波特率为115200、8N1(8个数据位,无校验位,1个停止位)。然后将编译生成的uart.bin文件烧入NAND Flash后上电运行。最后在PC上的串口工具中输入数字或者字母,可以看到输出另一个字符(加1);如果输入其他字符,则无输出。