串行通信协议 —— UART

摘要:
工作原理是逐位传输传输数据的每个字符。它可以在串行通信和并行通信之间转换要传输的数据。然而,由于USB的通信时序和信号电平与串行端口完全不同,因此它是USB接口的UART模块。

UART —— Universal Asynchronous Receiver/Transmitter —— 通用异步收发器

 一、UART简介

(强烈推荐一篇详细介绍UART的博客:https://www.cnblogs.com/mylinux/p/4078576.html

UART是异步串口通信协议,

工作原理是将传输数据的每个字符一位接一位地传输,它能将要传输的资料在串行通信与并行通信之间加以转换,能够灵活地与外部设备进行全双工数据交换。

USART是UART的升级版,其支持同步模式,用法与UART相同

二、概念辨析

------------------------------------UART  COM口   串口  USB口  RS - 232  TTL---------------------------------------------

 UART,在硬件上表现为串口收发的逻辑电路,可被集成为独立地模块化芯片

 COM口,串行通信端口,有时也称为串口,是一种连接器的结构,这里区别于USB的“通用串行总线”和硬盘的“SATA”,串口的接口标准规范和总线标准规范是RS-232

           常见的有两种物理标准,D型9针插头,和4针杜邦头,

串行通信协议 —— UART第1张

USB口:通用串行总线,和串口完全是两个概念。虽然也是串行方式通信,但由于USB的通信时序和信号电平都和串口完全不同,因此和串口没有任何关系。USB是高速的通信接口,用于PC连接各种外设,U盘、键鼠、移动硬盘、当然也包括“USB转串口”的模块。(USB转串口模块,就是USB接口的UART模块)

TTL,RS232,RS485等都是一种逻辑电平的表示方式,详见:A


三、UART原理说明

串行通信协议 —— UART第2张

   发送数据时,CPU将并行数据写入UART,UART按照一定格式在一根电线上串行发出;接收数据时,UART检测另一根电线上的信号,将串行收集放在缓冲区,CPU就可以读取UART获取这些数据。

  TxD —— 发送数据      RxD —— 接收数据   Gnd —— 用于给双方提供参考电平

  UART使用标准的TTL / CMOS逻辑电平来表示数据,高电平表示1,低电平为0,。为了增强数据的抗干扰能力,提高传输长度,通常将TTL / COMS逻辑电平转换为RS-232逻辑电平


  数据传输流程:以传输一字节数据‘A’为例

  传送时,数据的低位在前,高位在后

   传送开始前,发收双方把所采用的起止式格式(包括字符的数据位长度,停止位位数,有无校验位以及是奇校验还是偶校验等)和数据传输速率---波特率(每一位占据的时间)作统一规定。

   规定传输协议 

     1)平时数据线处于“空闭”状态(1状态)

   2)当要发送数据时,UART改变TxD数据线的状态,变为0状态,并维持一位的时间,这样接收方检测到开始位后,再等待1.5位的时间就开始一位一位地检测数据线的状态得到所传输的数据。

  3)UART一帧中可以有5/6/7/8位的数据,发送方一位一位的改变数据线的状态将数据发送出去,首先发送最低位

  4)如果使用校验功能,UART在发送完数据后,还要发送一个校验位:奇校验,偶校验——数据位连同校验位中,“1”的数目属于奇数或偶数。

  5)最后发送停止位,数据线恢复到“空闭”状态(1状态),停止位长度有三种:1位、1.5位、2位

串行通信协议 —— UART第3张

图1-1  TTL / COMS逻辑电平下,传输A时的波形

  

串行通信协议 —— UART第4张

 图1-2  RS - 232逻辑电平下,传输数据A的波形

  对于TTL / CMOS 电平,在xV至5V之间,就认为是逻辑1,在0V至yV之间就为逻辑0。

  对于RS - 232电平,在-12V至-3V之间,就认为是逻辑1,在+3V至+12V之间就为逻辑0。

  RS-232的电平比TTL/CMOS高,能传输更远的距离,在工业上用得比较多。

在ARM芯片上的串口都是TTL电平的,通过板子或外接电平转换芯片,可以转成RS232标准的接口。如图2-1所示

串行通信协议 —— UART第5张

 如今终端上的RS-232标准的接口越来越少,越多的是USB口,因也可以使用USB转串口芯片将TTL电平转为USB传输电平。


四、异步串行通信的特点

所谓异步通信,是指数据传送以字节为单位,字符与字符间的传送是完全异步的,位与位的传送基本是同步的。

特点:

  1)以字符为单位传送信息

  2)相邻两字符间的间隔任意长

  3)由于一个字符的波特位长度有限,所以需要接受时钟和发送时钟相近就可以

  4)字符间异步,字符内同步


五、基本结构

串行通信协议 —— UART第6张

  图3-1   UART结构图

   ARM处理器中UART功能相似,都有独立的通道,每个通道都可工作于中断或DMA模式,即UART可以发出中断或DMA请求以便在UART、CPU间传输数据。具体的UART特性可参见不同芯片的芯片手册。以下以S3C2440芯片分析UART的使用过程。

  S3C2440  UART的FIFO深度为64字节。发送数据时,CPU先将数据写入发送FIFO中,然后UART自动将FIFO的数据复制到“发送移位器”中,发送移位器将数据一位一位地发送到TxDn数据线上(按照设定的格式,插入开始位,校验位和停止位)。接收数据时,“接收移位器”将RxDn数据线上的数据一位一位接收进来,然后复制到FIFO中,CPU即可从中读取数据。UART的结构如图3-1所示。


六、使用UART

  在UART通道0上实现最简单的字符发送与接收功能

 1、看原理图,找到对应引脚

串行通信协议 —— UART第7张

 使用TxD0与RxD0,对应GPH2, GPH3

 串行通信协议 —— UART第8张

 串行通信协议 —— UART第9张

2、设置波特率

每个UART的波特率发生器要为发送器与接收器提供串行时钟,对于S3C2440,其波特率发送器的时钟源可以选择

PCLK(外设时钟),FCLK/n(帧时钟),UEXTCLK,设置波特率可以通过UART波特率分频寄存器所得,有以下公式:

  UBRDIVn = (int)( UART 时钟源 / ( 波特率 x 16) ) –1

  PCLK = 50MHZ = 50000000,使用波特率115200,得出UBRDIVn=26

  串行通信协议 —— UART第10张

   

接下来看如何设置相关寄存器:

  1) UCON0 ——  UART 控制寄存器

  选择PCLK外设时钟作为时钟源,

       串行通信协议 —— UART第11张

   仅需设置[11:10] - 时钟选择,设置成0,对于[15:12]为时钟源为FCLK情况下的设置,故不用设置。

   [9:4]位,为uart中断相关,暂时不用设置。

   串行通信协议 —— UART第12张

   接收模式和接收模式都设置为中断或查询模式。[3:0] = 0101 

  由上:UCON0 = 0x00000005;   UBRDIVn=26;

3、设置其他的特殊寄存器

  1)ULCON -- UART线路控制寄存器

  串行通信协议 —— UART第13张

   不使用红外模式;奇偶校验位,暂时不设置;停止位设置为0,使用一个停止位;传送的数据位数选择8位;

  ULCON0 = 0x00000003;

  2)UFCON0 --- FIFO控制寄存器

    FIFO在UART传输大量数据的时候有很大作用,暂时先不设置,保持默认值

  3)UMCON0 --- 流量控制寄存器

    默认值

  4)UTRSTAT0 -- Tx / Rx状态寄存器

    串行通信协议 —— UART第14张

     发送器空[2]=1,说明发送缓冲空间为空,且移位寄存器里的数据已经发送出去,因此可以通过判断这

     一位,当其为1时,就可往里面写数据。

    接收缓冲器,[0]=1, 有数据,可以读取

  5)UFRSTAT0 -- 错误状态寄存器

    假设一切正常,默认值

  6)UFSTAT0 -- FIFO状态寄存器

    暂时没用到FIFO,不用设置(对于2440,使用FIFO时,缓冲区可以存放64字节的数据,不使用FIFO

    只能存放1字节的数据)

  7)UMSTAT0  -- MODEM状态寄存器

    不用设置

  8)UTXH0 与 URXH0 ---发送/接收缓冲寄存器

    发送数据,将数据写入UTXH0即可,接收数据,从URXH0读即可

七、示例

串行通信协议 —— UART第15张串行通信协议 —— UART第16张
 1 #include "uart.h"
 2 
 3 int main(void)
 4 {
 5     unsigned char c;
 6     
 7     uart0_init();
 8     puts("Hello, world!

");
 9     
10     while(1) 
11     {
12         c = getchar();
13         if (c == '
')
14         {
15             putchar('
');
16         }
17 
18         if (c == '
')
19         {
20             putchar('
');
21         }
22 
23         putchar(c);
24     }
25     return 0;
26 }
main.c
串行通信协议 —— UART第17张串行通信协议 —— UART第18张
 1 #define     __REG(x)                    (*(volatile unsigned int *)(x)) 
 2 #define     GPHCON                   __REG(0x56000070)  //Port H control
 3 #define     GPHUP                    __REG(0x56000078)  //Pull-up control H  
 4 #define     UCON0                    __REG(0x50000004)  //UART 0 control 
 5 #define     UBRDIV0                  __REG(0x50000028)  //UART 0 baud rate divisor 
 6 #define     ULCON0                   __REG(0x50000000)  //UART 0 line control
 7 #define     UTRSTAT0                 __REG(0x50000010)  //UART 0 Tx/Rx status      
 8 #define     UTXH0                    __REG_BYTE(0x50000020)  //UART 0 transmission hold 
 9 #define     URXH0                    __REG_BYTE(0x50000024)  //UART 0 receive
10 
11 
12 void uart0_init()
13 {
14     /* 设置引脚用于串口  */
15     /* GPH2->TxD0->[5:4]=10,GPH3->RxD0->[7:6]=10 ,即[7:4]=1010*/
16     GPHCON &= ~((3<<4) | (3<<6)); //先将第5位到第8位清零,~(110000 | 11000000) = ~1111000 = 00001111
17     GPHCON |=  ((2<<4) | (2<<6)); //(100000 | 10000000) = 10100000,由于[7:4]已经清零了。所以直接或上就可以
18 
19     GPHUP  &= ~((1<<2) | (1<<3)) ; //将2,3引脚使能内部上拉,将[2:3]=00
20     
21     /* 设置波特率  */
22     /**UBRDIVn = (int)( UART clock / ( buad rate x 16) ) –1*/
23     /*UART CLOCK设置为PCLK (50MHZ)= 50000000, UBRDIV0 = 26*/
24     UCON0 = 0x00000005;  /*PCLK,查询/中断*/
25     UBRDIV0 = 26;
26 
27     /* 设置数据格式  */
28     ULCON0 = 0x00000003;  /* 8n1:8个数据位,没有校验位,1个停止位 */
29 
30     /*  */
31 }
32 
33 int putchar(int c)
34 {
35     /* UTRSTAT0 -- 发送接收状态寄存器*/
36     /* UTXH0 发送缓冲寄存器 */
37     while(!(UTRSTAT0 & (1<<2))) ; //UTRSTAT0的[2]不为1,说明有数据,循环等待
38     UTXH0 = (unsigned char)c;  //没有数据,写入数据
39     
40 }
41 
42 int getchar(void)
43 {
44     /* URXH0  接收缓冲寄存器 */
45     while(!(UTRSTAT0 & (1<<0))) ; //UTRSTAT0的[1]不为1,说明没有数据在接收缓冲区,循环等待
46     return URXH0;  //有数据,读出数据
47 
48 }
49 
50 int puts(const char *s)
51 {
52     while(*s)
53     {
54         putchar(*s);
55         s++;
56     }
57 }
uart.c
串行通信协议 —— UART第19张串行通信协议 —— UART第20张
1 #ifndef _UART_H
2 #define _UART_H
3 
4 void uart0_init();
5 int putchar(int c);
6 int getchar(void);
7 int puts(const char *s);
8 
9 #endif 
uart.h
串行通信协议 —— UART第21张串行通信协议 —— UART第22张
1 all:
2     arm-linux-gcc -c -o uart.o uart.c
3     arm-linux-gcc -c -o main.o main.c
4     arm-linux-gcc -c -o start.o start.S
5     arm-linux-ld -Ttext 0 start.o  uart.o main.o -o uart.elf
6     arm-linux-objcopy -O binary -S uart.elf uart.bin
7     arm-linux-objdump -D uart.elf > uart.dis
8 clean:
9     rm *.bin *.o *.elf *.dis
Makefile
串行通信协议 —— UART第23张串行通信协议 —— UART第24张
 1 .text
 2 .global _start
 3  
 4 _start:
 5 
 6     /* 关闭看门狗 */
 7     ldr r0, =0x53000000
 8     ldr r1, =0
 9     str r1, [r0]
10 
11     /* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */
12     /* LOCKTIME(0x4C000000) = 0xFFFFFFFF */
13     ldr r0, =0x4C000000
14     ldr r1, =0xFFFFFFFF
15     str r1, [r0]
16 
17     /* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8  */
18     ldr r0, =0x4C000014
19     ldr r1, =0x5
20     str r1, [r0]
21 
22     /* 设置CPU工作于异步模式 */
23     mrc p15,0,r0,c1,c0,0
24     orr r0,r0,#0xc0000000   //R1_nF:OR:R1_iA
25     mcr p15,0,r0,c1,c0,0
26 
27     /* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0) 
28      *  m = MDIV+8 = 92+8=100
29      *  p = PDIV+2 = 1+2 = 3
30      *  s = SDIV = 1
31      *  FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M
32      */
33     ldr r0, =0x4C000004
34     ldr r1, =(92<<12)|(1<<4)|(1<<0)
35     str r1, [r0]
36 
37     /* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定
38      * 然后CPU工作于新的频率FCLK
39      */
40     
41     
42 
43     /* 设置内存: sp 栈 */
44     /* 分辨是nor/nand启动
45      * 写0到0地址, 再读出来
46      * 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动
47      * 否则就是nor启动
48      */
49     mov r1, #0
50     ldr r0, [r1] /* 读出原来的值备份 */
51     str r1, [r1] /* 0->[0] */ 
52     ldr r2, [r1] /* r2=[0] */
53     cmp r1, r2   /* r1==r2? 如果相等表示是NAND启动 */
54     ldr sp, =0x40000000+4096 /* 先假设是nor启动 */
55     moveq sp, #4096  /* nand启动 */
56     streq r0, [r1]   /* 恢复原来的值 */
57     
58 
59     bl main
60 
61 halt:
62     b halt
63     
start.s

结果:

串行通信协议 —— UART第25张

 小结:

简要描述串行通信协议

通信协议,

  即通信双方约定的一种协议,约定内容包括:数据格式,同步方式,传输速度,传输步骤,检纠错方式等。
串口通信协议:
  通常分为同步协议与异步,此处描述的是异步串口通信协议,UART。
  UART是异步,全双工的通信协议,以字符为单位,按照顺序逐位进行传送。有两根线,RXD接收数据、TXD发送数据;
传送每个字符都是以起始位开始,以停止位结束,传送时,数据的低位在前,高位在后;
在传输数据前,收发双方会对传输协议做统一规定:数据位长度—8位、停止位位数—1位、奇校验、传输速率115200bit/s
现以发送数据为例:
  TxD在空闲时和停止位时都是高电平,当想要发送数据,先发送一个起始位,即改变TxD为低电平维持一位的时间,借着就
传输8位数据位与1位奇校验位与一个高电平的停止位,接受方检测到下降沿并确认为起始位后,就开始接收数据位,
校验位与停止位,且将停止位去掉,将数据位拼接为并行字节,经校验无错,就算接收一个字符完毕。发送端继续发送数据,接收端继续接收,直至数据传输完毕。

参考文档:

嵌入式LINUX应用开发 —— 韦东山

基于STM32之UART串口通信协议(一)详解串口、COM口、UART口, TTL、RS-232、RS-485区别详解

免责声明:文章转载自《串行通信协议 —— UART》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇python发送各类邮件的主要方法各主流浏览器内核介绍下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Sql Server使用技巧

1. 当用户在在SQL Server 2008企业管理器中更改表结构时,必须要先删除原来的表,然后重新创建新表,才能完成表的更改,如果强行更改会出现以下提示: 不允许保存更改。您所做的更改要求删除并重新创建以下表。您对无法重新创建的标进行了更改或者启用了“阻止保存要求重新创建表的更改”选项。如果要去掉此提示,打开SQL 2008 在最上面 工具-〉选项-〉...

kubernetes in action

Volume解决Kubernetes的存储的问题 对于Pod使用的存储,抽象为volume,volume伴随着Pod的创建而创建,消失而同时消失,不能单独的创建 这样的好处,是存储的塑胶不会因为某个container重启而丢失,因为volume是pod级别的 还有好处是,volume是pod资源,所以所有Pod中的container都可以共享访问这个vol...

CPF 入门教程

CPF C#跨平台桌面UI框架 系列教程 CPF 入门教程(一) CPF 入门教程 - 数据绑定和命令绑定(二) CPF 入门教程 - 样式和动画(三) CPF 入门教程 - 绘图(四)  CPF 入门教程 - 设计器和模板库的使用(五) CPF 入门教程 - 控件布局(六)  CPF 入门教程 - 属性和事件(七) CPF 入门教程 - 各个控件介绍(八...

Mysql字符集设置

最近,在项目组使用的mysql数据库中,插入数据出现乱码,关于这个问题做了下总结,我们从最基本的地方说起,到错误产生的深层次原因和解决办法。 基本概念 • 字符(Character)是指人类语言中最小的表义符号。例如’A'、’B'等;• 给定一系列字符,对每个字符赋予一个数值,用数值来代表对应的字符,这一数值就是字符的编码(Encoding)。例如,我们给...

[转]ubuntu下解压zip文件

1.功能作用:解压缩zip文件2.位置:/usr/bin/unzip3.格式用法:unzip [-Z] [-opts[modifiers]] file[.zip] [list] [-x xlist] [-d exdir]4.主要参数    -c 将解压缩的结果显示到屏幕上,并对字符做适当的转换    -p 与-c参数类似,会将解压缩的结果显示到屏幕上,但不...

VC++-数据加密

版本: VS2012 实例说明: 在一些应用程序或网络程序中,经常会存有一些非常机密的文件或数据,为了防止其他非法用户查阅或盗取这些机密数据,可对其进行加密。运行程序,在“密钥”编辑框中输入密钥,在“待加密的字符串”编辑框中输入要加密的字符串,单击“加密”按钮,密文将显示在“加密后的字符串”编辑框中,如图所示。 界面: 技术要点: 通过使用GetAt和S...