Verilog UART通信实现

  此代码实现了波特率低至300pbs,且速率可调的UART 8N1通信协议,顶层模块为UART回环测试

 

综合后的RTL图

 

 

1. UART接收模块

  1 //UART接收模块
  2 module uart_rx #(
  3 parameter    SYS_CLK        =    50_000_000,        //50MHz系统时钟,用于计数器
  4 parameter    UART_BPS    =    115200            //波特率
  5 )(
  6 input clk,                                    //系统输入时钟
  7 input rst_n,                                //系统复位信号,低电平有效
  8 input rxd,                                    //UART数据接收信号
  9 
 10 output reg [7:0] rxd_data,                    //UART数据接收存储器
 11 output reg rxd_done                            //UART一帧数据接收完成标志,高表示接收完成
 12 );
 13 
 14 localparam    BPS_CNT_MAX    =    SYS_CLK / UART_BPS;            //UART 1bit说需要的时钟周期数
 15 
 16 reg rxd_d1;                                    //对UART rxd数据信号打第一拍存储器
 17 reg rxd_d2;                                    //对UART rxd数据信号打第二拍存储器
 18 reg rxd_flag;                                //UART接收数据采集标志
 19 reg [17:0] bps_cnt;                            //UART 1bit计数器,可支持波特率300bps计数
 20 reg [3:0] rxd_bit_cnt;                        //UART数据接收位数标志计数器,即标志现在第几位开始接收
 21 reg [7:0] data;                                //UART数据接收暂存器
 22 
 23 wire rxd_start_flag;                        //UART一帧数据开始标识
 24 
 25 //下降沿检测,即检测到低为一帧的开始,如果在传输过程中怎么识别?如前一位是1,后面是0,
 26 //所以增加了一个rxd_flag接收标志信号,只要一帧开始,则此信号就为高,后面的操作也是基于此信号,从而避开这个开始信号的低电平
 27 assign    rxd_start_flag = ((~rxd_d1) && rxd_d2);        
 28 
 29 //对UART rxd数据信号打第一拍
 30 always @(posedge clk or negedge rst_n)
 31     if(!rst_n)
 32         rxd_d1 <= 1'b0;
 33     else
 34         rxd_d1 <= rxd;
 35 
 36 //对UART rxd数据信号打第二拍
 37 always @(posedge clk or negedge rst_n)
 38     if(!rst_n)
 39         rxd_d2 <= 1'b0;
 40     else
 41         rxd_d2 <= rxd_d1;
 42 
 43 //接收数据采集标志信号处理
 44 always @(posedge clk or negedge rst_n)
 45     if(!rst_n)
 46         rxd_flag <= 1'b0;
 47     else if(rxd_start_flag)
 48         rxd_flag <= 1'b1;
 49     else if((rxd_bit_cnt == 4'd9) && (bps_cnt == BPS_CNT_MAX / 2))        //在停止位半位时间时,将接收数据采集标志信号设为失效
 50         rxd_flag <= 1'b0;
 51     else
 52         rxd_flag <= rxd_flag;
 53 
 54 //UART 1bit计时        
 55 always @(posedge clk or negedge rst_n)
 56     if(!rst_n)
 57         bps_cnt <= 18'b0;
 58     else if(rxd_flag)
 59         begin        
 60             if(bps_cnt == BPS_CNT_MAX - 1'b1)
 61                 bps_cnt <= 18'b0;                            
 62             else
 63                 bps_cnt <= bps_cnt + 1'b1;    
 64         end
 65     else
 66         bps_cnt <= 18'b0;
 67     
 68 //UART接收位标志,即标志现在第几位开始接收        
 69 always @(posedge clk or negedge rst_n)
 70     if(!rst_n)
 71         rxd_bit_cnt <= 4'b0;
 72     else if(rxd_flag)
 73         begin
 74             if(bps_cnt == BPS_CNT_MAX - 1'b1)                
 75                 rxd_bit_cnt <= rxd_bit_cnt + 1'b1;
 76             else
 77                 rxd_bit_cnt <= rxd_bit_cnt;
 78         end
 79     else
 80         rxd_bit_cnt <= 4'b0;
 81 
 82         
 83 //接收完成信号标志    
 84 //其实可以一接受完数据就可以将rxd_done置1,即去掉bps_cnt == BPS_CNT_MAX / 2
 85 always @(posedge clk or negedge rst_n)
 86     if(!rst_n)
 87         rxd_done <= 1'b0;
 88     else if(rxd_bit_cnt == 4'd9)         
 89         rxd_done <= 1'b1;
 90     else
 91         rxd_done <= 1'b0;
 92 
 93 //接收数据,从低位开始接收
 94 always @(posedge clk or negedge rst_n)
 95     if(!rst_n)
 96         data <= 8'b0;
 97     else if(rxd_flag)
 98         if(bps_cnt == BPS_CNT_MAX / 2)
 99             case(rxd_bit_cnt)
100                 4'd1:    data[0] <= rxd_d2;
101                 4'd2:    data[1] <= rxd_d2;
102                 4'd3:    data[2] <= rxd_d2;
103                 4'd4:    data[3] <= rxd_d2;
104                 4'd5:    data[4] <= rxd_d2;
105                 4'd6:    data[5] <= rxd_d2;
106                 4'd7:    data[6] <= rxd_d2;
107                 4'd8:    data[7] <= rxd_d2;    
108                 default:    
109                         data <= data;
110             endcase
111         else 
112             data <= data;
113     else
114         data <= 8'b0;
115 
116 //接收完成标志为高时,则表示数据接收完成,将数据暂存器中的数据给到输出
117 always @(posedge clk or negedge rst_n)
118     if(!rst_n)
119         rxd_data <= 8'b0;
120     else if(rxd_done)
121         rxd_data <= data;
122     else
123         rxd_data <= 8'b0;
124 
125 endmodule
View Code

 

2. UART发送模块

  1 //UART发送模块
  2 module uart_tx #(
  3 parameter    SYS_CLK        =     50_000_000,        //50MHz系统时钟,用于计数器
  4 parameter    UART_BPS     =     115200            //波特率
  5 )(
  6 input clk,                                    //系统输入时钟
  7 input rst_n,                                //系统复位信号,低电平有效
  8 input txd_en,                                //UART数据发送使能信号
  9 input [7:0] txd_data,                         //UART待发送数据存储器
 10 output reg txd                                //UART数据发送信号
 11 );
 12 
 13 localparam    BPS_CNT_MAX    =    SYS_CLK / UART_BPS;            //UART 1bit说需要的时钟周期数
 14 
 15 reg txd_d1;                                    //对UART txd数据信号打第一拍存储器
 16 reg txd_d2;                                    //对UART txd数据信号打第二拍存储器
 17 reg txd_flag;                                //UART发送数据采集标志
 18 reg [17:0] bps_cnt;                            //UART 1bit计数器,可支持波特率300bps计数
 19 reg [3:0] txd_bit_cnt;                        //UART数据发送位数标志计数器,即标志现在发送第几位数据
 20 reg [7:0] data;                                //UART数据发送暂存器
 21 
 22 wire txd_en_flag;                            //数据开始传输的标志
 23 
 24 //边沿检测,上升沿,一帧的开始标志;txd_en一帧发送过程中一直为高,所以这里只有一帧开始时检测为高,
 25 //其余时间均为低,所以后面的txd_flag、data均在一检测到上升沿时,进行赋值
 26 assign txd_en_flag = (txd_d1 && (~txd_d2));    
 27 
 28 //对UART txd_en数据发送使能信号打第一拍
 29 always @(posedge clk or negedge rst_n)
 30     if(!rst_n)
 31         txd_d1 <= 1'b0;
 32     else
 33         txd_d1 <= txd_en;
 34 
 35 //对UART txd_en数据发送使能信号打第二拍
 36 always @(posedge clk or negedge rst_n)
 37     if(!rst_n)
 38         txd_d2 <= 1'b0;
 39     else
 40         txd_d2 <= txd_d1;
 41     
 42 //发送数据采集标志信号处理
 43 always @(posedge clk or negedge rst_n)
 44     if(!rst_n)
 45         txd_flag <= 1'b0;
 46     else if(txd_en_flag)
 47         txd_flag <= 1'b1;
 48     else if((txd_bit_cnt == 4'd9) && (bps_cnt == BPS_CNT_MAX / 2))
 49         txd_flag <= 1'b0;        
 50     else 
 51         txd_flag <= txd_flag;    
 52     
 53 //UART 1bit计时,在txd_flag有效时,开始计时
 54 always @(posedge clk or negedge rst_n)
 55     if(!rst_n)
 56         bps_cnt <= 18'b0;
 57     else if(txd_flag)
 58         begin
 59             if(bps_cnt == BPS_CNT_MAX - 1'b1)
 60                 bps_cnt <= 18'b0;                
 61             else                    
 62                 bps_cnt <= bps_cnt + 1'b1;                    
 63         end
 64     else
 65         bps_cnt <= 18'b0;
 66 
 67 //发送完成信号标志    
 68 //其实可以一发送完数据就可以将rxd_done置1,即去掉bps_cnt == BPS_CNT_MAX / 2
 69 always @(posedge clk or negedge rst_n)
 70     if(!rst_n)
 71         txd_bit_cnt <= 4'b0;
 72     else if(txd_flag)
 73         begin
 74             if(bps_cnt == BPS_CNT_MAX - 1'b1)                
 75                 txd_bit_cnt <= txd_bit_cnt + 1'b1;
 76             else
 77                 txd_bit_cnt <= txd_bit_cnt;
 78         end
 79     else
 80         txd_bit_cnt <= 4'b0;
 81             
 82 //一检测到发送指令,就将待发送数据暂存到data存储器中;即一检测到上升沿信号,就将数据存入暂存器
 83 always @(posedge clk or negedge rst_n)
 84     if(!rst_n)
 85         data <= 8'b0;
 86     else if(txd_en_flag)
 87         data <= txd_data;
 88     else if((txd_bit_cnt == 4'd9) && (bps_cnt == BPS_CNT_MAX / 2))
 89         data <= 8'b0;
 90     else
 91         data <= data;  //不能data <= txd_data;        
 92 
 93 //发送数据,数据从低位开始发送
 94 always @(posedge clk or negedge rst_n)
 95     if(!rst_n)
 96         txd <= 1'b1;
 97     else if(txd_flag)
 98         case(txd_bit_cnt)
 99             4'd0:    txd <= 1'b0;        //一帧数据的开始信号
100             4'd1:    txd <= data[0];
101             4'd2:    txd <= data[1];
102             4'd3:    txd <= data[2];
103             4'd4:    txd <= data[3];
104             4'd5:    txd <= data[4];
105             4'd6:    txd <= data[5];
106             4'd7:    txd <= data[6];
107             4'd8:    txd <= data[7];
108             4'd9:    txd <= 1'b1;        //一帧数据的结束信号
109             default:
110                     txd <= 1'b1;        
111         endcase
112     else
113         txd <= 1'b1;
114         
115 endmodule
View Code

 

3. UART顶层模块,回环测试

 1 //UART顶层模块,回环测试
 2 module uart #(
 3 parameter SYS_CLK = 50_000_000,            //50MHz系统时钟,用于计数器
 4 parameter UART_BPS = 115200                //波特率,可支持波特率低至300bps的传输速率
 5 )(
 6 input clk,                                //系统输入时钟    
 7 input rst_n,                            //系统复位信号,低电平有效
 8 input rxd,                                //UART数据接收信号
 9 output txd                                //UART数据发送信号
10 );
11 
12 wire txd_rxd_flag;                        //用于连接数据接收模块的一帧数据接收完成信号rxd_done,与发送模块的发送使能信号txd_en
13 wire [7:0]data;                            //用于连接数接收模块、发送模块的数据
14 
15 //UART接收模块例化
16 uart_rx  #(
17 .SYS_CLK(SYS_CLK),
18 .UART_BPS(UART_BPS)
19 )
20 rx1(
21 .clk(clk),
22 .rst_n(rst_n),
23 .rxd(rxd),
24 .rxd_data(data),
25 .rxd_done(txd_rxd_flag)
26 );
27 
28 //UART发送模块例化
29 uart_tx  #(
30 .SYS_CLK(SYS_CLK),
31 .UART_BPS(UART_BPS)
32 )
33 tx1(
34 .clk(clk),
35 .rst_n(rst_n),
36 .txd_en(txd_rxd_flag),
37 .txd_data(data),
38 .txd(txd)
39 );
40 
41 endmodule
View Code

 

总结:

1. 注意一帧开始的标志,对于接收模块,使用的是边沿检测中的下降沿;发送模块使用边沿检测的上升沿

2. 接收模块中的数据接收过程标志,是基于下降沿的,后面的具体工作就基于这个过程标志rxd_flag;这么做的原因是因为避开数据传输过程中出现的下降沿,避免误判一帧开始

3. 发送模块中的数据暂存寄存器data、发送过程标志txd_flag,要在一开始检测到一帧开始时,就要给其赋值,否则后面一帧开始传输使能信号txd_en会一直为高,则边沿检测的上升沿随后将一直为低,后面的数据将无法基于此操作

4. UART传输协议,数据从低位到高位开始传输

5. UART空闲状态,处于高电平,所以接收模块,开始标志使用下降沿检测,即发送端发送一个低电平表示一个开始位

 

posted @ 2021-05-29 15:13  秋水寒林  阅读(491)  评论(0编辑  收藏  举报