串口接收模块uart_rx详解

 2023-09-05 阅读 90 评论 0

摘要:接收模块要比发送模块稍微复杂一丢丢,但有助于锻炼自己对于时序模块的逻辑性的掌握能力。 本文是学习了小梅哥的串口接收模块教程的总结整理。 1. 原理介绍 详见《串口发送模块uart_tx详解》 https://blog.csdn.net/m0_37921318/article/details/105913390 2. 串口接收

接收模块要比发送模块稍微复杂一丢丢,但有助于锻炼自己对于时序模块的逻辑性的掌握能力。

本文是学习了小梅哥的串口接收模块教程的总结整理。

1. 原理介绍

详见《串口发送模块uart_tx详解》 https://blog.csdn.net/m0_37921318/article/details/105913390

2. 串口接收数据时序分析

在这里插入图片描述在这里插入图片描述
进行对比便可以看出:发送模块可以在一个数据bit的任意位置采样输出,图3.11-1中选择了在bit时序开头处采样输出;而接收时序则不然,必须在1bit数据最稳定的中点位处采样,如图3.11-2,。

且因为工业应用现场常有较强的电磁干扰,只采样一次的数据不够可靠,需要多次采样求概率进行状态判断,如图3.11-3,采样6次取频率高的结果(0or1)进行输出
在这里插入图片描述
将每一位数据再平均分成了 16 小段。每个小段取样一次,对于 Bit_x 这一位数据,考虑到数据在刚刚发生变化和即将发生变化的这一时期,数据极有可能不稳定的(用深灰色标出的两段),在这两个时间段采集数据,很有可能得到错误的结果,因此判定这两段时间的电平无效,采集时直接忽略。而中间这一时间段(用浅灰色标出),数据本身是比较稳定的,一般都代表了正确的结果。对这一段电平,进行多次采样,并求高低电平发生的概率, 6 次采集结果中,取出现次数多的电平作为采样结果 。例如,采样 6 次的结果分别为 1/1/1/1/0/1/,则取电平结果为 1,若为 0/0/1/0/0/0,,则取电平结果为 0,当 6 次采样结果中 1 和0 各占一半(各 3 次),则可判断当前通信线路环境非常恶劣,数据不具有可靠性,不进行处理

3.模块设计

整体模块&接口

在这里插入图片描述

(1) 输入信号预处理 模块(同步化&边沿检测)

先看有没有异步输入信号。有,数据输入Rs232_Rx即为异步,因此先对他进行同步化,消除亚稳态。
然后是边沿检测。不再赘述

//同步寄存器,消除亚稳态always@(posedge Clk or negedge Rst_n)if(!Rst_n)begins0_Rs232_Rx <= 1'b0;s1_Rs232_Rx <= 1'b0;	endelse begins0_Rs232_Rx <= Rs232_Rx;s1_Rs232_Rx <= s0_Rs232_Rx;	end//数据寄存器always@(posedge Clk or negedge Rst_n)if(!Rst_n)begintmp0_Rs232_Rx <= 1'b0;tmp1_Rs232_Rx <= 1'b0;	endelse begintmp0_Rs232_Rx <= s1_Rs232_Rx;tmp1_Rs232_Rx <= tmp0_Rs232_Rx;	endassign nedge = !tmp0_Rs232_Rx & tmp1_Rs232_Rx;
(2) 波特率计数值设置 模块
	always@(posedge Clk or negedge Rst_n)if(!Rst_n)bps_DR <= 16'd324;else begincase(baud_set)0:bps_DR <= 16'd324;1:bps_DR <= 16'd162;2:bps_DR <= 16'd80;3:bps_DR <= 16'd53;4:bps_DR <= 16'd26;default:bps_DR <= 16'd324;	//默认为9600bps	endcaseend
(3)bps_clk gen模块
	//counteralways@(posedge Clk or negedge Rst_n)if(!Rst_n)div_cnt <= 16'd0;else if(uart_state)beginif(div_cnt == bps_DR)div_cnt <= 16'd0;elsediv_cnt <= div_cnt + 1'b1;endelsediv_cnt <= 16'd0;// bps_clk genalways@(posedge Clk or negedge Rst_n)if(!Rst_n)bps_clk <= 1'b0;else if(div_cnt == 16'd1)//在每个1/16小段的开头的时钟上升沿处采样(每个1/16小段含有bps_DR个clk周期,只采样第一个,其他忽略)bps_clk <= 1'b1;elsebps_clk <= 1'b0;
(4)bps_counter 模块
	//bps counteralways@(posedge Clk or negedge Rst_n)if(!Rst_n)	bps_cnt <= 8'd0;else if(bps_cnt == 8'd159 | (bps_cnt == 8'd12 && (START_BIT > 2)))
//当一个字节采样完成(10位*16次采样),bps_cnt计数到159时,计数值归零;
//或 **(bps_cnt == 8'd12 && (START_BIT > 2))**  时计数值归零,因为这意味着起始位发生数据紊乱,则整个字节数据作废bps_cnt <= 8'd0;else if(bps_clk)bps_cnt <= bps_cnt + 1'b1;elsebps_cnt <= bps_cnt;always@(posedge Clk or negedge Rst_n)if(!Rst_n)Rx_Done <= 1'b0;else if(bps_cnt == 8'd159)Rx_Done <= 1'b1;elseRx_Done <= 1'b0;
(5) 接收完成标志位Rx_Done 模块
output reg Rx_Done;
always@(posedge Clk or negedge Rst_n)if(!Rst_n)Rx_Done <= 1'b0;else if(bps_cnt == 8'd159)Rx_Done <= 1'b1;elseRx_Done <= 1'b0;
(6)uart_state 接收状态检测模块
	always@(posedge Clk or negedge Rst_n)if(!Rst_n)uart_state <= 1'b0;else if(nedge)uart_state <= 1'b1;else if(Rx_Done || (bps_cnt == 8'd12 && (START_BIT > 2)))uart_state <= 1'b0;elseuart_state <= uart_state;
(7) 数据判定&接收存储模块

重点

以图 3.11.3 起始位为例,位于中间的采样时间段对应的 bps_cnt 值分别为 6、7、 8、 9、 10、 11, 在这些时刻直接累加本位数据。然后,后一位数据的采样时间段的第一个 bps_cnt 值为前一位数据采样时间段的第一个 bps_cnt 值加 16。 所以,起始位后面紧跟的第一个数据位 Bit0 的采样时间段的 bps_cnt 值分别是 22、 23、24、 25、 26、 27,同样,在这些时刻,直接累加本位数据。以此类推,可以得到其他位的采样时间段对应的 bps_cnt 值。

现解释为何在上面清零条件之一为(bps_cnt == 8’d12 && (START_BIT > 2)),理想情况下(真正的起始位)也就是当 bps_cnt 计数值为 12 时, START_BIT 的计算值应该为 0。 而在实际中,有可能会出现一个干扰信号,而并非真正的起始信号,也导致下降沿的出现。此时,如果不加判断,直接视之为起始信号,进入接收状态,那么必然会接收到错误数据,也可能导致错过正确数据的接收。 为了增加抗干扰能力,这里采样了这样的一种思路:当数据线上出现了下降沿时, 先假设它是起始信号,然后对其进行中间段采样。如果这 6 次采样值累加结果大于 2,即 6 次采样中有至少一半的状态为高电平时,那么这显然不符合真正起始信号的持续低电平要求,此时就把刚才到来的下降沿视为干扰信号,而不视为起始信号。

/*串并转换,选择寄存模块
将串口接入并消除亚稳态后的数据s1_Rs232_Rx,从START_BIT到STOP_BIT共10个连续字,每个字分16个bps_clk来传输,其中的6-11位的0或1的累加和,转换为三位二进制数r_data_byte[x][2:0]
r_data_byte[x][2]就是稳定值
从低到高 逐位赋给r_data_byte[7:0][2:0]
*/
reg [2:0] r_data_byte[7:0];
always@(posedge Clk or negedge Rst_n)if(!Rst_n)beginSTART_BIT = 3'd0;r_data_byte[0] <= 3'd0;r_data_byte[1] <= 3'd0;r_data_byte[2] <= 3'd0;r_data_byte[3] <= 3'd0;r_data_byte[4] <= 3'd0;r_data_byte[5] <= 3'd0;r_data_byte[6] <= 3'd0;r_data_byte[7] <= 3'd0;STOP_BIT = 3'd0;endelse if(bps_clk)begincase(bps_cnt)//0~159的取值范围0:beginSTART_BIT = 3'd0;r_data_byte[0] <= 3'd0;r_data_byte[1] <= 3'd0;r_data_byte[2] <= 3'd0;r_data_byte[3] <= 3'd0;r_data_byte[4] <= 3'd0;r_data_byte[5] <= 3'd0;r_data_byte[6] <= 3'd0;r_data_byte[7] <= 3'd0;STOP_BIT = 3'd0;			end6,7,8,9,10,11:START_BIT <= START_BIT + s1_Rs232_Rx;22,23,24,25,26,27:r_data_byte[0] <= r_data_byte[0] + s1_Rs232_Rx;38,39,40,41,42,43:r_data_byte[1] <= r_data_byte[1] + s1_Rs232_Rx;54,55,56,57,58,59:r_data_byte[2] <= r_data_byte[2] + s1_Rs232_Rx;70,71,72,73,74,75:r_data_byte[3] <= r_data_byte[3] + s1_Rs232_Rx;86,87,88,89,90,91:r_data_byte[4] <= r_data_byte[4] + s1_Rs232_Rx;102,103,104,105,106,107:r_data_byte[5] <= r_data_byte[5] + s1_Rs232_Rx;118,119,120,121,122,123:r_data_byte[6] <= r_data_byte[6] + s1_Rs232_Rx;134,135,136,137,138,139:r_data_byte[7] <= r_data_byte[7] + s1_Rs232_Rx;150,151,152,153,154,155:STOP_BIT <= STOP_BIT + s1_Rs232_Rx;default:beginSTART_BIT = START_BIT;r_data_byte[0] <= r_data_byte[0];r_data_byte[1] <= r_data_byte[1];r_data_byte[2] <= r_data_byte[2];r_data_byte[3] <= r_data_byte[3];r_data_byte[4] <= r_data_byte[4];r_data_byte[5] <= r_data_byte[5];r_data_byte[6] <= r_data_byte[6];r_data_byte[7] <= r_data_byte[7];STOP_BIT <= STOP_BIT;						endendcaseend
(8). 数据状态判定模块(高位稳定值寄存)

在原理部分介绍过,对一位数据需进行 6 次采样,然后取出现次数较多的数据作为采样结果,也就是说, 6 次采样中出现次数多于 3 次的数据才能作为最终的有效数据。
对此, 可以用接收到数据 r_data_byte[n]结合数值比较器来判断,也可以直接令其等于当前位的最高位数据。 以下面例子说明:当 r_data_byte[n]分别为二进制的 011B/010B/100B/101B 时, 这几个数据十进制格式分别为 3d/2d/4d/5d,可以发现大于等 4d 的为 100B/101B。当最高位是 1 即此时的数据累加值大于等于 4d,可以说明数据真实值为 1;当最高位是 0 即此时的数据累加值小于等于 3d,可以说明数据真实值为 0,因此只需判断最高位即可

	reg [2:0] r_data_byte [7:0];reg [2:0] START_BIT,STOP_BIT;
always@(posedge Clk or negedge Rst_n)if(!Rst_n)data_byte <= 8'd0;else if(bps_cnt == 8'd159)begindata_byte[0] <= r_data_byte[0][2];data_byte[1] <= r_data_byte[1][2];data_byte[2] <= r_data_byte[2][2];data_byte[3] <= r_data_byte[3][2];data_byte[4] <= r_data_byte[4][2];data_byte[5] <= r_data_byte[5][2];data_byte[6] <= r_data_byte[6][2];data_byte[7] <= r_data_byte[7][2];end	

4.仿真

在板级调试中可以使用ISSP 中的探针功能( probe)进行功能验证
也可以将上一篇的uart_tx串口发送模块的输出Rs232_Tx输入到接收模块的Rs232_Rx端口上,对两个模块的端口设置写进一个testbench中。

仿真略。


wire Rs232_Tx;
uart_byte_rx uart_byte_rx(
.Clk(Clk),
.Rst_n(Rst_n),
.baud_set(baud_set),
.Rs232_Rx(Rs232_Tx),
.data_byte(data_byte_r),
.Rx_Done(Rx_Done)
);uart_byte_tx uart_byte_tx(
.Clk(Clk),
.Rst_n(Rst_n),
.data_byte(data_byte_t),
.send_en(send_en),
.baud_set(baud_set),
.Rs232_Tx(Rs232_Tx),
.Tx_Done(Tx_Done),
.uart_state(uart_state)
);

5.总结串口发送模块&串口接收模块的异同

1.发送模块不需要边沿检测,因为发送方是动作发出者,他不需要通过边沿检测来判断是否有信号传输,他本身是控制信号输出的一方;而接受模块需要,因为它是信号接收方,他需要判断信号线给他一个指令,这个指令就是START_BIT的nedge。
2.发送模块可以在一个数据bit的任意位置采样输出,因为它的数据是新鲜出炉的、是热乎的、稳定的(我目前是这样理解的),图3.11-1中选择了在bit时序开头处采样一次便可输出;而接收时序则不然,必须在1bit数据最稳定的中点位处多次采样进行状态判断,然后确定接受到的数据是0或1,而且就算这样也会出现错误的情况。
3.发送模块的采样分频时钟bps_clk的分频计数值是接收模块的1/16,接收模块要在1bit内均分成16份再进行多次采样,故而其bps_clk的采样频率为发送模块的16倍,计数值为其1/16。

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://808629.com/133.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 86后生记录生活 Inc. 保留所有权利。

底部版权信息