串口通信--明德扬至简设计案例与应用FPGA

发布时间:2018年11月13日 10:11    发布者:luckyb1
1  项目背景 技术交流群 :544453837




        在使用教学板的串口前,需要安装CH340的驱动程序。下载后直接解压安装就可以了。

      当用USB线接上电脑和教学板,并且教学板上电后,打开电脑的“设备管理器”,将会看到如下显示。当出现该显示时,表示驱程安装成功并且已经被电脑正确地识别。

     我们可以从“设备管理器”中查看串口号,如下图所示。图中表示该串口号为XX。我们可以修改串口号,方法是:XXXXXX。



       串口:可以选择串口号,支持串口号1~4。如果连接的串口号不在此范围,则需要从设备管理器中修改串口号,详细见本章前面的描述。

       波特率:选择串口的波特率,支持9600、19200、38400、57600、115200。该选项影响了每一位码元占用的时间。

      校验位:可选择没有检验位、奇校验和偶校验。

     数据位:可以设置数据位的位数,可选择4~8位的数据拉。

      停止位:可以设置停止位的时间长度。有1位、1.5位和2位可供选择。

      打开/关闭串口:用来打开和关闭串口。打开软件时,串口默认是关闭状态。注意,一定要设置好参数后,才能打开串口;一定要关闭串口后,才能关掉教学板电源和拨掉USB线。

       十六进制显示:本软件支持ASCII显示和十六进制显示。勾选后,用十六进制显示。例如FPGA发送8’b00110001,本软件收到后会显示31。如果不勾选,则是用ASCII码显示。下图就是ASCII表。FPGA发送8’b00110001,即下表中十六进制的31,它所对应的图形为1,所以软件只会显示1。假设FPGA发送的是8’h00100011,即下表中十六进制的23,它所对应的是图形是#,所示软件会显示#。



       十六进制发送:本软件支持ASCII发送和十六进制发送。勾选后,用十六进制发送。例如填写“31”,按手动发送,那么FPGA收到的值是8’b00110001。如果不勾选,则是用ASCII码发送。例如填写“31”,按下手动发送,软件将首先发送ASCII码“3”所对应的十六进制值8’h33,再发送ASCII码“1”所对应的十六进制值8’h31。也就是FPGA将收到两个字节数据:8’h33和8’h31。


2  设计目标

上板效果图如下图所示



3 设计实现3.1 顶层信号



      综上所述,我们这个工程需要4个信号,时钟clk,复位rst_n,串口输入信号rx_uart和输出控制LED灯的8位信号led。

      将module的名称定义为uart。并且我们已经知道该模块有四个信号:clk、rst_n、rx_uart和dout。为此,代码如下:



      其中clk、rst_n和rx_uart是输入信号,dout是输出信号,其中clk、rst_n、rx_uart的值是0或者1,一根线即可,dout为8位位宽的,根据这些信息,我们补充输入输出端口定义。代码如下:



3.2 信号设计

      我们先分析要实现的功能,led信号控制了8个LED灯的亮灭,而具体哪些灯亮哪些灯灭,是取决于串口过来的数据。那串口过来的数据,是如何告知FPGA,并与led对应起来呢?我们可以看一下串口过来的时序。



       上面波形是CH340控制的信号rx_uart。如果CH340要发送一个8位数据data,它首先会将信号rx_uart变0并持续一段时间(起启位),然后发送data[0]并持续一段时间,然后发送data[1]并持续一段时间,以此类推,发送data[7]并持续一段时间,最后CH340将rx_uart为1并持续一段时间(结束位)。这样CH340就完成了数据的发送。

       例如,CH340要发送的数据data=8’h00110001,则rx_uart的波形如下。



      再考虑一下时间信息。由于波特率是9600,那么每位持续的时间是1s/9600=104166ns。将时间信息补上波形。



       本开发板的晶振时钟是50Mz,104166ns/20ns 约等于5208个时钟周期。也就是说上面波形中,每个比特的持续时间约等于5208个时钟周期。需要注意的是,5208只是一个估计的大概数字,实际情况会有偏差。同时,我们还有计数这是第几个比特,用于让我们判断是开始位、数据位和停止位等。



      可以看出,我们需要2个计数器,1个计数器用于计算1比特的位宽长度:5208个时钟周期,命名为cnt0;另一个用于计算有多少个比特,命名为cnt1。

      很明显cnt0每次都是数5208个,但cnt0的加1条件需要仔细分析。我们很清楚cnt0的加1区域是下面的灰度地方。




      目前没有任何信号可以区分出此区域。参考至简设计法案例2的方法,设计一个信号flag_add,当其为1表示上述灰度区域,即cnt0的加1区域。



       有了flag_add,我们就很明确,cnt0的加1条件是flag_add==1,数到5208下就结束。为此,可以写出cnt0的代码。








      中间信号,trigger连到触发器的信号输入端D,触发器的输出器连的是tri_ff0。将trigger取反,与tri_ff0相与,就得到信号neg_edge,如果neg_edge=1就表示检测到trigger的下降沿。将tri_ff0取反,与trigger相与,就得到信号pos_edge,如果pos_edge=1,就表示检测到trigger的上升沿。

      我们来讲解这个原理,画出信号的波形图。



      Tri_ff0是触发器的输出,因此tri_ff0的信号与trigger信号相似,只是相差一个时钟周期。我们也可以这样理解:每个时钟上升沿看到的tri_ff0的值,其实就是triffer信号上一个时钟看到的值,也就是tri_ff0是trigg





3.2.2  异步信号同步化










      这样,flag_add变1的条件就变成:rx_uart_ff1==0&& rx_uart_ff2==1。

      Flag_add变0的条件,可以完成收完9比特数据就变0,不用再计数了。所以变0条件:end_cnt1。

      综上所述,可以写出flag_add的代码。



      设计下data信号,该信号的值来自于图中第2~第9比特的值。第2比特的值赋给data[0],第3比特的值赋给data[1],以此类推,第9比特的值赋给data[7]。



      由于每一个比特都持续5208个时钟周期,我们必须选定一个时刻,将值赋给data。



      首先,不能在end_cnt0的时候赋值,如上图的点。因为我们这里的5208个时钟周期是理想、估算的数值,实际上是非常有可能有偏差的。如果我们在end_cnt0的时候取值,就有可能采错。

      最保险的做法是在中间点取值。这样,即使有比较多的偏差,都不会影响到采样的正确性。



      综上所述,我们在cnt0数到一半时采到当前rx_uart的值赋给dout,其中第2比特赋给led[0],第3比特赋给led[1],以此类推,第9比特赋给led[7]。

      进一步用信号表示,可翻译成:数到add_cnt0 && cnt0==5208/2 -1时,如果cnt1==1,则将rx_uart_ff1赋给led[0]。如果cnt1==2,则将rx_uart_ff1赋给led[1],以此类推,如果cnt1==8,将rx_uart_ff1赋给led[7]。

      那么直接翻译成代码。




      上面代码可优化,简写成如下:



      通常我们设计时,首先是想到实现功能,所以会先写出前面代码。在功能实现的前提下,再考虑有没有优化空间,从而写出后面代码。好代码都是一步步优化出来的。

      注意,上面代码,我们采集的是rx_uart_ff1而不是rx_uart信号。这是因为rx_uart是异步信号,我们只能用同步化后的信号,否则会引起亚稳态。所以只能是rx_uart_ff1。

      至此,主体程序已经完成。接下来是将module补充完整。


3.3  信号定义

      cnt0是用always产生的信号,因此类型为reg。cnt0计数的最大值为5208,需要用13根线表示,即位宽是13位。因此代码如下:



      add_cnt0和end_cnt0都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1个线表示即可。因此代码如下:



      cnt1是用always产生的信号,因此类型为reg。cnt1计数的最大值为9,需要用4根线表示,即位宽是4位。因此代码如下:



      add_cnt1和end_cnt1都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1根线表示即可。因此代码如下:



      flag_add是用always方式设计的,因此类型为reg。并且其值是0或者1,1根线表示即可。因此代码如下:



      rx_uart_ff0、rx_uart_ff1和rx_uart_ff2是用always方式设计的,因此类型为reg。并且其值是0或1,需要1根线表示即可。因此代码如下:



4     综合工程和上板4.1  新建工程

      1.首先在d盘中创建名为“uart”的工程文件夹,将写的代码命名为“uart.v”,顶层模块名为“uart”。




      2.    然后打开Quartus ,点击File下拉列表中的New Project Wzard...新建工程选项。



      3.在出现的界面中直接点击最下方的“Next”。



      4.之后出现的是工程文件夹、工程名、顶层模块名设置界面。按照之前的命名进行填写,第一栏选择工程文件夹“uart”,第二栏选择工程文件“uart.v”,最后一栏选择顶层模块名“uart”,然后点击”Next”,再出现的界面选择empty project。





      6.     器件型号选择界面。在“Device family”处选择Cyclone E,在“Available devices”处选择EP4CE15F23C8,然后点击“Next”。​



      7.   EDA工具界面。该页面用默认的就行,直接点击最下方“Next”。



      8.之后出现的界面是我们前面的设置的总结,确认没有错误后点击“Finish”



4.2 综合

      1.新建工程步骤完成后,就会出现以下界面。在“Project Navigator”下选中要编译的文件,点击上方工具栏中“Start Compilation”编译按钮(蓝色三角形)



      2.编译成功后会出现以下界面,点击“OK”。



​4.3   配置管脚



​      在菜单栏中,选中Assignments,然后选择Pin Planner,就会弹出配置管脚的窗口。



​      在配置窗口最下方中的location一列,参考下表中最右两列配置好FPGA管脚。



       配置完成后,关闭Pin Planner,软件自动会保存管脚配置信息。

4.4 再次综合



      在菜单栏中,选中Processing,然后选择Start Compilation,再次对整个工程进行编译和综合。



​      出现上面的界面,就说明编译综合成功。


4.5  连接开发板

      图中,下载器接入电脑USB接口,电源接入电源,uart线连接电脑USB,然后摁下电源开关,看到开发板灯亮。

4.6  上板

      1.双击Tasks一栏中”Program Device”



       2.会出现如下界面,点击add file添加.sof文件,在右侧点击“Start”,会在上方的“Progress”处显示进度。



      3.进度条中提示成功后,即可在显示器上观察到相应的现象。

4.7 串口调试

      1,安装串口调试工具。



       2. 开发板连接完成,打开电源后,在设备管理器中查看串口名。填写图片摘要(选填)




      3,打开串口调试助手,在串口处选择之前查看的串口名,波特率、校验位、数据位、停止位根据之前给出的数据进行填写,发送和接收都选择十六进制。



      4,上板成功之后,在发送数据栏输入相应的数据(将8个LED灯对应的8位二进制数转化为十六进制),然后发送,即可在开发板上看到相应的现象。



欢迎分享本文,转载请保留出处:http://www.eechina.com/thread-549927-1-1.html     【打印本页】
您需要登录后才可以发表评论 登录 | 立即注册

厂商推荐

关于我们  -  服务条款  -  使用指南  -  站点地图  -  友情链接  -  联系我们
电子工程网 © 版权所有   京ICP备16069177号 | 京公网安备11010502021702
回顶部