UART定义
UART 的全称是通用异步收发器(Universal Asynchronous Receiver/Transmitter),是实现设备之间低速数据通信的标准协议。
是一种通用的串行、异步通信总线。
该总线有两条数据线,可以实现全双工的发送和接收在嵌入式系统中常用于主机与辅助设备之间的通信。
“异步”:
指不需要额外的时钟线进行数据的同步传输, 是一种串行总线接口,只需占用两根线就可以完成数据的收发(一根接收数据,一根发送数据),常用的标准通信波特率有 9600bps、115200bps 等。
UART 模块工作类型
注意:master和slave只是一种模式,不是固定发送模块或者接收使用。
并行和串行
按数据传送的方式,通信可分为
- 串行通信
- 并行通信
串行通信
是指设备之间通过少量数据信号线(一般是 8 根以下),地线以及控制信号线,按数据位形式一位一位地传输数据的通讯方式。
并行通信
一般是指使用 8、 16、 32 及 64 根或更多的数据线进行传输的通讯方式,
同步通信和异步通信
根据通信的数据同步方式,又分为同步和异步两种。可以根据通信过程中是否有使用到时钟信号进行简单的区分。
异步通信:
在异步通信中,不使用时钟信号进行数据同步,它们直接在数据信号中穿插一些同步用的信号位,或者把主体数据进行打包,以数据帧的格式传输数据。例如规定由起始位、数据位、奇偶校验位、停止位等。
某些通信中还需要双方约定数据的传输速率,以便更好地同步 。
波特率(bps)是衡量数据传送速率的指标。
同步通信:
在同步通信中,收发设备双方会使用一根信号线表示时钟信号,在时钟信号的驱动下双方进行协调,同步数据。
通信中通常双方会统一规定在时钟信号的上升沿或下降沿对数据线进行采样。
全双工、半双工及单工通信
- 全双工:在同一时刻,两个设备之间可以同时收发数据
- 半双工:两个设备之间可以收发数据,但不能在同一时刻进行
- 单工:在任何时刻都只能进行一个方向的通信,即一个固定为发送设备,另一个固定为接收设备
UART协议
串口通信:
串口按位(bit)发送和接收字节。尽管比特字节(byte)的串行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。
串口通信协议:
指规定了数据包的内容,内容包含了起始位、主体数据、校验位及停止位,双方需要约定一致的数据包格式才能正常收发数据的有关规范。
UART 帧格式
串口通讯的数据包由发送设备通过自身的发送数据端口( TXD )传输到接收设备的接收数据端口(RXD)。
在串口通讯的协议层中,规定了数据包的内容,它由起始位、主体数据、校验位以及停止位组成,通讯双方的数据包格式要约定一致才能正常收发数据 。
下面虚线分开的每一格就是代表一个码元。
串口通讯的一个数据包从起始信号开始,直到停止信号结束。
- 起始位(start bit):开始发起传输的标志。(由一个逻辑 0 的数据位表示。)
- 主体数据:在数据包的起始位之后紧接着的就是要传输的主体数据内容,也称为有效数据,有效数据的长度常被约定为 5、 6、 7 或 8 位长。(UART最多一次传输8位而不是更多位的原因在于收发双方并不是时钟同源的,并不像SPI系统有一个主时钟供主、从机共享。UART的设计标准就是允许收发两端的频率偏差在10%以内,因此当接收8位数据后,收发双方的误差刚好控制在1位内,对数据的采样不会出错,可正确通信。)
- 停止位(stop bit):结束传输的标志。(停止信号可由 0.5、 1、 1.5 或 2 个逻辑 1 的数据位表示,只要双方约定一致即可。)
- 校验位(check bit):为了保证传输可靠性增加的校验信息。(校验方法有奇校验(odd)、偶校验(even)、 0 校验(space)、 1 校验(mark)以及无校验(noparity)。)
UART 功能
UART设备在不发送数据时,数据信号线上总是呈现高电平状态,称为空闲状态(又称MARK状态)。
当有数据发送时,信号线变成低电平,并持续1位的时间,用于表示发送字符的开始,该位称为起始位,也称SPACE状态。
起始位之后,在信号线上依次出现待发送的每一位字符数据,并且按照先低位后高位的顺序逐位发送。待发送的位数可以选择5位、6位、7位或8位。
数据位的后面可以加上一位奇偶校验位,也可以选择不加。
最后传送的是停止位。一般选择1位、1.5位或2位。
例子:传输0x55信号
0x55的二进制数为01010101,
UART传输采用LSB模式( bit 0最先传输)
因此传输线信号的切换过程为:1-0 -1-0 – 1-0 -1-0。
UART协议相关概念
“波特率”(Baudrate)
- 比特率(Bitrate):每秒钟传输的二进制位数,单位为比特每秒(bit/s)。
- 波特率(Baudrate):表示每秒钟传输了多少个码元。
码元是通信信号调制的概念,通信中常用时间间隔相同的符号来表示一个二进制数字,这样的信号称为码元。
串口异步通讯中由于没有时钟信号,所以两个通讯设备之间需要约定好波特率,即每个码元的长度,以便对信号进行解码 。
常见的波特率为4800、 9600、 115200 等。
每bit时间宽度:bit_width=1/ Baudrate
每bit时钟周期数: bit_num_clk=bit_width/T (T是时钟周期)
波特率和比特率之间的关系
波特率的大小与比特率一致
常见的通讯传输中,用 0V 表示数字 0, 5V 表示数字 1,
那么一个码元可以表示两种状态 0 和 1,
所以一个码元等于一个二进制比特位,
此时波特率的大小与比特率一致。
因为很多常见的通讯中一个码元都是表示两种状态,人们常常直接以波特率来表示比特率 。
波特率的大小与比特率不一致
如果在通讯传输中,有 0V、2V、 4V 、 6V 分别表示二进制数 00、 01、 10、 11,
那么每个码元可以表示四种状态,即两个二进制比特位,
所以码元数是二进制比特位数的一半,
这个时候的波特率为比特率的一半。
奇校验、偶校验:
对数据传输正确性的一种校验方法。
- 奇校验:要求有效数据和校验位中“1”的个数为奇数
- 偶校验:要求帧数据和校验位中“1”的个数为偶数
有效数据中,“1”的个数计算
缩减运算符
缩减运算是对单个操作数进行的运算,结果是1位的二进制数。
第一步先将操作数的第1位和第2位进行与、或、非运算,第二步将运算结果与第3位进行与、或、非运算,以此类推直至最后一位。
举例:
reg[3:0]B;
reg C;C=^B;//相当于 C=((B[0]^B[1])^B[2])^B[3];
有效数据中,“1”的个数计算
//输入的有效数据
data[7:0];//计算有效数据1的个数
^data[7:0]=1'b1 //1的个数为奇数;
^data[7:0]=1'b0 //1的个数为偶数
奇校验、偶校验方式:
发送方发送数据后计算奇偶性
接收方收到数据后再一次计算奇偶性
接收方计算出的奇偶校验位与发送方一致,表示在此次传输过程中未发生错误。
例子:
注意:
- 奇偶校验只能判别一位数据是否丢失,两位是无法判断的
- 发送方和接收方的奇偶校验要一致,两个都使用奇校验或者都使用偶校验
UART的设计
UART通信协议,通过协议规定收发双方的行为;通过约定动作获得收发同步;通过冗余实现容错传输。
具体实现思路如下:
- 通过初始状态位的变换(下降沿)通知接收方进入通信状态,收发双方能够有一个基本同步过程。
- 通过预留1位的起始位,使接收方有充分的时间启动状态机,并能够准确判断上一次启动是否为误触发。
- 后面通过事前约定,双方按照一定速率传输5~8位数据。
- 当传输完毕后,发送方将初始状态复位,从而通知接收方发送完毕,接收方也可以据此判断当前接收是否为误触发。
- 通过选择停止位,保证接收方的状态机能够顺利复位到初始状态。
UART的内部结构
波特率寄存器
波特率是单位时间内传送的二进制数据的位数,以位/秒( bps)表示,也称为比特率。
目前最常用的波特率是9600bps 和1152 00bps,其余速率可以通过收发双方约定实现。
假定当前时钟频率为f,而波特率为Baud,则相当于一个位,可以采样N=f/Baud个样点。
但由于收发双方是异步的,双方的位对齐位置会逐渐漂移,所以接收方需要找到最佳采样(判断)位置。而实际上最佳采样位置就是发送数据的中心位置,即在N/2处采样最佳。
UART的接收状态机就是通过判断当前样点序列是否等于N/2作为存储当前接收值的依据。
例子:8倍采样的情形
可以发现最佳采样位置就是第4位或第5个位处。
在IC设计中,可以通过设定N实现任意波特率的速率,而这个N寄存器就是波特率寄存器。
收发的通信基准时钟电路可以认为是一个N倍分频电路。
发送模块
UART的发送原理如下。
- 根据外部指令,将8位数据加载到UART的发送FIFO或发送寄存器上。
- 如果当前发送进入空闲状态,则字符数据被加载到发送控制模块。
- 发送控制模块将字符数据转换为串行数据流输出,同时加入起始位、奇偶校验位、停止位。
- 每N个时钟,将串行数据流的1位发送到UART的发送引脚(TXD)上。
状态规划:
接收模块
UART的接收原理如下。
- 在接收系统开始工作以后,启动接收进程,并直接进入空闲状态。在空闲状态时,始终检测起始位,即判断接收信号是否由1跳变为0。
- 当检测到有效起始位后,以约定波特率的时钟开始接收数据,并进入接收数据状态。
- 在数据接收状态下,当计数器统计接收位数达到规定的位数后,如果存在奇偶校验,则进入奇偶校验状态,否则直接进入停止位状态。
- 在奇偶校验状态下,如果校验位检测无误,则进入接收停止位状态。
- 停止位接收完毕后,将所接收数据转存到数据寄存器中,并返回到初始空闲状态。
在UART接收过程中,有几个基本的电路状态判断技巧:
起始位的判断:
在接收端开始接收数据位之前,通常需要搜索起始位。
接收端通常以N倍波特率的速率读取线路状态,检测线路上出现低电平的时刻。
因为异步传输的特点是以起始位为基准同步的,但通信线路噪声也极有可能使信号由1跳变到0,所以接收器以N倍的波特率对这种跳变进行检测,直至在连续N/2个接收时钟以后采样值仍然是低电平,才认为这是一个真正的起始位,而不是噪声引起的。
其中,若有一次采样得到的为高电平,则认为起始信号无效,也就是说,要返回初始状态重新等待起始信号的到来。
最佳采样点:
最可靠的接收应该是接收时钟的出现时刻正好对着数据位的中央,即前面提到的第N/2个采样位置。
合法UART接收状态的判断:
当采样计数器计数结束后,所有数据位都已经输入完成。最后需要对停止位的高电平进行检测,若正确检测到高电平,则说明本帧各位传输格式正确,可以将数据转存到数据寄存器中。否则说明本帧接收有误,定时关系出错。
接收器的功能的状态机(省略奇偶校验情况)
UART的design spec
- 接收上一级模块并行数据,将数据按照UART协议发送出去
- 采用奇校验
- 1bit停止位
- 波特率115200
- 工作时钟50MHz
计数模块
波特率 bandrate= 115200bit/s,周期 T=1/50MHz=20ns
传输1bit时间 T_1bit=[1/115200(s)] * ns
传输1bit需要的时钟周期数T_num=T_1bit / T =433 (二进制 1 1011 0010 位宽9)
一共需要传输的数据个数 N=1bit起始位+8bit有效数据位+ 1bit结束位=10(二进制 1010 位宽4)
两个计数器
- counter1:传输1bit数据需要的时钟周期数。设变量 uart_bit_cnt
- counter2:传输数据的个数。 设变量 uart_bit_num
counter1( uart_bit_cnt):
- 复位,清零 uart_bit_cnt =0
- uart使能,时钟触发一次,计数一次 uart_bit_cnt=uart_bit_cnt+1
- 计数到433,清零 uart_bit_cnt =0
always@(posedge clk_i or negedge rst_n_i)
beginif(!rst_n_i)uart_bit_cnt<=9'b0;else if(uart_en)beginif(uart_bit_cnt==9'd433)uart_bit_cnt<=9'd0;elseuart_bit_cnt<= uart_bit_cnt+1'b1;end
end
counter2(uart_bit_num):
- 复位,清零
- uart使能,uart_bit_cnt=433,计数一次 uart_bit_num=uart_bit_num+1
- uart不使能,清零
always@(posedge clk_i or negedge rst_n_i)
beginif(!rst_n_i)uart_bit_num<=4'b0;else if(uart_en)beginif(uart_bit_cnt==9'd433)uart_bit_num<= uart_bit_num+1'b1;endelseuart_bit_num<=4'b0;
end
发送模块(TX输出)
输入的并行数据存入FIFO/寄存器
//输入的数据data_in[7:0]
//存入的寄存器堆tx_buffer[7:0]
assign tx_buffer=data_in;
uart使能 uart_tx_en
- 端口输入数据使能,uart 使能有效uart_tx_en=1
- 数据传输结束,uart 使能无效 uart_tx_en=0
- 复位,uart 使能无效 uart_tx_en=0
always@(posedge clk_i or negedge rst_n_i)
beginif(!rst_n_i)uart_tx_en<=1'b0;else if (en_data_i)uart_tx_en<=1'b1;else if ((uart_bit_num==4'd10)&&(uart_bit==9'd433))uart_tx_en<=1'b0;
end
状态0:等待空闲ready 空闲标志为高电平 ready=1。如果空闲标志拉低,跳到状态1。
always@(posedge clk_i or negedge rst_n_i)
beginif(!rst_n_i)rdy <=1'b1;else if(((uart_bit_num==4'd10)&&(uart_bit==9'd433)))rdy <=1'b1;else if( uart_tx_en)rdy <=1'b0;
end
状态1: uart使能,时钟周期数uart_bit_cnt=433 uart_bit_num=0,传输数据0位,起始位。跳到状态2。
if(uart_bit_num==0)tx<=1'b0;
状态2: uart使能,时钟周期数为uart_bit_cnt=433,1<=uart_bit_num<=8时,传输有效数据。跳到状态3。
if (uart_bit_num>=4'd1 &&uart_bit_num<=4'd8&&)TX <= tx_buffer[uart_bit_num-1];
状态3: uart使能,时钟周期数uart_bit_cnt=433 uart_bit_num=8,传输数据9位,校验位。跳到状态4。
if(uart_bit_num==4'd9) TX <=^tx_buffer;
状态4: uart使能,时钟周期数uart_bit_cnt=433 uart_bit_num=9,传输数据10位,停止位。跳到状态0。
if(uart_bit_num==4'd10) TX <=1'b0;
接收模块(RX输入)
状态0:空闲识别
always@(posedge clk_i or negedge rst_n_i)
beginif(uart_bit_cn==433)uart_bit_cn<=0; else uart_bit_cn<=uart_bit_cn+1; if(uart_bit_cn==0)beginif(RX)uart_bit_num <= uart_bit_num+1;else//如果接收到不是1的数据,说明空闲结束beginuart_bit_num <=0;uart_bit_cnt<=0;state<=1;endendif( uart_bit_num==9) //连续接收10个1,判断是空闲,跳到状态1等待起始位beginuart_bit_num <=0;uart_bit_cnt<=0;state<=1;end
end
状态1:找起始位:找下降沿
将下降沿(RX)做一个时钟周期的延时(RX_delay)
将RX反向,与RX_delay相与,得到一个尖。
看到这个尖,就知道起始位到了。
~RX&RX_delay =1
RX_delay<=RX;//有时钟就在动,不需要条件
en_data_out<=0;
if(~RX&RX_delay)state<=2;
状态2: 收数据:(数据中间比较可靠)
uart_bit_cnt=216,1<uart_bit_num<8
//接收数据的buffer
//uart_rx_buf[7:0]
//输出数据data_out[7:0]always@(posedge clk_i or negedge rst_n_i)
beginif(!rst_n_i)uart_rx_buf<=8'd0;else if(uart_bit_cnt==9'd216 &&uart_bit_num>=4'd1 &&uart_bit_num<=4'd8)uart_rx_buf[uart_bit_num-1]<=RX;
end
assign data_out = uart_rx_buf;state<=3;
状态3:产生校验位(奇校验)
if(uart_bit_num==4'd9) RX <=^uart_rx_buf;state<=4;
状态4:检测到停止位为高电平
uart_bit_num=8,RX=1 检测到高电平.产生使能脉冲。
否则采样出错。跳到状态0。
if(uart_bit_cnt=433&& uart_bit_num=10) if(RX)en_data_out<=1;state<=0;elsestate<=0;
完整Verilog代码
//--band rate : 115200 每秒传输的bit位
//--check bit : odd
//--stop bit : 1bit
//--clock freq : 50MHz
//--clock num of uart bit:434clk_i 传输1bit需要的时钟周期 1/115200*10^9ns/20ns=434module uart
(input clk_i,input rst_n_i,//--TXinput [7:0] data_in,input en_data_i,output reg rdy,output TX, //--RXinput RX,output en_data_out,output [7:0] data_out,
//检查收发数据是否一致标志output flag
)
reg [8:0] uart_bit_cnt;
reg [3:0] uart_bit_num;reg uart_en;
wire [7:0] tx_buffer;
reg uart_tx_en;reg [2:0] tx_sate;
reg [2:0] rx_sate;
wire RX_delay;wire [7:0] uart_rx_buf;
//counter1:传输1bit数据需要的时钟周期数
always@(posedge clk_i or negedge rst_n_i)
beginif(!rst_n_i)uart_bit_cnt<=9'b0;else if(uart_en)beginif(uart_bit_cnt==9'd433)uart_bit_cnt<=9'd0;elseuart_bit_cnt<= uart_bit_cnt+1'b1;end
end
//counter2:传输数据的个数。
always@(posedge clk_i or negedge rst_n_i)
beginif(!rst_n_i)uart_bit_num<=4'b0;else if(uart_en)beginif(uart_bit_cnt==9'd433)uart_bit_num<= uart_bit_num+1'b1;endelseuart_bit_num<=4'b0;
end//-----连接发送模块和接收模块
always@(*)
begin tx_next1=TX;tx_next2= tx_next1;RX = tx_next2;
end//--------------TX发送数据-----------------------------------
//输入的并行数据存入FIFO/寄存器
assign tx_buffer = data_in;//uart使能 uart_tx_en
always@(posedge clk_i or negedge rst_n_i)
beginif(!rst_n_i)uart_tx_en<=1'b0;else if (en_data_i)uart_tx_en<=1'b1;else if ((uart_bit_num==4'd10)&&(uart_bit_cnt==9'd433))uart_tx_en<=1'b0;
endalways@(posedge clk_i or negedge rst_n_i)
beginif(!rst_n_i)beginrdy <=1'b1;tx_sate<=3'd0;uart_tx_en<=0;uart_bit_num<=0;uart_bit_cnt<=0;TX<=1'b1;endelse begincase(tx_sate)0: //状态0:等待空闲readybeginif(((uart_bit_num==4'd10)&&(uart_bit==9'd433)))rdy <=1'b1;else if( uart_tx_en)rdy <=1'b0;tx_sate<=3'd1;1://发送起始位beginif(uart_tx_en && (uart_bit_num==4'd0))TX<=1'b0;if(uart_tx_en && (uart_bit_num==4'd1)) tx_sate<=3'd2;2://发送有效数据beginif (uart_tx_en && (uart_bit_num>=4'd1) && (uart_bit_num<=4'd8))TX <= tx_buffer[uart_bit_num-1];if(uart_tx_en && (uart_bit_num==4'd9))tx_sate<=3'd3;3://发送校验位beginif(uart_tx_en && uart_bit_num==4'd9) TX <=^tx_buffer; if(uart_tx_en && (uart_bit_num==4'd10))tx_sate<=3'd4; 4://发送 停止位if(uart_tx_en && uart_bit_num==4'd10) TX <=1'b0;if(uart_tx_en && (uart_bit_num==4'd10)&&(uart_bit==9'd433))tx_sate<=3'd0; end
end
//--------------------------接收数据--------------------------------
always@(posedge clk_i or negedge rst_n_i)
beginif(!rst_n_i)beginrx_state<=0;uart_bit_cn<=0; uart_bit_num <=0;RX_delay<=0;data_out<=0;en_data_out<=0;uart_rx_buf<=8'd0;endelsebeginRX_delay<=RX;//有时钟就在动,不需要条件case(rx_state)0://空闲识别beginif(uart_bit_cn==433)uart_bit_cn<=0; else uart_bit_cn<=uart_bit_cn+1; if(uart_bit_cn==0)beginif(RX)uart_bit_num <= uart_bit_num+1;else//如果接收到不是1的数据,说明空闲结束beginuart_bit_num <=0;uart_bit_cnt<=0;rx_state<=1;endif( uart_bit_num==9) //连续接收10个1,判断是空闲,跳到状态1等待起始位beginuart_bit_num <=0;uart_bit_cnt<=0;rx_state<=1;endend1://起始位判断beginif(~RX&RX_delay)rx_state<=2;end2://接收有效数据beginif(uart_bit_cnt==9'd216 &&uart_bit_num>=4'd1 &&uart_bit_num<=4'd8)uart_rx_buf[uart_bit_num-1]<=RX; assign data_out = uart_rx_buf;if(uart_bit_cnt==9'd433 &&uart_bit_num>=4'd9 )rx_state<=3; end3://产生校验位(奇校验)beginif(uart_bit_num==4'd9) RX <=^uart_rx_buf;if(uart_bit_cnt==9'd433 &&uart_bit_num>=4'd10 )rx_state<=4;end4://检测到停止位 高电平beginif(uart_bit_cnt=433&& uart_bit_num=10) if(RX)en_data_out<=1;rx_state<=0;elserx_state<=0; end end
end
//检查收发数据是否一致
always@(*)
beginif(^tx_buffer==1&&^uart_rx_buf==1)falg=1;elsefalg=0;end
endmodule
参考:
巨详细的UART文档,学习UART的保证你不后悔 – 数字IC设计资料(IC前端|FPGA|ASIC) – EETOP 创芯网论坛 (原名:电子顶级开发网) –
USART串口协议 – 孤情剑客 – 博客园 (cnblogs.com)
《通信IC设计》 李庆华著 (第一章中的uart例子)
Verilog零基础入门_哔哩哔哩_bilibili(串口的发送和串口接收)