vivado学习(4)uart 通信
本文介绍了UART通信协议的基本原理与实现方法。UART协议由起始位、8位数据、校验位和停止位共11位组成,通过波特率同步收发双方。发送模块通过计数器控制比特传输时间,接收模块利用中间采样技术捕获数据。文章详细阐述了发送和接收模块的设计思路,包括关键变量定义、状态控制和数据采样方法,并提供了Verilog代码实现。仿真结果验证了设计的正确性,展示了数据收发过程。该设计通过参数化配置支持不同波特率和
一、 uart 原理
协议由:起始位、数据(8位)、校验位、停止位,共11位组成,空闲时保持高位。
详细的协议不做赘述。
二、使用步骤
设计发送和接收模块,顶层模块给出发送使能和发送数据。
uart没有时钟线,我们只需要规定发送和接收方的波特率相同即可。例如波特率选择115200,时钟100M,那么1位传输所需要保持的时间是100M / 115200 = 868 (周期),所以我们需要计数器 baud_cnt ,它的最大值是867,记录单比特传输时间。
1.uart发送模块
输入输出:
1、clk,rstn,tx_de,[7:0]data
2、tx,tx_done
定义若干个变量(重点):
1、baud_cnt : 传输1bit需要维持的时间。
2、baud_time :完整的传输一次协议中传输1bit的次数。
3、tx_flag:表征一次协议传输有效,传输完成一次协议需要拉低,由顶层给的rx_de控制拉高。
4、bit_flag : 在传输1bit 的中间时刻拉高。
5、bit_cnt:用来表示传输的位数,和baud_time类似,但是是在bit_flag的控制下产生的(中间时刻采样),tx输出的数据也是在bit_cnt的控制下输出的。
6、tx : 当bit_flag为1时,对应bit_cnt 把tx_data_latch的对应位赋值给tx。
几个变量:
1、tx_data_latch:由顶层发的tx_data锁存而来,保证后续的时序。
2、baud_end,baud_mid,check,check_sum,
语法:
1、generate
if(check==0)
begin : “label1”
else if(check==1)
begin: “label2”
endgenerate
顶层传参check后,在generate中只会执行其中一条。
2、uart 接收模块
输入输出:
1、clk,rstn,rx
2、[7:0] rx_data, rx_de
几个重要模块:
1、baud_cnt : 传输1bit需要维持的时间。
2、baud_time :完整的传输一次协议中传输1bit的次数。
3、rx_flag:表征一次协议传输有效,传输完成一次协议需要拉低,由rx的下降沿给出。
4、bit_flag : 在传输1bit 的中间时刻拉高。
5、bit_cnt:用来表示传输的位数,和baud_time类似,但是是在bit_flag的控制下产生的(中间时刻采样),tx输出的数据也是在bit_cnt的控制下输出的。
6、rx : 是顶层给的输入,==tx。
几个变量:
1、rx_r1、rx_r2、rx_r3:打拍。
2、baud_end,baud_mid,check,check_sum,
方法:
在接收模块中没有像发送那样用case语句按bit_cnt按位分配bit输出到rx_data,用了下面的方法:
定义了两个变量:recv_data recv_de。用以下语句构造recv_data:
recv_data <= {rx,recv_data[7:1]};
思路:首先明确recv_de 和recv_data的作用,他们不需要考虑check,只要计数正确就赋值。recv_de 当bit_cnt==8,bit_flag == 1拉高。bit_flag这个条件保证是bit_cnt == 8持续了完整的1bit传输。recv_data中的bit_flag作用同理。rx_de和rx_data则用于输出,分check讨论即可。
核心代码:
always @(posedge clk or negedge rstn) begin
if(!rstn)
recv_de <= 1'b0;
else if(bit_cnt == 8'd8 && bit_flag == 1'b1 )
recv_de <= 1'b1;
else
recv_de <= 1'b0;
end
always @(posedge clk or negedge rstn) begin
if(!rstn)
recv_data <= 8'd0;
else if(bit_flag == 1'b1 && bit_cnt >= 8'd1 && bit_cnt <= 8'd8)
recv_data <= {rx,recv_data[7:1]};
else
recv_data <= recv_data;
end
always @(posedge clk or negedge rstn) begin
if(!rstn)
rx_de <= 1'd0;
else if(recv_de == 1'b1 && check == 1'd0)
rx_de <= 1'd1;
else if(bit_flag == 1'b1 && bit_cnt == bit_num - 2 && check != 1'b0 && check_sum==rx)
rx_de <= 1'd1;
else
rx_de <= 1'd0;
end
always @(posedge clk or negedge rstn) begin
if(!rstn)
rx_data <= 8'd0;
else if(recv_de == 1'b1 && check ==1'b0)
rx_data <= recv_data;
else if(bit_flag == 1'b1 && bit_cnt == bit_num - 2 && check != 1'b0&& check_sum==rx)
rx_data <= recv_data;
else
rx_data <= recv_data;
end
3、顶层模块
主要是写为发送模块提供的tx_de和tx_data以及传参这两件事。
写一个cnt,负责第一次的tx_de拉高,或上 tx_done == 1 就可以让tx自己为后续的协议发送提供tx_de了。
传参只需要定义好 baud 和check 即可。
rx和tx相连不要忘记。assign rx = tx;
4、仿真结果


火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)