孩子都能学会的FPGA:第六课——用计数器实现UART协议的接收模块

(原创声明:该文是作者的原创,面向对象是FPGA入门者,后续会有进阶的高级教程。宗旨是让每个想做FPGA的人轻松入门作者不光让大家知其然,还要让大家知其所以然!每个工程作者都搭建了全自动化的仿真环境,只需要双击top_tb.bat文件就可以完成整个的仿真(前提是安装了modelsim),降低了初学者的门槛。如需整个工程请留言(微信Blue23Light),不收任何费用,但是仅供参考,不建议大家获得资料后从事一些商业活动!

有了第五课的uart发送模块,那uart接收模块相对就简单多了。还是需要两个计数器就可以实现,一个计数器用于一次uart传输位数的计数,一个计数器用于uart一位数据宽度的控制,就是波特率的设置。

uart接收模块的计数器和发送模块的计数器功能是完全相同的,那接收模块的设计要点在哪儿?我们知道uart是异步串行通信,就是只有数据线,没有同步时钟线。发送模块有数据就可以发送,那接收模块什么时候接收呢?这时候就能看出来uart起始位的作用了。我们知道,uart的数据线在空闲状态下是高电平,而起始位是低电平,所以FPGA应该时刻监控uart数据线上的状态,如果前一个时刻还是高电平,下一个时刻就是低电平了(暂时不考虑线上有干扰),说明有新的数据要发送过来了,做好接收数据的准备。

判断出来起始位,那后面的数据位,结束位就能顺理成章的能判断出来。每一位用100MHz的时钟可以计数868次,那我们在哪个点从线上取数据呢?发送端是在bandrate_cnt计数为0的时候把数据放到线上,实际生效是在bandrate_cnt计数为1的时候,持续到bandrate_cnt下次计数为0为止。对于接收端,理论来说在bandrate_cnt0计数到867的每一点都可以,但是bandrate_cnt的计数值在0附近表明线上的信号刚发生了变化,此时从线上取数据可能会取到错误的数据,所以一般在数据最稳定的地方取数据,即bandrate_cnt的计数值是433的时候。

如下所示,与uart发送模块类似,设置了一个接收标志寄存器recv_domain,当有新的数据要接收时拉高,当一次10位的传输完成后拉低;byte_cnt和bandrate_cnt计数器的功能和uart发送模块的完全一样,新增了一个标志信号get_data_point,在计数为433的时候拉高一个时钟周期,用于uart线上数据的采集。

知道了上面的两个设计要点,下面我们来分析一下uart_byte_rx模块的设计。首先输入信号clkrst_n是时钟和复位。rx_signaluart线上的信号,dout_vld是接收数据dout的有效指示信号。

增加了一个GET_DATA_CNT的参数,用于指示在波特位的中间位置采集数据,用wire类型的信号get_data_point来指示数据采集点。recv_domain来标志数据采集区域,所有控制信号在recv_domain拉高的区域变化。增加了rx_sync0/rx_sync1/rx_sync2这几个信号,作用有两个,一个对其它时钟域的信号rx_signal进行打拍同步,二是通过打拍实现对rx_signal信号的下降沿进行检测。信号rx_detect用于指示rx_signal信号的下降沿,即uart接收数据的开始。寄存器end_ok用来检测停止位是否正确。

首先对rx_signal信号进行打拍同步,因为rx_signal信号和本模块的clk一般不是同一个时钟域。我们都知道,uart一般都是对外的接口,所以FPGA收到的rx_signal这个信号是同其它芯片发过来的,和FPGA的主时钟clk不是同一个时钟域。这个地方的信号打拍就涉及到不同时钟域的信号同步问题,信号同步的方法后面会有课时专门讲述,这个地方先大体说一下打拍为什么管用。

用一个例子解释一下大家就明白了,假设两趟地铁在站点X可以进行换乘,已知如下的信息:

地铁A每7分钟一趟;

地铁B5分钟一趟;

同一站点换乘时间是2-5分钟(考虑人流的密度)。

假设你乘坐的地铁A到站的时间是7点3分,而最近的一辆地铁B1的发车时间是7点5分,下一辆地铁B2的发车时间是7点10分下一辆地铁B3的发车时间是7点15分,然后地铁B4的发车时间是7点20分,那就可以分析一下,你直接去赶地铁,有可能赶上地铁B1(人流量小的时候),也有可能赶不上地铁B1(人流量大的时候),但是不出意外的话一般可以赶上地铁B2。

但是你朋友是坐的地铁A是7点10分到站点X,他提前打电话让你等他一块去做地铁B,这样你和朋友一定赶不上地铁B2,不出意外可以赶上地铁B3,但是一定可以赶上B4。

所以信号的寄存器打拍就和地铁换乘一样,打拍越多,留给信号的处理时间裕量就越大,同步也就越好,但是花的时间也就越多。一般而言,打两到三拍能解决绝大多数的同步问题。rx_detect是采样rx_signal信号的下降沿,也就是上一个时刻是高电平,当前时刻是低电平,这就是下降沿。

接收标志寄存器recv_domain,有新的数据要接收时拉高,一次uart传输完成拉低,有这个信号的好处是其它的控制信号都在recv_domain高电平的时候变化,在recv_domain低电平的时候清零或者默认值即可。

下面是两个计数器的实现,与uart发送模块的计数器实现方式完全一样,只不过把send_domain改为了recv_domain。

然后就可以在期望的点(get_data_point)进行uart线上的数据的采集了,由于uart线上是先发的低位,再发的高位,所以这儿采用右移的方式来存储采集的数据,数据先存储在寄存器dout_tmp中。同理采集结束位的信号,保存在寄存器stop_ok中,用于线上数据的校验

最后完成数据的接收后,如果停止位检测正确,那就可以输出采集的uart线上数据dout,同时用dout_vld信号来标识dout信号的有效。

在仿真模块中,将uart_byte_rx模块和上节课的uart_byte_tx模块一起例化仿真。

由仿真结果可以看出来,发送端把8bit的信号转换成了单bit的串行信号,接收端把串行的单bit信号又转变成了8bit的信号,发送数据和接收数据一致,功能设计正确。

这节课实现了uart接收的基本功能,和上接课的模块一起就可以完成uart最基本的通讯功能。我们知道很多uart还需要奇偶校验位,那我们下节就进一步完善uart的通讯功能。

  • 8
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值