上节内容介绍了UART串行通信协议中的数据帧格式,以及举例说明了在发送端如何组织数据帧。本节内容继续介绍协议中的其它内容。
1.波特率(Baud rate):
波特率是指每秒钟发送或接收的位( Bits)的总量,包含起始位、数据、校验位(如果采用校验),停止位的总和。异步串行通信常用波特率有150bps,300bps,600bps,1200bps,2400bps,4800bps,9600bps,19200bps,38400bps,57600bps,115200bps等,其中比较常用有9600bps,19200bps,38400bps,57600bps,115200bps等。
例1:波特率为115200,8位数据, 一个停止位,没有校验位,计算每秒钟发送数据字节数。
由于开始位为1位,停止位为1位,数据为8位,没有校验位,因此每个数据帧的构成为:
1 start bit + 8 data bits + 1 stop bit = 10 bits,而每发送10 bits的帧包含一个数据字节,因此每秒钟发送的字节数为 115200/10 = 11520 Bytes/s。
2. 波特率设计
在系统设计中一般提供的时钟频率都比较高如50 MHz,100 MHz等,如何做到系统时钟与波特率匹配呢?一般采用的方法如下:
a)发送端:
-
- 时钟直接分频,如时钟为100MHz,波特率为115200,则分频率设置为100000000/115200=868.05,取整为868。 虽然不能准确的整数化分频,但误差在万分之一以内。因此在发送中如果不是特别长的数据包,在时间上的误差积累一般也是可以承受的(数据包是指每次发送的数据是连续不间歇的传送,如传送一个文件,一副图片等)。一般解决办法是发送端可以设2个停止位,接收端设为1个接收停止位。这样接收端可以利用过采样的方式解决起始位相移的问题。
- 采用使能的方式,即分频的输出不是频率的直接输出,而是采用使能的方式。
注:关于分频输出时钟或使能的设计优缺点见文章Verilog 分频器及对应视频。
b)接收端:
一般采用过采样的方式进行数据接收,在异步串行通信中一般采用8倍或16倍过采样方式。这里我们以8倍过采样进行介绍。首先计算波特率:
-
- 时钟直接分频,由于采用8倍过采样,因此同样时钟为100MHz,115200的波特率,分频系数采用100000000/(115200*8) = 108.507 取整,取108(注意一定要向下取108而不是109),当然这个误差比较大,约200分之1。解决误差大的方法,可以利用PLL生成最接近的时钟如72MHz,此时72000000/115200 = 625,72000000/(115200*8) = 78.125,取整数倍78。虽然在接收端采用8倍过采样依然不能得到准确的整数倍分频,但误差却减小很多。
- 采用使能脉冲的方式,就是分频后的触发器输出结果不是作为时钟直接使用,而是作为后级电路的使能或选择端使用。
- 波特率使用建议,在进行FPGA异步串行通信开发时,建议采用使能的方式进行,这样可以保证正常时序收敛。
- 如果需要波特率从时钟频率获得整数分频,一般可以采用11.0592 MHz(或22.1184 MHz)的晶振(或震荡器),该时钟频率配合PLL经过分频后可以准确无误的得到所需常用波特率对应的整数倍分频系数。
例1:设时钟频率为100M,波特率为115200,用Verilog设计发送与接收波特率的时钟和使能信号。
- 时钟分频的方法
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
module baud_rate_clk #( parameter BAUD_RATE = 115200, parameter FREQUENCY = 100000000 ) ( input clk, input rst, output reg tx_clk, output reg rx_clk ); localparam TX_DIV_COE = FREQUENCY / (BAUD_RATE*2) - 1; //coe---coefficient,系数 localparam RX_DIV_COE = FREQUENCY / (BAUD_RATE*8*2) - 1; reg [18:0] tx_clk_div; reg [18:0] rx_clk_div; always@(posedge clk or posedge rst) if(rst) begin tx_clk_div <= 0; rx_clk_div <= 0; tx_clk <= 0; rx_clk <= 0; end else begin if(tx_clk_div == TX_DIV_COE) begin tx_clk_div <= 0; tx_clk <= !tx_clk; end else tx_clk_div <= tx_clk_div + 1; if(rx_clk_div == RX_DIV_COE) begin rx_clk_div <= 0; rx_clk <= !rx_clk; end else rx_clk_div <= rx_clk_div + 1; end endmodule |
Testbench 程序
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
`timescale 1 ns / 1 ps module tb ( ); parameter PERIOD = 10 ; //100Mhz reg clk; reg rst; wire tx_clk; wire rx_clk; initial begin clk = 0; #(PERIOD/2); forever #(PERIOD/2) clk=~clk; end initial begin rst = 1; #50 rst = 0; end baud_rate_clk #( .BAUD_RATE (115200), .FREQUENCY (100000000) ) bau_rate_clk_inst ( .clk (clk), .rst (rst), .tx_clk (tx_clk), .rx_clk (rx_clk) ); endmodule |

图1
从图1可以看出,rx_clk的频率接近tx_clk的8倍关系,但由于采用整数倍分频,因此分频系数由于误差带来频率和相位偏移,这样对于收发器正确采样是不利的。因此在接收端要能及时调整偏差才能消除误差积累。
- 产生波特率使能信号
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
module baud_rate_en #( parameter BAUD_RATE = 115200, parameter FREQUENCY = 100000000 ) ( input clk, input rst, output reg tx_bd_en, output reg rx_bd_en ); localparam TX_DIV_COE = FREQUENCY / (BAUD_RATE) - 1; localparam RX_DIV_COE = FREQUENCY / (BAUD_RATE*8) - 1; reg [18:0] tx_clk_div; reg [18:0] rx_clk_div; always@(posedge clk or posedge rst) if(rst) begin tx_clk_div <= 0; rx_clk_div <= 0; tx_bd_en <= 0; rx_bd_en <= 0; end else begin tx_bd_en <= 1'b0; if(tx_clk_div == TX_DIV_COE) begin tx_clk_div <= 0; tx_bd_en <= 1'b1; end else tx_clk_div <= tx_clk_div + 1; rx_bd_en <= 1'b0; if(rx_clk_div == RX_DIV_COE) begin rx_clk_div <= 0; rx_bd_en <= 1'b1; end else rx_clk_div <= rx_clk_div + 1; end endmodule |
Testbench 文件tb.v
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
`timescale 1 ns / 1 ps module tb ( ); parameter PERIOD = 10 ; //100Mhz reg clk; reg rst; wire tx_bd_en; wire rx_bd_en; initial begin clk = 0; #(PERIOD/2); forever #(PERIOD/2) clk = ~clk; end initial begin rst = 1; #50 rst = 0; end baud_rate_en #( .BAUD_RATE (115200), .FREQUENCY (100000000) ) bau_rate_en_inst ( .clk (clk), .rst (rst), .tx_bd_en (tx_bd_en), .rx_bd_en (rx_bd_en) ); endmodule |
Modelsim下仿真波形

图2
从图2中可以看出rx_bd_en比tx_bd_en快8倍,它们都是100Mhz时钟的一个时钟周期的宽度。在FPGA应用设计时推荐使用如图2的使能方式。
因此上节内容中的TX发送器的代码可以修改如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
module tx #( parameter PARITY = "ODD", parameter STOP_BIT = 1 ) ( input rst, input clk, input tx_bd_en, input [7:0] tx_data, input tx_rdy, output reg tx_ack, output reg tx ); localparam [3:0] IDLE = 0, START = 1, FIRST_BIT = 2, SEC_BIT = 3, THIRD_BIT = 4, FOUTH_BIT = 5, FIF_BIT = 6, SIX_BIT = 7, SEVN_BIT = 8, EIGTH_BIT = 9, PAR_BIT = 10, STOP1_BIT = 11, STOP2_BIT = 12; reg [7:0] tx_data_r; wire odd = ^tx_data_r; reg [3:0] tx_st; always@(posedge clk or posedge rst) if(rst) begin tx <= 1'b1; tx_st <= 0; tx_data_r <= 0; tx_ack <= 1'b0; end else begin if(tx_bd_en) case(tx_st) IDLE: begin tx_ack <= 1'b0; tx <= 1'b1; if(tx_rdy) begin tx_data_r <= tx_data; tx_ack <= 1'b1; tx_st <= START; end end START: begin tx <= 1'b0; tx_ack <= 1'b0; tx_st <= FIRST_BIT; end FIRST_BIT: begin tx <= tx_data_r[0]; tx_st <= SEC_BIT; end SEC_BIT: begin tx <= tx_data_r[1]; tx_st <= THIRD_BIT; end THIRD_BIT: begin tx <= tx_data_r[2]; tx_st <= FOUTH_BIT; end FOUTH_BIT: begin tx <= tx_data_r[3]; tx_st <= FIF_BIT; end FIF_BIT : begin tx <= tx_data_r[4]; tx_st <= SIX_BIT; end SIX_BIT: begin tx <= tx_data_r[5]; tx_st <= SEVN_BIT; end SEVN_BIT: begin tx <= tx_data_r[6]; tx_st <= EIGTH_BIT; end EIGTH_BIT: begin tx <= tx_data_r[7]; if( (PARITY=="ODD") || (PARITY=="EVEN") ) tx_st <= PAR_BIT; else tx_st <= STOP1_BIT; end PAR_BIT : begin tx_st <= STOP1_BIT; if(PARITY=="ODD") tx <= ~odd; else tx <= odd; end STOP1_BIT: begin tx <= 1'b1; if(STOP_BIT == 1) begin if(tx_rdy) begin tx_data_r <= tx_data; tx_ack <= 1'b1; tx_st <= START; end else tx_st <= IDLE; end else tx_st <= STOP2_BIT; end STOP2_BIT : begin tx <= 1'b1; if(tx_rdy) begin tx_data_r <= tx_data; tx_ack <= 1'b1; tx_st <= START; end else tx_st <= IDLE; end default: tx_st <= IDLE; endcase end endmodule |
本节内容主要介绍了UART中的波特率的概念,以及波特率在收、发模块中的不同要求,以及波特率的精度探讨。最后以发送器tx.v展示了波特率的使用。
思考题:100M时钟,8倍采样波特率分频系数采用100000000/(115200*8) = 108.507 取整,为什么取108而不是109?