上一篇文章里,我们介绍了rx模块在参数固定时后的写法,其实主要工作已经完成,剩下的就是对模块的扩展和完善。扩展和完善工作也不复杂,只要对uart几个可变参数进行变量化或参数化即可。在之前的文章中我们也曾这样做过,感兴趣的朋友可以也可以看一下之前tx端的写法(https://2743.com/archives/3478)。下面我们按照同样的做法,完善一下rx模块。

图1
uart 字符帧结构
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
module uart_rx_all #( parameter BAUD = 9600, parameter CLK_F = 50_000_000, parameter PARITY = 0, parameter DATA_BITS = 8, parameter STOPBIT = 1 ) ( input wire clk, input wire rst, input wire rx, output reg [ DATA_BITS - 1: 0 ] data, output reg parity_err, //接收校验错误 output reg done //接收完成提示 ); localparam NONE = 0; localparam ODD = 1; localparam EVEN = 2; localparam MARK = 3; localparam SPACE = 4; localparam STOPBIT_ONE = 0; localparam STOPBIT_ONEHALF = 1; localparam STOPBIT_TWO = 2; //【1】检测起始位 reg [ 1: 0 ] rx_r; always@( posedge clk or posedge rst ) begin if ( rst ) rx_r <= 0; else rx_r <= { rx_r[ 0 ], rx }; end wire en; //起始位信号 assign en = ( rx_r == 2'b10 ) ? 1'b1 : 1'b0; //【2】使用状态机来接收rx线上的数据 localparam [31:0] CNT_MAX = CLK_F / BAUD; //接收bit位计数(时钟周期个数)上限值 //[1] 定义状态空间 localparam FSM_IDLE = 0; //空闲状态 localparam FSM_START = 1; //接收起始位状态 localparam FSM_DATA = 2; //接收数据位 localparam FSM_PARITY = 3; //接收校验位 localparam FSM_STOP = 4; //接收停止位 //校验位 reg ifparity; //是否有检验位 reg parity_value; //校验位的值 //[2] 定义状态变量 reg [ 2: 0 ] cstate; //[3] 实现状态机 //每个状态做状态判断和跳转,以及根据状态输出 reg [ DATA_BITS - 1: 0 ] data_r; //暂存接收到的数据 reg [ 31: 0 ] cnt; //时钟周期计数,上限就是CNT_MAX reg startbit; //接收到的起始位值 reg [ 3: 0 ] num; //接收到的数据位个数 wire baud_tick = (cnt == CNT_MAX[31:0] - 1) ? 1'b1 : 1'b0; always@( posedge clk or posedge rst ) begin if ( rst ) begin cstate <= 0; data_r <= 0; num <= 0; done <= 0; cnt <= 0; startbit <= 0; parity_err <= 0; end else case ( cstate ) FSM_IDLE : begin data_r <= 0; done <= 0; if ( en ) begin cstate <= FSM_START; cnt <= 0; end else cstate <= FSM_IDLE; end FSM_START : begin cnt <= cnt + 1; startbit <= rx_r[ 1 ]; if ( cnt == CNT_MAX[31:1] && startbit ) //在bit位中点时进行采样 cstate <= FSM_IDLE ; //收到开始位为1 ,说明前面是个干扰信号 // else if ( startbit == 0 && cnt == CNT_MAX - 1 ) else if ( cnt == CNT_MAX - 1 ) begin cstate <= FSM_DATA; //收到开始位为0 ,是起始位 cnt <= 0; end end FSM_DATA : begin cnt <= cnt + 1 ; if ( cnt == CNT_MAX[31:1] ) //在bit位中点时进行采样 data_r[ num ] <= rx_r[ 1 ]; if ( cnt == CNT_MAX - 1 && num <= DATA_BITS - 1 ) //接收bit位少于DATA_BITS时继续接收 begin num <= num + 1 ; cnt <= 0; end else if ( num == DATA_BITS ) //接收bit位大于DATA_BITS时跳转 //写的明明是==,为何说大于?因为bit位数为8,计数应该是0-7 begin cstate <= ifparity ? FSM_PARITY : FSM_STOP; //有无校验位的判断 cnt <= 0; num <= 0; end end FSM_PARITY : begin cnt <= cnt + 1; if ( cnt == CNT_MAX[31:1] ) begin if ( parity_value == rx_r[ 1 ] ) //计算出的校验位的值 和 发送过来的校验位是否相等? parity_err <= 1'b0; else parity_err <= 1'b1; //保证奇偶校验错误也只发单周期的高电平 end else if ( cnt == CNT_MAX - 1 ) begin cstate <= FSM_STOP; cnt <= 0; end end FSM_STOP : begin cnt <= cnt + 1; case ( STOPBIT ) //不同长度停止位的处理 STOPBIT_ONE : //1停止位 begin if ( cnt == CNT_MAX[31:1] ) begin cstate <= FSM_IDLE; data <= data_r; done <= 1; cnt <= 0; end end STOPBIT_ONEHALF: //1.5停止位 begin if( cnt == ( CNT_MAX[31:0] - 1) ) begin cstate <= FSM_IDLE; data <= data_r; done <= 1; cnt <= 0; end end STOPBIT_TWO : //2停止位 begin if( cnt == ( CNT_MAX[31:0] + CNT_MAX[31:1] - 1) ) begin cstate <= FSM_IDLE; data <= data_r; done <= 1; cnt <= 0; end end default : begin if ( cnt == CNT_MAX - 1 ) begin cstate <= FSM_IDLE; data <= data_r; done <= 1; cnt <= 0; end end endcase end default : begin cstate <= 0; data_r <= 0; num <= 0; done <= 0; cnt <= 0; startbit <= 0; parity_err <= 0; end endcase end always@( * ) begin if ( rst ) begin ifparity = 0 ; parity_value = 0 ; end else case ( PARITY ) NONE : begin ifparity = 0 ; parity_value = 0 ; end ODD : begin ifparity = 1 ; parity_value = ~( ^ data_r ) ; end EVEN : begin ifparity = 1 ; parity_value = ^ data_r ; end MARK : begin ifparity = 1 ; parity_value = 1 ; end SPACE : begin ifparity = 1 ; parity_value = 0 ; end default : begin ifparity = 0 ; parity_value = 0 ; end endcase end endmodule |
仿真工程:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
`timescale 1ns/1ps module tb(); reg clk = 0; always #10 clk = ~clk; reg rst = 0; initial begin rst = 1; #20; rst = 0; #10; #800000; $stop; end wire txrx; uart_core # ( .SIM_DEBUG ("TRUE"), .BAUD (115200), .CLK_F (50_000_000) ) uart_core_inst ( .clk (clk), .rx (txrx), .tx (txrx), .rst (rst) ); endmodule |
仿真波形:

图2
工程代码(quartus)
[content_control]
[/content_control]