1. 串口接收原理
基本原理:通过数据起始位判断要是否要开始接收的数据,通过采样的方式确定每一位数据是0还是1。
如何判断数据起始位到来:通过边沿检测电路检测起始信号的下降沿
如何采样:一位数据采多次,统计得到高电平出现的次数,次数多的就是该位的电平值
2. 自己写的代码(不完善)
设计代码
module uart_byte_rx(clk,rstn,uart_byte_rx,blaud_set,data,rx_done
);input clk;input rstn;input uart_byte_rx;input blaud_set;output reg [7:0] data;output reg rx_done;//Blaud_set = 0时,波特率 = 9600;//Blaud_set = 1时,波特率 = 19200;//Blaud_set = 2时,波特率 = 38400;//Blaud_set = 3时,波特率 = 57600;//Blaud_set = 4时,波特率 = 115200;reg[17:0] bps_dr;always@(*)case(blaud_set)0: bps_dr = 1000000000/9600/20;1: bps_dr = 1000000000/19200/20;2: bps_dr = 1000000000/38400/20;3: bps_dr = 1000000000/57600/20;4: bps_dr = 1000000000/115200/20;endcasereg [1:0] test;reg get_en;always@(posedge clk or negedge rstn) //边沿检测,使能后续的采样if(!rstn) begindata <= 0;test <= 0;get_en <= 0;rx_done <= 0;endelse begintest[0] <= uart_byte_rx;test[1] <= test[0];if((test[0] ==0 )&&(test[1] == 1))get_en <= 1;rx_done <= 0;endreg [17:0] div_cnt;reg [3:0] counter;reg [3:0] tx_counter;always@(posedge clk or negedge rstn) //计数时钟,一个计数周期代表一位数据if(!rstn) div_cnt <= 0;else if(get_en)beginif(div_cnt == 434 - 1)div_cnt <= 0;elsediv_cnt <= div_cnt + 1'd1;end wire bps_clk;assign bps_clk = (div_cnt == 1);always@(posedge clk or negedge rstn) //数据段,包含起始位和终止位,共十段if(!rstn) tx_counter <= 0;else if(get_en)beginif(tx_counter == 11)tx_counter <= 0;else if(div_cnt == 1)tx_counter <= tx_counter + 1'd1;end always@(posedge clk or negedge rstn) //数据采样,每段数据采样8次。if(!rstn)counter <= 0;else if(div_cnt == 3)counter <= 0;else if((div_cnt == 1*433/8 - 10)||(div_cnt == 2*433/8 - 10)||(div_cnt == 3*433/8 - 10)||(div_cnt == 4*433/8 - 10)||(div_cnt == 5*433/8 - 10)||(div_cnt == 6*433/8 - 10)||(div_cnt == 7*433/8 - 10)||(div_cnt == 8*433/8 - 10))counter <= counter + uart_byte_rx;always@(posedge clk or negedge rstn) //if(!rstn) counter <= 0;else if(div_cnt == 2)case(tx_counter)2:if(counter > 4 )data[0] <= 1; else if(counter <4) data[0] <= 0;3:if(counter > 4 )data[1] <= 1; else if(counter <4) data[1] <= 0; 4:if(counter > 4 )data[2] <= 1; else if(counter <4) data[2] <= 0; 5:if(counter > 4 )data[3] <= 1; else if(counter <4) data[3] <= 0; 6:if(counter > 4 )data[4] <= 1; else if(counter <4) data[4] <= 0; 7:if(counter > 4 )data[5] <= 1; else if(counter <4) data[5] <= 0; 8:if(counter > 4 )data[6] <= 1; else if(counter <4) data[6] <= 0; 9:if(counter > 4 )data[7] <= 1; else if(counter <4) data[7] <= 0;11:begin rx_done <= 1; get_en <= 0; div_cnt <= 0; enddefault: begin rx_done <= 0; data <= data; endendcase
endmodule
仿真波形
3. 看完视频后写的代码(完善)
设计代码
3.1 需学习的点:
1.将div_cnt划分为最小时间段
2.某些判断信号直接用assign利用,而不需要利用寄存器
3.仿真代码中task的使用
module uart_byte_rx1(clk,rstn,blaud_set,uart_rx,data,rx_done
);input clk;input rstn;input [2:0]blaud_set;input uart_rx;output reg [7:0] data;output rx_done;reg [8:0] bps_dr;always@(*)case(blaud_set)0:bps_dr = 1000000000/9600/16/20;1:bps_dr = 1000000000/19200/16/20;2:bps_dr = 1000000000/38400/16/20;3:bps_dr = 1000000000/57600/16/20;4:bps_dr = 1000000000/115200/16/20;default : bps_dr = 1000000000/9600/16/20;endcase//边沿信号检测reg [1:0] uart_rx_r; //用两位寄存器分别存储两个时间沿的uart_rx信号always@(posedge clk) beginuart_rx_r[0] <= uart_rx;uart_rx_r[1] <= uart_rx_r[0];end//将两位寄存器的值直接通过导线输出进行判断(不需要再使用寄存器)wire nedge_uart_rx; //掌握一下这个方法,之前一直使用的是寄存器//法一://assign nedge_uart_rx = ((uart_rx_r[0] == 0)&&(uart_rx_r == 1));//法二:assign nedge_uart_rx = (uart_rx_r == 2'b10);reg rx_en;always@(posedge clk or negedge rstn)if(!rstn)rx_en <= 0;else if(nedge_uart_rx)rx_en <= 1;else if(rx_done)rx_en <= 0;//周期计数器reg [8:0] div_cnt;always@(posedge clk or negedge rstn)if(!rstn)div_cnt <= 0;else if(rx_en) beginif(div_cnt == bps_dr - 1)div_cnt <= 0;elsediv_cnt <= div_cnt + 1'd1;endelsediv_cnt <= 0;wire [3:0]bps_clk_16x; //(一定要记得加位宽)采样信号,这种写法很灵活assign bps_clk_16x = bps_dr/2; //采样每一段的中点值,同时也可以用它来计数。//发送一字节的数据有需要十个数据位,每位数据有16个小段供采样,共160reg [7:0]bps_cnt;always@(posedge clk or negedge rstn)if(!rstn)bps_cnt <= 0;else if(rx_en) beginif(bps_cnt == 159)bps_cnt <= 0;else if(div_cnt ==bps_clk_16x)bps_cnt <= bps_cnt + 1'd1; endelse bps_cnt <= 0;reg[2:0] r_data[7:0];//二维数据,代表八个r_data,每个r_data有3位寄存器存储数值。reg[2:0] sta_data;reg[2:0] sto_data;always@(posedge clk or negedge rstn)if(!rstn)beginsta_data <= 0;sto_data <= 0;r_data[0] <= 0; //语法规定,二维数组赋值要分开赋值r_data[1] <= 0; r_data[2] <= 0;r_data[3] <= 0; r_data[4] <= 0; r_data[5] <= 0; r_data[6] <= 0; r_data[7] <= 0; endelse if(div_cnt == bps_clk_16x - 1)case(bps_cnt) //下面合在一起的写法是允许的0:beginr_data[0] <= 0; r_data[1] <= 0; r_data[2] <= 0;r_data[3] <= 0; r_data[4] <= 0; r_data[5] <= 0; r_data[6] <= 0; r_data[7] <= 0;end 5,6,7,8,9,10,11: sta_data <= sta_data + uart_rx;21,22,23,24,25,26,27: r_data[0] <= r_data[0] + uart_rx;37,38,39,40,41,42,43: r_data[1] <= r_data[1] + uart_rx;53,54,55,56,57,58,59: r_data[2] <= r_data[2] + uart_rx;69,70,71,72,73,74,75: r_data[3] <= r_data[3] + uart_rx;85,86,87,88,89,90,91: r_data[4] <= r_data[4] + uart_rx;101,102,103,104,105,106,107: r_data[5] <= r_data[5] + uart_rx;117,118,119,120,121,122,123: r_data[6] <= r_data[6] + uart_rx;133,134,135,136,137,138,139: r_data[7] <= r_data[7] + uart_rx;149,150,151,152,153,154,155: sto_data <= sto_data + uart_rx;default:;endcase reg rx_done;always@(posedge clk or negedge rstn)if(!rstn)rx_done <= 0;else if(bps_cnt == 159) beginrx_done <= 1;endelserx_done <= 0;//数据接收完成后赋值给data输出always@(posedge clk or negedge rstn)if(!rstn)data <= 0;else if(rx_done)begindata[0] <= (r_data[0] >= 4 ) ? 1 : 0; //可换种写法,写法如下data[1] <= (r_data[1] >= 4 ) ? 1 : 0;data[2] <= (r_data[2] >= 4 ) ? 1 : 0;data[3] <= (r_data[3] >= 4 ) ? 1 : 0;data[4] <= (r_data[4] >= 4 ) ? 1 : 0;data[5] <= (r_data[5] >= 4 ) ? 1 : 0;data[6] <= (r_data[6] >= 4 ) ? 1 : 0;data[7] <= (r_data[7] >= 4 ) ? 1 : 0;end// data[1] <= r_data[1][2]// 0:3'd000// 1:3'd001// 2:3'd010// 4:3'd100// 5:3'd101// 6:3'd110// 7:3'd111 利用第3位的区别给data赋值endmodule
仿真代码
`timescale 1ns/1nsmodule uart_byte_rx1_tb();reg clk;reg rstn;reg uart_rx;wire [2:0]blaud_set;wire [7:0]data;wire rx_done;uart_byte_rx1 uart_byte_rx_inst1(.clk(clk),.rstn(rstn),.blaud_set(blaud_set),.uart_rx(uart_rx),.data(data),.rx_done(rx_done));assign blaud_set = 3'd4;initial clk = 1;always #10 clk = ~clk;initial beginrstn = 0;uart_rx = 1;#201;rstn = 1;#200;uart_tx_byte(8'h5a);@(posedge rx_done)#5000;uart_tx_byte(8'ha5);@(posedge rx_done)#5000;uart_tx_byte(8'h86);@(posedge rx_done)#5000;$stop;endtask uart_tx_byte;input [7:0] tx_data;beginuart_rx = 1;#20;uart_rx = 0;#8680;uart_rx = tx_data[0];#8680;uart_rx = tx_data[1];#8680;uart_rx = tx_data[2];#8680;uart_rx = tx_data[3];#8680;uart_rx = tx_data[4];#8680;uart_rx = tx_data[5];#8680;uart_rx = tx_data[6];#8680;uart_rx = tx_data[7];endendtaskendmodule
仿真波形