背景
最近刚开始接触FPGA,在大概看了基本教材学习了Verilog后就买了一块黑金的开发板,开始正儿八经的撸代码。然后就到了UART通信这一节,基本上例程都是顶层模块例化UART收发模块,作者觉得这样不能很好的理解UART的底层协议,UART发送具体是怎么发送,又怎么接收,也不想直接ctrl+ch和ctrl+v,然后作者在参考正点原子的代码后,通过状态机实现500ms发送一个字节的数据和UART接收,具体请看下文,还有请正点原子支付下广告费。
协议
如下图所示,UART通信需要三根线,一根发送TX,一根接收RX,还有一根地。总线(TX和RX)空闲时处于高电平,发送方发送数据时,先发送起始位(逻辑0),然后以地为在前、高位在后的顺序将一个字节的每一个比特位发送出去(这一字节可以是7位或者8/9位),数据发送完成后,再发送1位校验位和停止位(可以是1位或者2位,逻辑1)。接收方的RX是与发送方的TX连接在一起的,因此,接收方检测RX上是否存在下降沿,是,则准备开始接收,因为UART通信没有时钟,因此只能规定多少时间发送一个比特位来保证数据收发不会出错,这就是比特率(或者说是波特率),单位是比特每秒(bit/s),一般情况下 ,比特率使用9600和115200,。本次作者的UART通信是采用8位数据位,1位停止位,没有奇偶检验位,共10位。
代码编写
协议清楚后开始撸代码。
UART发送
作者的思路是用状态机进行数据发送,初始化、发送、结束,发送周期是500ms,波特率是9600,系统频率是50MHz,所以需要对时钟脉冲计数50_000_000/9600=5208.33个,取整5208。下面是代码。
`timescale 1ns/1ps
module uart_tx(
input clk,
input rst_n,
output reg tx_pin);
parameter CLK_FREQ =50000000,
BPS =9600,
BPS_CNT =CLK_FREQ/BPS;
parameter IDLE =4'd0,
SEND =4'd1,
END =4'd2;
reg[7:0] data_buf;
reg[24:0] cnt1;//500ms 计数寄存器
reg tx_start;//计时到了置1,并开始发送
//计时500ms
always@(posedge clk,negedge rst_n)
if(!rst_n)
begin
cnt1<=0;
tx_start<=0;
end
else if(cnt1==25000000)
begin
cnt1<=0;
tx_start<=~tx_start;
end
else
cnt1<=cnt1+1;
reg[3:0] tx_cnt;
reg[24:0] clk_cnt;
reg[3:0] state;
always@(posedge clk,negedge rst_n)
if(!rst_n)
begin
state<=IDLE;
data_buf<=8'h5A;