串口接收模块——verilog实现
1、设计想法
原理与之前的串口发送模块一样,1位的起始位和8位的数据位再加上1位的停止位。唯一不同的是在接收的时候要考虑到有干扰的情况下,为了避免干扰,我们对每位数据进行多次采样,按出现概率大的值为该数据位的值。
如果按照通常想法在每bits位中间取值的话,bit3位出现图中的干扰很有可能会读出错误的值。所以需要对每位进行多次抽样进行判断。
每位要抽8次的话,那需要将每个波特段分成9等分。
2、状态机设定
3、模块代码
`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: Lclone // // Create Date: 2022/12/16 15:37:44 // Design Name: uart_byte_rx // Module Name: uart_byte_rx // Project Name: uart_byte_rx // Target Devices: // Tool Versions: // Description: 8位串口接收模块 // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module uart_byte_rx # ( parameter RX_BAUD = 9600, //波特率 parameter CLK_FQC = 50_000_000, //模块时钟频率 parameter BAUD_CNT = CLK_FQC/RX_BAUD) //模块每波特需要计数的次数(设置此端口方便仿真用) ( input Clk, //时钟频率接口 input Rst_n, //复位接口 input Uart_rx, //串口接收接口 output reg [7:0] Data, //接收到的数据接口 output reg Rx_done //接收完成信号 ); reg uart_rx_r; //延一拍 reg uart_rx_rr; //延两拍 reg uart_rx_rrr; //延三拍,减少亚稳态出现的概率 reg receiv_begin; //接收开始信号 reg receiv_flag; //接收状态信号 reg [ 3:0] state; //状态机寄存器 reg [15:0] baud_cnt; //波及计数器 reg [ 3:0] sampel_cnt; //采样计数器 reg sampel_en; //采样使能 reg sampel_ref; //样本寄存器 reg [ 3:0] acc; //累加寄存器 reg [ 3:0] bit_cnt; //数据位寄存器 always @(posedge Clk) begin //延两拍为下降沿捕获 uart_rx_r <= Uart_rx; uart_rx_rr <= uart_rx_r; uart_rx_rrr <= uart_rx_rr; end always @(posedge Clk or negedge Rst_n) begin //接收信号发生 if(Rst_n == 0) receiv_begin <= 0; else if(state == 0 & uart_rx_rrr & ~uart_rx_rr) receiv_begin <= 1'b1; else receiv_begin <= 0; end always @(posedge Clk or negedge Rst_n) begin //状态机 if(Rst_n == 0) begin state <= 0; sampel_ref <= 8'b0; acc <= 8'b0; Data <= 8'b0; end else case(state) 0: //空闲状态 if(receiv_begin == 1) state <= 3'd1; else state <= 0; 1: begin //抽样状态 if(sampel_en == 1) begin sampel_ref <= Uart_rx; state <= 3'd2; end else state <= 3'b1; end 2: begin //数据判断状态 acc <= acc + sampel_ref; if(sampel_cnt == 7) begin if(acc >= 4) begin Data[7] <= 1'b1; state <= 3'd3;acc <= 8'b0; end else begin Data[7] <= 0; state <= 3'd3;acc <= 8'b0; end end else state <= 3'd1; end 3: begin //数据移位状态 if(bit_cnt < 8) begin Data <= Data >> 1; state <= 3'd1; end else state <= 0; end default:; endcase end always @(posedge Clk or negedge Rst_n) begin //接收进行标志 if(Rst_n == 0) receiv_flag <= 0; else if(receiv_begin == 1) receiv_flag <= 1'b1; else if(bit_cnt == 9 & baud_cnt == BAUD_CNT/9*8) //这里设置为记到BAUD_CNT/9*8是为了让Rx_done信号提前一点产生, receiv_flag <= 1'b0; //避免因为Rx_done出现过晚,导致错过下一个起始位的下降沿。 end //后面和其相同的条件判断,也是因为相同原因设置的。 always @(posedge Clk or negedge Rst_n) begin //波特计数 if(Rst_n == 0) baud_cnt <= 0; else if(receiv_flag == 1) begin if(baud_cnt == BAUD_CNT - 1) baud_cnt <= 0; else baud_cnt <= baud_cnt + 1'b1; end else baud_cnt <= 0; end always @(posedge Clk or negedge Rst_n) begin //采样计数 if(Rst_n == 0) begin sampel_cnt <= 0; sampel_en <= 0; end else if(receiv_flag == 1) begin case(baud_cnt) BAUD_CNT/9*1-1 : begin sampel_cnt <= 0; sampel_en <=1; end BAUD_CNT/9*2-1 : begin sampel_cnt <= 1; sampel_en <=1; end BAUD_CNT/9*3-1 : begin sampel_cnt <= 2; sampel_en <=1; end BAUD_CNT/9*4-1 : begin sampel_cnt <= 3; sampel_en <=1; end BAUD_CNT/9*5-1 : begin sampel_cnt <= 4; sampel_en <=1; end BAUD_CNT/9*6-1 : begin sampel_cnt <= 5; sampel_en <=1; end BAUD_CNT/9*7-1 : begin sampel_cnt <= 6; sampel_en <=1; end BAUD_CNT/9*8-1 : begin sampel_cnt <= 7; sampel_en <=1; end BAUD_CNT/9*9-1 : sampel_cnt <= 0; default:sampel_en <=0; endcase end end always @(posedge Clk or negedge Rst_n) begin //数据位计数 if(Rst_n == 0) bit_cnt <= 0; else if(bit_cnt == 9 & baud_cnt == BAUD_CNT/9*8) bit_cnt <= 0; else if(baud_cnt == BAUD_CNT - 1) bit_cnt <= bit_cnt + 1'b1; end always @(posedge Clk or negedge Rst_n) begin //接收完成信号产生 if(Rst_n == 0) Rx_done <= 0; else if(bit_cnt == 9 & baud_cnt == BAUD_CNT/9*8) Rx_done <= 1'b1; else Rx_done <= 0; end endmodule
4、仿真验证
(1)仿真激励文件
`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: // // Create Date: 2022/12/16 21:36:04 // Design Name: // Module Name: uart_byte_rx_tb // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module uart_byte_rx_tb(); reg CLK_50M; reg RST_N; wire [7:0] Data; reg Uart_rx; reg [7:0] test_data; wire RX_DONE; uart_byte_rx # ( .RX_BAUD (9600), .CLK_FQC (50_000_000), .BAUD_CNT (50)) uart_byte_rx_inst ( .Clk (CLK_50M), .Rst_n (RST_N), .Uart_rx (Uart_rx), .Data (Data), .Rx_done (Rx_done) ); always #10 CLK_50M <= ~CLK_50M; initial begin CLK_50M <= 1'b0; RST_N <= 1'b0; Uart_rx <= 1'b1; test_data <= 8'h0; #100 RST_N <= 1'b1; #20 test_data <= 8'haf; #1000 Uart_rx <= 1'b0; #1000 Uart_rx <= test_data[0]; #1000 Uart_rx <= test_data[1]; #1000 Uart_rx <= test_data[2]; #1000 Uart_rx <= test_data[3]; #1000 Uart_rx <= test_data[4]; #1000 Uart_rx <= test_data[5]; #1000 Uart_rx <= test_data[6]; #1000 Uart_rx <= test_data[7]; #1000 Uart_rx <= 1'b1; test_data <= 8'h56; #1000 Uart_rx <= 1'b0; #1000 Uart_rx <= test_data[0]; #1000 Uart_rx <= test_data[1]; #1000 Uart_rx <= test_data[2]; #1000 Uart_rx <= test_data[3]; #1000 Uart_rx <= test_data[4]; #1000 Uart_rx <= test_data[5]; #1000 Uart_rx <= test_data[6]; #1000 Uart_rx <= test_data[7]; #1000 Uart_rx <= 1'b1; #2000 $stop; end endmodule
(2)仿真结果
本文作者:FPGA与ZYNQ的学习笔记
本文链接:https://www.cnblogs.com/Lclone/p/16988555.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步