洛阳网站建设兼职,网站开发财务预算,青岛模板做网站,wordpress 登录功能文章目录 一、UART概述二、UART协议帧格式2.1 波特率2.2 奇校验ODD2.3 偶校验EVEN 三、UART接收器设计3.1 接收时序图3.2 Verilog代码3.3 仿真文件测试3.4 仿真结果3.5 上版测试 四、UART发送器设计4.1 发送时序图4.2 Verilog代码4.3 仿真文件测试4.4 仿真结果4.5 上板测试 五、… 文章目录 一、UART概述二、UART协议帧格式2.1 波特率2.2 奇校验ODD2.3 偶校验EVEN 三、UART接收器设计3.1 接收时序图3.2 Verilog代码3.3 仿真文件测试3.4 仿真结果3.5 上版测试 四、UART发送器设计4.1 发送时序图4.2 Verilog代码4.3 仿真文件测试4.4 仿真结果4.5 上板测试 五、UART回环测试5.1 系统框图5.2 Verilog代码5.3 仿真文件测试5.4 仿真结果5.5 上板测试 六、参考 一、UART概述 从《浅谈UARTTTLRS-232RS-485的区别》这篇文章我们知道了UART是一种串行、异步、全双工的通信协议属于协议层传输过程一般采用RS-232RS-485电平标准将所需传输的数据一位接一位地传输整体传输框架如下 串口通信由发送端和接收端构成两根信号线实现一根数据发送一根数据接收。
二、UART协议帧格式 在串口通信过程中数据接收与发送双方没有共享时钟因此双方必须协商好数据传输波特率。根据双方协议好的波特率接收端即可对发送端的数据进行采样。整个传输流程如下
传输线空闲时为高电平然后拉低一个码元时间表示起始位接着传输 8 个数据位先传输低位再传输高位然后传输一个校验位用来检测本次传输的数据是否正确最后输出高电平表示停止位可以是 1 位、1.5 位、2 位的高电平并且进入空闲状态等待下一次的数据传输
协议帧格式如下
2.1 波特率 根据双方协议好的传输速率接收端即可对发送端的数据进行采样。如果波特率为 9600也就是相当于每秒中划分成了 9600 等份的码元时间。常见的波特率标准为 300bps600bps800bps9600bps19200bps38400bps115200bps 等。通常对串口进行数据采样采用更高频的时钟。 一个码元的计算方法例如我们采样时钟为 50M 所使用的波特率为 115200传输 1个码元需要的时间( 1 / 115200 ) 8680ns
2.2 奇校验ODD 使完整编码有效位和校验位中的 “1” 的个数为奇数个。如果原来信息中 1 的个数为奇数个则校验位为 0这样所有信息中1的个数还是奇数 如果原来信息中 1 的个数为偶数个则校验位为 1这样所有信息中1的个数还是奇数。 常见的串口通信格式是8 位数据位1 位奇数校验位。以发送字符10010101和11010101 为例
数据第0位数据第1位数据第2位数据第3位数据第4位数据第5位数据第6位数据第7位校验位101010011101010110
2.3 偶校验EVEN 使完整编码有效位和校验位中的 “1” 的个数为偶数个。如果原来信息中 1 的个数为奇数个则校验位为 1这样所有信息中1的个数还是偶数 如果原来信息中 1 的个数为偶数个则校验位为 0这样所有信息中1的个数还是偶数。 常见的串口通信格式是8 位数据位1 位奇数校验位。以发送字符10010101和11010101 为例
数据第0位数据第1位数据第2位数据第3位数据第4位数据第5位数据第6位数据第7位校验位101010010101010111
三、UART接收器设计
开发平台vivado2021.1 开发芯片xc7a100tfgg484-2
3.1 接收时序图 本次实现11500波特率无奇偶校验位时钟频率为50M则每个码元计数次数为50000000/115200434。时序图如下
3.2 Verilog代码
timescale 1ns/1ns
module uart_rx#
(parameter CLK_FREQ d50_000_000, //时钟频率parameter UART_BPS d115200 //波特率
)
(input clk , input rst_n , input rx , //uart_rxoutput reg [7:0] rx_data , //uart_rx_dataoutput reg rx_data_valid //uart_rx_data_valid
);localparam BAUD_CNT_MAX CLK_FREQ/UART_BPS ;//一个码元的长度localparam BAUD_CNT_MAX_div2 BAUD_CNT_MAX/2 ; reg rx_reg1 ;reg rx_reg2 ;reg rx_reg3 ;reg rx_flag ;reg [23:0] baud_cnt ;reg [3:0] bit_cnt ;reg bit_flag ;//打两拍消除亚稳态
always (posedge clk, negedge rst_n) beginif(rst_n 1b0)beginrx_reg1 1b1;rx_reg2 1b1;endelse beginrx_reg1 rx;rx_reg2 rx_reg1;end
end//再打一拍用于判断RX下降沿
always (posedge clk, negedge rst_n) beginif(rst_n 1b0)rx_reg3 1b1;elserx_reg3 rx_reg2;end//接收范围
always (posedge clk, negedge rst_n) beginif(rst_n 1b0)rx_flag 1b0;else if (rx_reg3 1b1 rx_reg2 1b0)rx_flag 1b1;else if(bit_cnt d8 bit_flag 1b1)rx_flag 1b0;elserx_flag rx_flag;
end//baud_cnt:波特率计数器计数从 0 计数到 BAUD_CNT_MAX
always (posedge clk, negedge rst_n) beginif(rst_n 1b0)baud_cnt d0;else if(baud_cnt BAUD_CNT_MAX || rx_flag 1b0)baud_cnt d0;else if(rx_flag 1b1)baud_cnt baud_cnt 1b1;elsebaud_cnt baud_cnt;
end//bit_flag: baud_cnt 计数器计数到码元中间时采样的数据最稳定
always (posedge clk, negedge rst_n) beginif(rst_n 1b0)bit_flag 1b0;else if(baud_cnt BAUD_CNT_MAX_div2)bit_flag 1b1;elsebit_flag 1b0;end//bit_cnt:有效数据计数器 8 个有效数据不含起始位和停止位
always (posedge clk, negedge rst_n) beginif(rst_n 1b0)bit_cnt d0;else if(bit_cnt d8 bit_flag 1b1)bit_cnt d0;else if(bit_flag 1b1)bit_cnt bit_cnt 1b1;elsebit_cnt bit_cnt;
end//输出数据
always (posedge clk, negedge rst_n) beginif(rst_n 1b0)rx_data d0;else if(bit_cnt d1 bit_flag 1b1)rx_data {rx_reg3,rx_data [7:1]};elserx_data rx_data ;
end//输出数据有效信号当接受完8个数据位后拉高
always (posedge clk, negedge rst_n) beginif(rst_n 1b0)rx_data_valid 1b0;else if(bit_cnt d8 bit_flag 1b1)rx_data_valid 1b1;elserx_data_valid 1b0;
endendmodule3.3 仿真文件测试
这次仿真文件使用task任务来模拟uart发送数据
timescale 1ns/1ns
module tb_uart_rx();reg clk ;reg rst_n ;reg rx ;wire [7:0] rx_data ;wire rx_data_valid ;initial beginclk 0;rst_n 0;rx 1;#100rst_n 1;end//调用task函数连续发送八个数据initial begin#300tx_data(8h6);tx_data(8h6);tx_data(8h1);tx_data(8hA);tx_data(8hB);tx_data(8hC);tx_data(8hD);tx_data(8hE);tx_data(8hF);end//定义task函数task tx_data(input [7:0] tx_data);integer i;for (i0; i10; ii1 ) begincase(i)0: rx 1b0;1: rx tx_data[0];2: rx tx_data[1];3: rx tx_data[2];4: rx tx_data[3];5: rx tx_data[4];6: rx tx_data[5];7: rx tx_data[6];8: rx tx_data[7];9: rx 1b1;endcase#(434*20);//每发送完一个bit数据后延迟一个码元的时间endendtaskalways #10 clk ~ clk;
uart_rx#(.CLK_FREQ ( d50_000_000 ),.UART_BPS ( d115200 )
)u1_uart_rx
(.clk ( clk ),.rst_n ( rst_n ),.rx ( rx ),.rx_data ( rx_data ),.rx_data_valid ( rx_data_valid )
);endmodule3.4 仿真结果 由仿真结果可以看出我们接收到的依次是661ABCDEF8个数据和仿真文件给的一致。
3.5 上版测试 调用ila核准备抓取rx_datarx_data_valid信号。通过上位机发送数据观察rx_data是否正确 上位机没发送数据时候ila采集不到rx_data_valid信号拉高。 上位机发送一个9F时ila采集到rx_data_valid信号拉高并且输出rx_data为9F
四、UART发送器设计
4.1 发送时序图 4.2 Verilog代码
timescale 1ns / 1ps
module uart_tx#
(parameter CLK_FREQ d50_000_000, //时钟频率parameter UART_BPS d115200 //波特率
)
(input clk ,input rst_n ,input [7:0] tx_data , //发送数据input tx_data_en , //发送数据有效信号output reg tx
);localparam BAUD_CNT_MAX CLK_FREQ/UART_BPS;//待计数的码元周期最大值reg [7:0] tx_data_reg ;reg [23:0] baud_cnt ;reg [3:0] bit_cnt ;reg bit_flag ;reg tx_flag ;//将待发送的数据缓存起来
always (posedge clk or negedge rst_n) beginif(rst_n 1b0)tx_data_reg d0;else if(tx_data_en 1b1)tx_data_reg tx_data;elsetx_data_reg tx_data_reg;
end//tx_flag拉高时刻
always (posedge clk or negedge rst_n) beginif(rst_n 1b0)tx_flag 1b0;else if(tx_data_en 1b1)tx_flag 1b1;else if(bit_cnt d9 bit_flag 1b1)tx_flag 1b0;elsetx_flag tx_flag;
end//baud_cnt 计数器
always (posedge clk or negedge rst_n) beginif(rst_n 1b0)baud_cnt d0;else if(baud_cnt BAUD_CNT_MAX)baud_cnt d0;else if(tx_flag 1b1)baud_cnt baud_cnt 1b1;elsebaud_cnt d0;
end
//bit_flag拉高时刻
always (posedge clk or negedge rst_n) beginif(rst_n 1b0)bit_flag 1b0;else if(baud_cnt d1)bit_flag 1b1;elsebit_flag 1b0;
end//bit_cnt 计数器
always (posedge clk or negedge rst_n) beginif(rst_n 1b0)bit_cnt d0;else if(bit_cnt d9 bit_flag 1b1)bit_cnt d0;else if(bit_flag 1b1)bit_cnt bit_cnt 1b1;elsebit_cnt bit_cnt;
end//移位data由低到高发送至tx
always (posedge clk or negedge rst_n) beginif(rst_n 1b0)tx 1b1;else if(bit_flag 1b1)case(bit_cnt)0: tx 1b0;1: tx tx_data_reg[0];2: tx tx_data_reg[1];3: tx tx_data_reg[2];4: tx tx_data_reg[3];5: tx tx_data_reg[4];6: tx tx_data_reg[5];7: tx tx_data_reg[6];8: tx tx_data_reg[7];9: tx 1b1;default:tx 1b1;endcase
endendmodule
4.3 仿真文件测试 模拟两个数据1A3B的输入让tx模块发送
timescale 1ns / 1ns
module tb_uart_tx();reg clk ;reg rst_n ;wire tx ;reg [7:0] tx_data ;reg tx_data_en ;initial beginclk 0;rst_n 0;tx_data 8d0;tx_data_en 0;#100rst_n 1;#300tx_data 8h1a;tx_data_en 1;#20 tx_data_en 0;#(434*20*10100)//等待10个码元周期时间再等待100nstx_data 8h3b;tx_data_en 1;#20 tx_data_en 0;#(434*20*10100)$stop;endalways #10 clk ~clk;uart_tx#
(.CLK_FREQ ( d50_000_000 ),.UART_BPS ( d115200 )
)
u1_uart_tx
(.clk ( clk ),.rst_n ( rst_n ),.tx_data ( tx_data ),.tx_data_en ( tx_data_en ),.tx ( tx )
);endmodule
4.4 仿真结果 因为发送数据是1A00011010uart先发送起始位再发送数据位从低到高最后拉高一个停止位。理论上tx发送的顺序就是0_01011000_1 从仿真结果来看tx线上的顺序和理论一致3B数据同理。
4.5 上板测试 我们创建一个顶层调用uart_tx模块在顶层上输入一个数据让发送模块发出然后在上位机上接送观察和我们发送的数据是否一致代码如下
timescale 1ns / 1psmodule uart_top(input clk ,input rst_n ,output tx );reg [7:0] tx_data ;reg tx_data_en ;reg [1:0] cnt ;always (posedge clk or negedge rst_n) beginif(rst_n 1b0)cnt d0;else if(cnt d3)cnt cnt;else cnt cnt 1b1;endalways (posedge clk or negedge rst_n) beginif(rst_n 1b0)begintx_data d0;tx_data_en 1b0;endelse if (cnt d2)begintx_data 8h6D;tx_data_en 1b1;endelse begintx_data d0;tx_data_en 1b0;endenduart_tx#
(.CLK_FREQ ( d50_000_000 ),.UART_BPS ( d115200 )
)
u_uart_tx
(.clk ( clk ),.rst_n ( rst_n ),.tx_data ( tx_data ),.tx_data_en ( tx_data_en ),.tx ( tx )
);endmodule我们在顶层模块发送了一个 6D数据。 上板后上位机收到了 6D数据
五、UART回环测试
5.1 系统框图 上面我们测试了UART发送和接收模块都正常接下来我们将接收模块的输出信号接到发送模块的输入信号在上位机上面发送数据和接收数据看是否一致系统框图如下
5.2 Verilog代码
timescale 1ns / 1psmodule uart_top(input clk ,input rst_n ,input rx ,output tx );wire [7:0] rx_data ;wire rx_data_valid ;uart_rx#
(.CLK_FREQ ( d50_000_000 ),.UART_BPS ( d115200 )
)
u_uart_rx(.clk ( clk ),.rst_n ( rst_n ),.rx ( rx ),.rx_data ( rx_data ),.rx_data_valid ( rx_data_valid )
);uart_tx#
(.CLK_FREQ ( d50_000_000 ),.UART_BPS ( d115200 )
)
u_uart_tx
(.clk ( clk ),.rst_n ( rst_n ),.tx_data ( rx_data ),.tx_data_en ( rx_data_valid ),.tx ( tx )
);endmodule
5.3 仿真文件测试 我们依然用uart_rx里的测试代码调用task函数发送661abcdef
timescale 1ns / 1ps
module tb_uart_top();reg clk ;reg rst_n ;reg rx ;wire tx ;initial beginclk 0;rst_n 0;rx 1;#100rst_n 1;end//调用task函数连续发送八个数据initial begin#300tx_data(8h6);tx_data(8h6);tx_data(8h1);tx_data(8hA);tx_data(8hB);tx_data(8hC);tx_data(8hD);tx_data(8hE);tx_data(8hF);end//定义task函数task tx_data(input [7:0] tx_data);integer i;for (i0; i10; ii1 ) begincase(i)0: rx 1b0;1: rx tx_data[0];2: rx tx_data[1];3: rx tx_data[2];4: rx tx_data[3];5: rx tx_data[4];6: rx tx_data[5];7: rx tx_data[6];8: rx tx_data[7];9: rx 1b1;endcase#(434*20);//每发送完一个bit数据后延迟一个码元的时间endendtaskalways #10 clk ~clk;
uart_top u_uart_top(.clk ( clk ),.rst_n ( rst_n ),.rx ( rx ),.tx ( tx )
);endmodule
5.4 仿真结果 可以看出接收后的数据和发送的数据没问题
5.5 上板测试 可以看到发送和接收都正常
六、参考 UART介绍