原文地址:?https://bbs.elecfans.com/jishu_2489801_1_1.html?
作者:@王钊
引言:FPGA开发,思路先行!
玩FPGA板子,读代码是基本功!尤其对从C语言转战FPGA的“宝贝们”来说,适应流水线(pipeline)编程可能需要点时间。
上篇点灯代码解读了基础,而如果能亲手写出串口通讯代码,恭喜你,FPGA的大门算是真正踏入了!
本文旨在帮初学者梳理FPGA开发的核心思维流程——思路对了,后面的路才顺畅,这可是重中之重!
一:感性认知 - 烧录 & 看效果
废话不多说,先烧程序,眼见为实!

在Ubuntu下,等程序烧录完成后,打开gtkterm,通过USB转串口设备节点与板子通讯。
本次烧录的实例实现的是“回显”(Echo):你键盘输入什么,板子就原样发回什么。
这种时候我们可能会以为出现了幻觉(这效果对吗?)
好办!在回显代码里“加个1”,把接收到的数据+1再发送
再次烧录以后,测试发现,按键输入123变成了234。果然不是幻觉,我们可以继续看代码了。
经验上讲以上步骤还是必要的,这证明代码是对的,以免被坑。
二:硬件连接 - 原理图 & 管脚对一对
回到代码,Top顶层模块走起!
输入脚有个25m的时钟,输出有个灯,这跟点灯程序一样。然后串口有收发两个脚。然后我去对一下原理图。
找到原理图第三页,底板的原理图,供电的Type-C口其实连到了CH340 USB转串口芯片(单片机玩家老朋友了!)
这个原理图其实看着有点晕,反正大致意思就是串口发送接收接到了H10和H11这两个脚上。
管脚配置瞧一瞧:外部时钟脚和led的脚跟之前点灯的程序一样,下面就是多了H10和H11两个脚,分别对于串口的RX和TX。
然后它们的电平是3.3V的。如果这里看着难受的话可以把它改到其他脚上,然后接一个自己的USB转串口的板子上,自己的串口板只需要多接个GND地线,无需接3.3V电源,加上RX和TX一共3根线,注意RX和TX可能需要反一下。
三:协议基础 - 磨刀不误砍柴工
到这里又要啰嗦一下,FPGA调一个接口,首先就是我们需要清楚的知道接口有个的数据定义,协议等知识。不能上来就研究代码,否则可能会迷糊。
(图片来源:https://zhuanlan.zhihu.com/p/689643287)
这里是我搜索出来知乎的帖子,讲串口协议的,有需要的话可以补充一下底层知识。
当然相信没几个人不知道串口的,但FPGA开发它还是蛮多套路的,比如接收一帧数据该如何接收。
大致的思路是这样的,我们需要用一个比串口波特率更高的采样信号去采集,串口的RX上什么时候出现起始位,然后接收每个数据位,最后延时一个停止位,再循环检测起始位,接收下一帧数据。
四:代码解析 - 接收模块的奥秘
然后代码我就不再讲倍频和reset逻辑了,点灯程序已经讲过了。
直接看接收的代码:这里看到模块的调用,可以想象成我们在板子上焊了一块芯片,它有一个clk脚rst_n脚;然后连到了top顶层的UART的RX上;收到数据后会返回一个接收完成的标志,8位的接收到的数据;最后还需要一个比波特率大的,这里是大16倍的采样时钟。
进入接收模块的代码里,我们看看芯片内部是怎样实现的。这里作者用ASCCII码画了一个时序图,这个太有用了,看代码的时候需要反复的看这个图。
简单说一下这个时序图,IDLE的时候是检测起始位的状态,起始位start是bit0,这里看着它是低电平的;然后到接收数据的状态,也就是bit1到bit8这8位;最后是end停止位,回到IDLE。
所以程序的思路就有了,我们需要在16倍波特率的采样周期上,不停的检测RX脚上的电平,完成串口通讯一帧数据的接收。
接着看代码,作者大神首先把RX脚做了个同步,这个套路不看代码是学不到的,久了看见这种代码脑袋里面会浮现出一个时序图,大概就能看到clk和rx信号的时序,然后理解到为什么要同步。
注意这个模块有两个时钟,一个是系统时钟,一个是16倍的波特率的时钟,它这同步的是系统时钟。咱们先别晕在这里,继续往下看。
后面的代码是个状态机,这代码还有点多,我抓屏一爪还抓不完。大家可以打开代码自己对着看,反正行号可以看出是讲到哪里了。状态机跟作者的时序图是一致的,就是那些IDLE,start,end之类的状态。
这里还有个特别玄乎的套路,本人也不是大神,所以也没看明白。就是这个状态机是用系统时钟来检测的,那个16倍波特率的采样时钟是在下面用个if来判断的,就是采样信号为高的时候去检测数据信号的高低。本宝认为,为什么不直接把采样信号放在always语句上面用呢。手贱的同学可以改一下试试,看看串口会不会丢数据,试完记得告诉我结论。
IDLE状态没什么看头,我们看看start状态。它这里有个采样的计数,一个bit采样16次。这里首先是从IDLE进入START状态需要rx管脚为低电平,并且在16次采样的中间那会不变成低电平,才认为起始信号有效。(为什么不判断全部为高?或者前半段为高?)
反正最重要的是数到16个采样就切到下一个状态,不能快也不能慢,保证时序要求。
这里还有一个套路是我们会看到那些a=a,b=b的语句,其实我感觉是可以删掉的,不知道是不是作者年级比较大,或许以前古时候的综合器不写else后面的东西,它会乱综合一些东西出来。好奇宝宝们可以写个测试程序,看看RTL电路有什么区别,同样,测试出结论以后记得告诉我……
采样状态的逻辑看了半天,也没真正做采样的事情,只是输出了一个变量rxd_cnt,这个变量表示采样的是第几个bit,还要注意的是这个变量在什么时候被锁存改变的,我们注意到是在采样计数为最后一个的时候锁存的。
停止状态啥也没干,只是保证延时一个波特率的时间而已。
接着看代码,上面采样状态输出了一个变量rxd_cnt,然后这里才进行真正的采样操作,它同时判断了采样计数器cmp_cnt,保证是在波形中部进行采样。可能这么做是增加鲁棒性。相信看到这里有些人会表示不服,我们可以把串口线接长一点,中间再加点电磁干扰,这样比较一下如果不在中间采样会不会导致丢数据的比率变高。同样,如果有人测试了,记得把结论告诉我!!!
最后一段代码是输出一帧率数据接收完成的标志,可以看到它的锁存逻辑是在停止位发送完毕的时候持续了一个采样时钟的高电平。同时锁存输出数据。
五:发送模块 - 相对简单
再往下就是看发送逻辑了,这块逻辑跟接收逻辑几乎一样。其实发送逻辑大可不必这么精细,因为接收才需要高频率的采样,发送只要保证时序就可以了。如果实在看不懂接收逻辑,我建议大家还是直接写一下发送逻辑,比如就按作者画的时序图,先用pll生成一个波特率的时钟,再按照时钟调整TX管脚的高低电平即可。最后用上面讲的接长串口线的方法测试一下数据传输的效率。
大半夜的不知道谁拍了我一下,我就突然看到这块代码有点不对……这个case里面的语句的等于符号前面居然没有小于符号了。这种情况奶奶没教过啊,而且它always语句里面是个*号。其实verilog我也是只学了三天,所以不太确定这是不是就是个组合逻辑电路。哎,这个迷哪位好心人回帖告诉我一声好不好。
万一有人整篇文章都看不懂,这里有个好玩的东西,test_io这个脚是接到灯上的,它这里的逻辑是TX和RX上只要有数据变化,灯就会闪烁,实测我一直往串口输入字符a,这个灯是可以看到闪烁的,闪的比较暗,大家可以关灯看看。
终极总结:FPGA接口开发心法
总结一下,关于接口的实现,无论接口多复杂,其实也是逻辑电平的控制,但前提是需要对协议非常的熟悉,再就是一下FPGA代码的套路了,这些套路都是一点一点看众大神的代码悟出来的。
-
FPGA
+关注
关注
1646文章
22069浏览量
619731 -
串口通信
+关注
关注
34文章
1641浏览量
56929 -
开发板
+关注
关注
25文章
5725浏览量
105607
发布评论请先 登录
【技术经典下载】《深入浅出玩转FPGA》-珍贵的学习经验和笔记
特权同学新书《勇敢的芯伴你玩转Altera FPGA》电子版 下载 (FPGA初学者首选)
赛灵思FPGA初学者 必备图书 特权同学新书《勇敢的芯伴你玩转赛灵思 FPGA》
MATLAB串口调试助手应用程序和基于MATLAB开发USB的串口通信源代码

单片机——串口通信(从串口接收多位数据保存到数组,发送多位数据到串口)

评论