从串口接收看逻辑的设计

in WhereIN3 years ago

从串口接收看逻辑的设计

最近研究了一些串口接收的逻辑设计代码,并从中总结了一下串口接收的设计方法,并总结了一些逻辑设计的体会。

串口通信是一种异步通信。最常用的通信格式就是“1起始位+8数据位+1停止位”。这也是最简单的情况。当然也有其它的格式,不过大同小异。我研究的代码也是以这种最简单的格式为例。

通常逻辑设计的最开始是架构设计。其实我感觉这是面向应用的。常用的逻辑模块大部分都是时序逻辑设计,而且只要是逻辑设计肯定需要系统时钟和复位对模块里的寄存器或状态机进行控制。所以clk和rst_n肯定得作为模块的输入控制。

然后串口肯定有一个用于串口接收的信号线,由于串口是接收逻辑,所以此信号对于模块而言就是输入。

而串口接收模块的一个作用就是将接收的数据解析出来并输出,此时输出的就是8位并行数据。而且此时可以附加一个信号来表示接收完成,比如rx_done。一般这个信号是与其它模块互联时使用的,表示一旦出现这个信号,对方就可以将数据读走了。当然,如果仅从模块的角度而言,也可以不使用这个信号。使不使用还是看是否需要它。
不过要是看到一个好的通用模型,还是得学习这种通用的写法。

其实串口接收还有一个重要的问题就是传输速率的问题。逻辑模块可以是固定的传输速率,比如9600bps。也可以是可选的。比如增加一组波特率选择的信号[2:0]baud_set,根据不同的输入值对应不同的波特率计数值。从电路的角度来说这就是一个多路选择器,但有意思的是有的老师设计成了时序电路,就是always(*)写成了always@(posedge clk or negedge rst_n)。不过,从功能的角度来说,这也没有问题,从电路来看不过是在多路选择器后面加了个触发器延迟了一拍。不过逻辑模块复位时是默认的9600bps的波特率,正常工作时会延迟一拍后切换到设定的值。

其实,复位后接收模块会对接收信号至少打3拍,甚至4拍。第一拍是同步,第二拍是消除亚稳态,第三拍是为了边沿检测。如果是打四拍的话,后两拍是为了边沿检测。如果这样看,即使多路复用器后面加了触发器也没什么影响。即使是发送完一个字节数据,在准备发送下一个字节数据时切换了波特率,也是同样的没有什么影响。

那边沿检测是怎么实现的呢?就是利用第二拍的输出和第三拍的输出差一个时钟周期来实现,比如检测下降沿,第二拍输出为低,第三拍输出则为高。那么定义下降沿nedge=~rx_r2&rx_r3,只有nedge等于1,表示出现了下降沿。

其实这有一个问题,为什么打一拍就会延迟一个时钟周期呢?其实就是触发器在采集数据时要求数据满足建立保持时间,即在时钟边沿数据应保持不变。然后触发器采到数据后,会经过Tco的时间到输出,这个时间很短,一般比保持时间Th还要小。所以一般触发器采样后就会很快(但不是立即)输出,并维持一个时钟周期的时间,就叫延迟。

也就是第二拍采集数据和第三拍采集数据是同时的,但是第二拍更新数据会有些许的延迟,导致第三拍采集的数据不是更新后的数据,而是更新前的数据。从而导致二者有一个时钟周期的延时。

如果接收模块的大致架构设计好了。接下来就是对模块的实现进行构思。假设以固定波特率9600bps传输数据,它指的是每秒传输9600位数据,那么每位数据传输需要消耗的时间是1/9600s,也就是104.167微秒。假设系统时钟周期是50MHz,也就是一个时钟周期20ns,如果用计数器来得到104.167us的时间,每个时钟周期计一个数,就需要计5208个数(从0到1算是计了一个数)。也就是计数器每计5208个数接收了一位数据。

所以,设计时步骤1,打三拍并产生下降沿信号nedge;步骤2,设计波特率计数器baud_cnt。一般波特率计数器复位后是0,直到检测到了下降沿,才开始计数。稍微仔细观察一下,下降沿信号边沿是与第二拍信号输出对齐的,如果检测到了下降沿就开始计数,计满最大值后,清零,再计数,……但这样的话计数值为1的时候,恰好与第三拍寄存器输出边沿对齐。那这样的话从位的开始计数值是1,记到5208也就是计了5207个数,然后5208计满直接变为1,表示下一位的开始。所以一位的波特率计数值是1~5208~1。但是还有一个问题就是波特率计数器等数据接收完成便不会再计数,什么时候接收完成,就是数据算上起始位,总共接收了9次,如果不对停止位接收,就表示接收完成了,波特率计数器就可以清0了。但需要注意的是,接收最后一位的波特率计数器不是1~5208~1,而是1~5208~0,是不是很奇妙?

所以波特率计数器设计中涉及了位计数器bit_cnt,复位时,这个值是0,每接收一位,这个值加1,直到加到最大值,再清0。但是这也涉及到什么时候表示接收到这一位呢?毕竟一位有5208个计数值这么长呢?换句话说就是我在这一位那个时刻表示我接收到了这一位?这一位的最开始?还是最后?呵呵,其实是最中间,因为最中间的数据被看成是最稳定的。

如果这样的话,在位的最中间进行采样,一般我们会这样说,如果波特率计数器达到多少多少,就开始采样,因为时序电路有一个时钟周期的延时,所以条件判断会提前一拍开始,所以如果波特率计数器的值为5208/2时,采样就可以。实际采样在2605处。采样其实就是将接收的数据存到寄存器[7:0]rx_data里,采用串并转换的思路rx_data<={rx_r3,rx_data[7:1]}。不过这种接收的方法是一位一位动态接收的。不过幸好有接收完成标志位rx_done。

所以这时就可以考虑bit_cnt的问题了。实际采样在2605处,那么bit_cnt就可以在2605处变为1。表示这时采到了一位数。那这样的话,起始位为计数值从0变为1,……采集最后一位有效数据时,计数值为8~0,那么波特率计数器清零的条件就有了,bit_cnt等于0且baud_cnt=5208。同时,如果清0了,必须等待下一个下降沿的出现才能继续计数。

那么关于rx_done信号,它得在最后一位采集完成后产生,也就是此时bit_cnt已经变为0,但波特率计数器为2605的时刻进行判定,rx_done信号的生成则在2606处。如果检测rx_done为高电平,那么就可以接收数据了。不过,这个接收模块假设是我写的,那么发送模块还得我写,别人写了有可能不能正常使用。因为rx_done我也可以在baud_cnt等于5208时判断,在下一个时钟输出。

接下来看一下逻辑是否可行,只要不产生自己的因是自己的果的问题就可以。开始,打三拍,产生下降沿,检测到下降沿波特率计数器计数,(分支1)计到一半,产生位计数,位计数和波特率计数器共同对波特率计数器清0,回到开始。(分支2)计到一半,对输入采样,存到输出寄存器。(分支3)根据波特率计数器和位计数器产生rx_done。

实际上可以加一些控制信号来帮我们更好的实现逻辑设计。比如接收有效标志位rx_flag,检测到下降沿后置位rx_flag,如果rx_flag为1,则波特率计数器计数,此时的计数值需要重新核定。

还可以产生采样标志,检测到采样标志后进行采样。增加这些信号有利于程序的阅读与分析,但说白了,这些不过是中间变量。

其实串口接收还可以更复杂。比如为了更好的抑制干扰,采用每位采样16次,取中间的6次进行累加,累加值大于3,则判定为高电平,累加值小于等于3,则判定为低电平。

这样每位的波特率计数器5208,就被分成了16份,每份约等于325,其实误差不会超过20ns,也就是一个时钟周期,完全可以接受。这时不应该叫波特率计数器,而应该叫采样波特率计数器。检测到有效接收标志后就开始计数,同时计数器的值达到一定值就产生采样标志,检测到采样标志就开始采样(个人倾向于中间采样)。采样值被送到采样寄存器里进行累加,同时采样计数器进行加1。

但是这里采样计数器是检测到采样标志后才进行加1,所以采样计数器有一个短时的0~1的变化,直到最大值160(每位16位,10位160位,考虑了停止位的采样,但是仅仅采样,并不加判断)后,会有一个短时(一个时钟周期)再变为0的过程。所以采样计数器在一个字节接收过程中,总是0~160~0。然后每位只对中间6次采样进行累加,如果大于3,为高电平,否则是低电平。如此只判断3位寄存器的最高位就可以了,如果是1则为高电平,否则是低电平。比如对起始位在采样计数器6,7,8,9,10,11处进行采样并累加,start_bit<=start_bit+rs232_rx;但接收完一个字节后,还要对寄存器的值清0。考虑过后,还是觉得在采样计数器为0时清0比较好。就是如果bps_cnt==0,那么start_bit<=3'd0。其它位也这样处理。要注意的是一个寄存器变量只能在一个进程里进行赋值。

当采样计数器计到最大值后,就会输出一个接收完成标志位。然后根据接收完成标志位,接收有效标志被清0,同时输出8位数据。其实按这个思路来说,接收有效标志并未持续到停止位结束。因为接收有效标志位被清0后,波特率计数器也被清0,但是最后一位有可能是在采样波特率计数器为1时刻判定,在采样波特率计数器为2时产生采样标志位,然后采样波特率计数器为3时进行采样(个人倾向于中间)并且会使采样计数器加1,此时采样计数器为最大值。然后输出接收完成标志位(采样波特率计数器为4),接收有效标志被清0(采样波特率计数器为5)。也就是最后一个采样波特率计数器没计满就会被清0。不过,停止位,不检查都可以,这点问题也就不是问题。关键看发送代码是不是和接收代码匹配。

除此,还可以加一个起始位错误检测,就是起始位的6次采样值如果大于2就认为是信号抖动,不是真正的起始位。从而使接收有效标志位和采样计数器清零。

所以这个过程中,对计数器的值进行控制来执行不同的操作,这种思想就叫做线性序列机思想。要灵活运用这种方法。

[WhereIn Android] (http://www.wherein.io)

Sort:  

看样子挺复杂的

[WhereIn Android] (http://www.wherein.io)

这算简单的,但注意点很多👍😂

[WhereIn Android] (http://www.wherein.io)

我日 这么长的论文

[WhereIn Android] (http://www.wherein.io)

😂😆

[WhereIn Android] (http://www.wherein.io)

👍🏻!shop

[WhereIn Android] (http://www.wherein.io)

谢谢😊

[WhereIn Android] (http://www.wherein.io)

你好鸭,lucienyong!

@boylikegirl给您叫了一份外卖!

巧克力蛋糕

吃饱了吗?跟我猜拳吧! 石头,剪刀,布~

如果您对我的服务满意,请不要吝啬您的点赞~

This post has been rewarded by the Steem Community Curation Project #wherein

昨天明明把手机放在桌上,今天又找不到了 ( ˘︹˘ )
咦?你是谁?我又是谁?
加入我们微信群没?还没的话赶快加我们瓜子老板。很帅的 iguozi <(^,^)>

Ha recibido un voto a favor de la comunidad WHEREIN, impulsada por STEEMIT INC. Gracias por usar WHEREIN

Terimakasih Sudah Menggunakan WhereIn, Postingan Anda Terpilih Untuk Mendapatkan Upvote Dari WhereIn Dan Di Dukung Oleh Steemit.inc !

厉害👍

[WhereIn Android] (http://www.wherein.io)

👍👍厉害了

[WhereIn Android] (http://www.wherein.io)

厉害了👍👍学废了😂

[WhereIn Android] (http://www.wherein.io)

😂要废了

[WhereIn Android] (http://www.wherein.io)

Coin Marketplace

STEEM 0.16
TRX 0.15
JST 0.029
BTC 55157.38
ETH 2317.75
USDT 1.00
SBD 2.33