基于FPGA实现uart串口模块(Verilog)--------接收模块及思路总结
基于FPGA实现uart串口模块(Verilog)--------接收模块及思路总结
uart通信协议简单理解为串转并和并转串的两个模块。同时必须保证数据的正确性。且输入输出端为串行。
此次实现uart协议通过回环来保证数据接收发送的正确。用状态机来理解(也不知道是不是状态机,觉得这样写比较好理解)。
两个接收,发送是对于开发板来说的。开发板的接收端连接的是pc机的发送端。反之。
波特率
串口发送接收的速度,一秒传输多少的数据,此次基于9600波特率。(比特率与波特率不同,比特率为在一个波特率内传输的bit数)
实现原理
(代码仿真的时候,可以使用01010101和10101010这两个数分别来检验模块的正确性)
可以看出,数据为高为通常状况,当需要发送时,首先发送一个低电平,然后开始传输数据,等八位数据发送结束后,再发送一个结束位。这样,结束8bit数据的发送。从上面仿真图的可以很清楚的看出发送接收状况。当接收到起始位时,表示开始接收。具体接收会在下文代码里边具体展现。通过状态机很好的理解。
同时发送的时候,需要先发送最低位。具体在下图表示。
代码实现
module uart_rx(
//-----------input
clk,rst_n,rx_data,
//-----------output
po_data,po_flag
);
input clk;
input rst_n;
input rx_data;
output [7:0] po_data;
output po_flag;
//------------------------------
parameter idle = 2'd0,
start = 2'd1,
work = 2'd2,
stop = 2'd3;
//------------------------------
/*这里得到5207的方法是系统时钟周期50_000_000/9600 可以改变两者数据实现不同的传输需求*/
localparam baud_cnt_end = 5207 ;
localparam baud_cnt_m = (baud_cnt_end + 1) / 2 - 1;
//------------------------------
reg rx1;
reg rx2;
reg rx_flag;
reg [12:0] baud_cnt;
reg bit_flag;
reg [3:0] bit_cnt;
reg po_flag;
reg [7:0] po_data;
reg [1:0] current_state;
reg [1:0] next_state;
//------------------------------
//第一个进程,同步时序always块,形式固定
[email protected](posedge clk or negedge rst_n)
begin
if(!rst_n)
current_state <= idle;
else
current_state <= next_state;
end
//第二个always,组合逻辑模块,描述状态迁移条件判断
[email protected](*)
begin
case(current_state)
idle:/*空闲状态*/
begin
rx_flag = 1'b0;
begin
if(nedge)/*当接收到下降沿的时候开始进入发送起始位状态*/
next_state = start;
else
next_state = idle;
end
end
start:/*发送起始位*/
begin
rx_flag = 1'b1;
begin
if(bit_cnt == 4'b1)
next_state = work;
else
next_state = start;
end
end
work:/*发送数据位*/
begin
rx_flag = 1'b1;
begin
if(bit_cnt == 4'd9)
next_state = stop;
else
next_state = work;
end
end
stop:/*发送停止位*/
begin
rx_flag = 1'b1;
begin
if(bit_cnt == 4'd10)
next_state = idle;
else
next_state = stop;
end
end
default:
begin
next_state = idle;
end
endcase
end
//第三个进程,描述输出,同步时序always块
//下降沿检测------------------------------
/*跨时钟域处理!!?后文专门解释所有的跨时钟域问题
但bit打两拍*/
//reg rx1;
//reg rx2;
//reg rx3;
[email protected](posedge clk)
begin
rx1 <= rx_data;
rx2 <= rx1;
end
wire nedge;
assign nedge = rx2 && ~rx1;
/*波特率计数,在发送状态计数*/
//定义band_cnt------------------------------
//reg [12:0] baud_cnt;
[email protected](posedge clk or negedge rst_n)
begin
if(!rst_n)
baud_cnt <= 13'd0;
else
if(baud_cnt == baud_cnt_end)
baud_cnt <= 13'd0;
else
if(rx_flag)
baud_cnt <= baud_cnt + 1'b1;
else
baud_cnt <= 13'd0;
end
/*发送接收位这里取数据是在接收中点处取数据,因为是串转并,所以在哪里接收数据都无所谓后面等会了
把小梅哥用数组接收总结下*/
//define bit_flag------------------------------
//reg bit_flag;
[email protected](posedge clk or negedge rst_n)
begin
if(!rst_n)
bit_flag <= 1'b0;
else
if(baud_cnt == baud_cnt_m)
bit_flag <= 1'b1;
else
bit_flag <= 1'b0;
end
/*发送的是第多少位*/
//define bit_cnt------------------------------
//reg [3:0] bit_cnt;
[email protected](posedge clk or negedge rst_n)
begin
if(!rst_n)
bit_cnt <= 4'd0;
else
if(rx_flag == 0)
bit_cnt <= 4'd0;
else
if(baud_cnt == baud_cnt_end)
bit_cnt <= bit_cnt + 1'b1;
end
/*接收结束信号*/
//define po_flag------------------------------
//reg po_flag;
[email protected](posedge clk or negedge rst_n)
begin
if(!rst_n)
po_flag <= 1'b0;
else
if(bit_cnt == 4'd10)
po_flag <= 1'b1;
else
po_flag <= 1'b0;
end
/*数据寄存*/
reg [7:0] po_data_r;
[email protected](posedge clk )
begin
if(bit_cnt == 4'd10)
po_data <= po_data_r;
else
po_data <= 8'd0;
end
/*数据转换,串行转并行数据,在相应的数据接收数据,忽略起始位和结束位*/
[email protected](posedge clk or negedge rst_n)
begin
if(!rst_n)
po_data_r <= 8'b0;
else
if(rx_flag)
if(bit_flag)
case(bit_cnt)
4'd1 : po_data_r[0] <= rx2 ;
4'd2 : po_data_r[1] <= rx2 ;
4'd3 : po_data_r[2] <= rx2 ;
4'd4 : po_data_r[3] <= rx2 ;
4'd5 : po_data_r[4] <= rx2 ;
4'd6 : po_data_r[5] <= rx2 ;
4'd7 : po_data_r[6] <= rx2 ;
4'd8 : po_data_r[7] <= rx2 ;
default:po_data_r <= po_data_r ;
endcase
else
po_data_r <= po_data_r;
else
po_data_r <= 8'd0;
end
endmodule