精华内容
下载资源
问答
  • FPGA状态机在多路异步串口通信处理的实时优势
  • 基于有限状态机的高速串口通信收发器的FPGA设计
  • 组合逻辑部分又可分为状态译码器和输出译码器,状态译码器确定状态机的下一个状态,即确定状态机的激励方程,输出译码器确定状态机的输出,即确定状态机的输出方程。寄存器用于存储状态机的内部状态。 状态机的基本...
  • FPGA串口通信

    2021-07-20 15:38:40
    本文主要讲解如何编写 FPGA 串口通信的收发程序,在程序中使用了状态机,是学习状态机的重要实验。 2 实验环境 黑金 FPGA 开发板(AX301 开发板、AX4010 开发板、AX515 开发板、AX530 开发板、AX1025 开发板) 串口...

    1 文档简介
    本文主要讲解如何编写 FPGA 串口通信的收发程序,在程序中使用了状态机,是学习状态机的重要实验。
    2 实验环境
    黑金 FPGA 开发板(AX301 开发板、AX4010 开发板、AX515 开发板、AX530 开发板、AX1025 开发板)
    串口调试助手
    3 实验原理
    3.1 串口通信简介
    本文所述的串口指异步串行通信,异步串行是指 UART(Universal Asynchronous
    Receiver/Transmitter),通用异步接收/发送。UART 是一个并行输入成为串行输出的芯片,通常集成在主板上。UART 包含 TTL 电平的串口和 RS232 电平的串口。 TTL 电平是 3.3V 的,而 RS232 是负逻辑电平,它定义+5~+12V 为低电平,而-12~-5V 为高电平,MDS2710、MDS SD4、EL805 等是RS232 接口,EL806 有 TTL 接口。

    串行接口按电气标准及协议来分包括 RS-232-C、RS-422、RS485 等。RS-232-C、RS-422 与 RS-485 标准只对接口的电气特性做出规定,不涉及接插件、电缆或协议。黑金 FPGA 开发板的串口通信通过 USB 转串口方式,主要是解决很多人电脑不带串口接口的问题,所以这里不涉及到电气协议标准,用法和 TTL 电平串口类似。FPGA 芯片使用 2 个 IO 口和 USB转串口芯片 CP2102 相连。在这里插入图片描述
    3.2 异步串口通信协议
    消息帧从一个低位起始位开始,后面是 7 个或 8 个数据位,一个可用的奇偶位和一个或几个高位停止位。接收器发现开始位时它就知道数据准备发送,并尝试与发送器时钟频率同步。如果选择了奇偶校验,UART 就在数据位后面加上奇偶位。奇偶位可用来帮助错误校验。在接收过程中,UART 从消息帧中去掉起始位和结束位,对进来的字节进行奇偶校验,并将数据字节从串行转换成并行。UART 传输时序如下图所示:在这里插入图片描述
    从波形上可以看出起始位是低电平,停止位和空闲位都是高电平,也就是说没有数据传输时是高电平,利用这个特点我们可以准确接收数据,当一个下降沿事件发生时,我们认为将进行一次数据传输。

    3.3 关于波特率
    常见的串口通信波特率有 2400 、9600、115200 等,发送和接收波特率必须保持一致才能正确通信。波特率是指 1 秒最大传输的数据位数,包括起始位、数据位、校验位、停止位。假如通信波特率设定为 9600,那么一个数据位的时间长度是 1/9600 秒。
    4 程序设计
    4.1 接收模块设计
    串口接收模块是个参数化可配置模块,参数“CLK_FRE”定义接收模块的系统时钟频率,单位是 Mhz,参数“BAUD_RATE”是波特率。接收状态机状态转换图如下:在这里插入图片描述
    “S_IDLE”状态为空闲状态,上电后进入“S_IDLE”,如果信号“rx_pin”有下降沿,我们认为是串口的起始位,进入状态“S_START”,等一个 BIT 时间起始位结束后进入数据位接收状态
    “S_REC_BYTE”,本实验中数据位设计是 8 位,接收完成以后进入“S_STOP”状态,在“S_STOP”没有等待一个 BIT 周期,只等待了半个 BIT 时间,这是因为如果等待了一个周期,有可能会错过下一个数据的起始位判断,最后进入“S_DATA”状态,将接收到的数据送到其他模块。在这个模块我们提一点:为了满足采样定理,在接受数据时每个数据都在波特率计数器的时间中点进行采样,以避免数据出错的情况:

    always@(posedge clk or negedge rst_n)
    begin
    if(rst_n == 1'b0)
    rx_bits <= 8'd0;
    else if(state == S_REC_BYTE && cycle_cnt == CYCLE/2 - 1)
    rx_bits[bit_cnt] <= rx_pin;
    else
    rx_bits <= rx_bits; 
    end
    

    注意:本实验没有设计奇偶校验位。
    在这里插入图片描述
    4.2 发送模块设计
    发送模式设计和接收模块相似,也是使用状态机,状态转换图如下在这里插入图片描述
    上电后进入“S_IDLE”空闲状态,如果有发送请求,进入发送起始位状态“S_START”,起始位发送完成后进入发送数据位状“S_SEND_BYTE”,数据位发送完成后进入发送停止位状态“S_STOP”,停止位发送完成后又进入空闲状态。在数据发送模块中,从顶层模块写入的数据直接传递给寄存器‘tx_reg’,并通过‘tx_reg’寄存器模拟串口传输协议在状态机的条件转换下进行数据传送:

    always@(posedge clk or negedge rst_n)
    begin
    if(rst_n == 1'b0)
    tx_reg <= 1'b1;
    else
    case(state)
     S_IDLE,S_STOP:
    tx_reg <= 1'b1; 
    S_START:
    tx_reg <= 1'b0; 
    S_SEND_BYTE:
    tx_reg <= tx_data_latch[bit_cnt];
    default:
    tx_reg <= 1'b1; 
    endcase
    end
    

    在这里插入图片描述
    4.3 测试程序
    测试程序设计 FPGA 为 1 秒向串口发送一次“HELLO ALINX\r\n”,不发送期间,如果接受到串口数据,直接把接收到的数据送到发送模块再返回。“\r\n”,在这里和 C 语言中表示一致,都是回车换行测试程序分别例化了发送模块和接收模块,同时将参数传递进去,波特率设置为 115200。

    uart_rx# ( .CLK_FRE(CLK_FRE),
    .BAUD_RATE(115200) ) uart_rx_inst
    ( .clk (clk ),
    .rst_n (rst_n ),
    .rx_data (rx_data ),
    .rx_data_valid (rx_data_valid ),
    .rx_data_ready (rx_data_ready ),
    .rx_pin (uart_rx )
    );
    uart_tx# ( .CLK_FRE(CLK_FRE),
    .BAUD_RATE(115200) ) uart_tx_inst
    ( .clk (clk ),
    .rst_n (rst_n ),
    .tx_data (tx_data ),
    .tx_data_valid (tx_data_valid ),
    .tx_data_ready (tx_data_ready ),
    .tx_pin (uart_tx )
    );
    

    5 实验测试
    由于开发板的串口使用 USB 转串口芯片,首先要安装串口驱动程序,正确安装驱动状态如下图所示(当然要连接串口的 USB 到电脑,AX301、AX4010 开发板串口和供电接口为同一个 USB 口,就是 J6 口)。如果没有正确连接请参考本文附录“串口驱动的安装在这里插入图片描述
    串口驱动正常的状态
    从图中可以看出系统给串口分配的串口号是“COM3”,串口号的分配是系统完成的,自动分配情况下每台电脑可能会有差异,笔者这里是“COM3”,使用串口号时要根据自己的分配情况选择。打开串口调试,端口选择“COM3”(根据自己情况选择),波特率设置 115200,检验位选None,数据位选 8,停止位选 1,然后点击“打开串口”。如果找不到这个小软件使用 windows 搜索功能,在黑金给的资料文件夹里搜索“串口调试”。

    打开串口以后,每秒可收到“HELLO ALINX”,在发送区输入框输入要发送的文字,点击“手
    动发送”,可以看到接收到自己发送的字符。在这里插入图片描述

    展开全文
  • EDA课设 FPGA开发板 VHDL实现串口通信

    千次阅读 多人点赞 2021-07-20 21:28:14
    VHDL UART串口通信设计 实现开发板与计算机串口助手之间的收发,并能够自行调节波特率。自行设计通信格式并完成调试。

    一、设计概述

    1. UART串口通信设计目的

    随着 FPGA/CPLD 器件在控制领域的广泛使用,开发嵌于 FPGA/CPLD 器件内部的通用异步收发器,以实现 FPGA/CPLD 开发系统与 PC 机之间的数据通信是很有实际意义的。FPGA/CPLD与单片机、ARM等器件不同,它内部并没有集成UART,因此要实现串行通信必须要独立开发UART模块。

    通用异步收发器(Universal Asynchronous Receiver/Transmitter, 简称为UART)由于具有数据通信时需要的连线少,具有相关工业标准提供的标准的接口电平规范等优点,在工业控制领域被广泛采用。近年来,FPGA器件逐渐开始广泛使用,开发基于 FPGA/CPLD 器件内部的通用异步收发器,以实现 FPGA/CPLD 开发系统与 PC 机或其它外设之间的数据通信引起了 FPGA/CPLD 领域研究人员的关注。为此,本设计基于 FPGA 器件设计了符合 RS232 标准的UART,实现开发板与计算机串口助手之间的收发,并能够自行调节波特率。

    2. UART串口通信设计目的

    通用异步收发传输器(Universal Asynchronous Receiver/Transmitter,UART)是一种异步收发传输器,其在数据发送时将并行数据转换成串行数据来传输,在数据接收时将接收到的串行数据转换成并行数据,可以实现全双工传输和接收。它包括了 RS232、RS449、RS423、RS422 和 RS485 等接口标准规范和总线标准规范。换句话说,UART是异步串行通信的总称。而 RS232、RS449、RS423、RS422 和 RS485 等,是对应各种异步串行通信口的接口标准和总线标准,它们规定了通信口的电气特性、传输速率、连接特性和接口的机械特性等内容。RS-232是美国电子工业联盟(EIA)制定的串行数据通信的接口标准,原始编号全称是 EIA-RS-232(简称232,RS-232),被广泛用于计算机串行接口外设连接。其 DB9 接口的针脚定义如下图所示。

    引脚功能如下表所示。若系统存在多个 UART 接口,则可分别称为 COM1、COM2 等。

    UART通信使用前需要做多项设置,常见的设置包括数据位数、波特率大小、奇偶校验类型和停止位数。数据位(Data bits):该参数定义单个UART数据传输在开始到停止期间发送的数据位数。可选择为:5、6、7或者8(默认)。波特率(Baud):是指从一设备发送到另一设备的波特率,即每秒钟可以通信的数据比特个数。典型的波特率有4800、9600、19200、38400、57600、115200等。一般通信两端设备都要设为相同的波特率,但有些设备也可设置为自动检测波特率。

    奇偶校验类型(Parity Type):是用来验证数据的正确性。奇偶校验一般不使用,如果使用,则既可以做奇校验(Odd)也可以做偶校验(Even)。在偶校验中,因为奇偶校验位会被相应的置1或0(一般是最高位或最低位),所以数据会被改变以使得所有传送的数位(含字符的各数位和校验位)中“1”的个数为偶数;在奇校验中,所有传送的数位(含字符的各数位和校验位)中“1”的个数为奇数。奇偶校验可以用于接受方检查传输是否发送生错误,如果某一字节中“1”的个数发生了错误,那么这个字节在传输中一定有错误发生。如果奇偶校验是正确的,那么要么没有发生错误,要么发生了偶数个的错误。如果用户选择数据长度为8位,则因为没有多余的比特可被用来作为奇偶校验位,因此就叫做“无奇偶校验”。

    停止位(Stop bits):在每个字节的数据位发送完成之后,发送停止位,来标志着一次数据传输完成,同时用来帮助接收信号方硬件重同步。可选择为:1(默认)、1.5或者2位。在 RS-232 标准中,最常用的配置是8N1(即八个数据位、无奇偶校验、一个停止位),其发送一个字节时序图如下图所示。

    空闲状态为一个高电平,按照一个完整的字节包括一位起始位、8位数据位、一位停止位即总共十位数据来算,要想完整地实现这十位数据的发送,就需要 11 个波特率时钟脉冲,第 1 个高电平脉冲标记一次传输的开始,第 11 个高电平脉冲标记一次传输的结束。

    二、顶层设计

    在 FPGA 内部实现串口接收与串口发送模块,串口接收模块接收上位机发送的数据,然后通过串口发送模块将数据发回上位机,实现串口数据环回。系统框图如下图所示:

    如上图所示,为串口通信的顶层设计原理图。

    三、子模块设计

    1. 波特率时钟生成模块

    本模块的设计是为了保证模块的复用性。当需要不同的波特率时,只需设置不同的波特率时钟计数器的计数初值,使用查找表即可实现。下面的设计代码中包含了针对 7 个波特率的设置,如需要其他波特率可根据实际使用情况自行调节。如下图所示。

    波特率生成,用一个定时器来定时,产生频率与对应波特率时钟频率相同的时钟信号,如下图所示。例如,我们使用波特率为115200bps,则我们需要产生一个频率为115200Hz的时钟信号。这里,我们首先将 115200Hz 时钟信号的周期计算出来,1秒钟为1000000000ns,因此波特率时钟的周期Tb=1000000000/115200=8680.6ns,即 115200Hz 时钟信号的一个周期为8680.6ns,那么,我们只需要设定我们的定时器定时时间为8680.6ns,每当定时时间到,产生一个系统时钟周期长度的高电平脉冲信号即可。系统时钟频率为50MHz,即周期为20ns,那么,我们只需要计数 8680/20 个系统时钟,就可获得 8680ns 的定时,bps115200=Tb×Fclk-1=Fclk115200-1。相应的,其它波特率定时值的计算与此相同。为了实现可以自行调节波特率,设计中使用了一个 3 位的波特率选择端口:Baud_Set。通过给此端口不同的值,就能选择不同的波特率,此端口控制不同波特率的原理很简单,就是一个多路选择器,多路选择器通过选择不同的定时器计数最大值来设置不同的比特率时钟频率。Baud_Set的值与各波特率的对应关系如下表所示。

    2. 发送模块

    串口发送模块的整体框图,如下图所示。

    其接口列表如下表所示:

    从原理部分已知,波特率是 UART 通信中需要设置的参数之一。在波特率时钟生成模块中,系统时钟周期为System_clk_period,这里为20ns。如果接入到该模块的时钟频率为其他值,需要根据具体的频率值修改该参数。

    3. 数据输出模块

    通过对波特率时钟进行计数,来确定数据发送的循环状态。为了使得模块可以对其他模块进行控制或者调用,这里产生一个 byte 传送结束的信号。一个数据位传输结束后 done 信号输出一个时钟的高电平,如下图所示。

    产生数据传输状态信号,如上图所示。即当在正常传输的时候 work_en 信号为高电平,其他情况均为低电平。

    4. 数据传输状态控制模块

    在模块结构图中还有一个多路选择器,作用是根据 bps_cnt 的值来确定数据传输的状态。不同的波特率时钟计数值时,有不同的传输数据对应。

    5. 接收模块

    串口接收模块与串口发送模块设计思路类似。串口接收模块整体框图,如下图所示。



    如上图所示,为接收模块的关键的 VDHL 代码。

    四、系统测试

    1. 仿真结果与分析

    对该发送模块进行功能仿真。在仿真文件中,生成了复位信号以及使能信号、待传输数据。这里将所有数据变化与系统时钟错开1ns,是为了能更清楚看到输入输出数据与时钟的时序关系。

    设置好仿真脚本后用ModelSim进行功能仿真,得到如图所示的波形文件,可以看出在复位信号为高电平以及使能信号有效之前输出信号Rs232_Tx均为 0,在复位结束以及使能后输出信号才开始正常,待发送数据设置为8’haa(MSB),且当开始发送后,输出信号依次为 1、0(起始位)、 8’haa(LSB)、1(停止位);当输入数据为01010101b(MSB)后,输出信号依次为 1、0(起始位)、10101010b(LSB)、1(停止位)。同时uart_state处于发送状态时为1,即仿真验证结果正确,如下图所示。

    对接收模块进行功能仿真,模块输出的Tx连接到接收模块上的Rx,作为接收模块的激励。

    设置好仿真脚本后运行 ModelSim 进行功能仿真,得到如下图所示的波形文件。分析可知:每当一个字节发送结束后,数据输出data_byte_r均会更新输出一次,符合设计预期。

    2. 管脚安排

    我们选用 Cyclone IV E 系列的 EP4CE6E22C8 芯片,最终的管脚安排 Pin Planner 如下图所示。

    3. 下载测试

    通过 USB 线缆连接 FPGA 开发板和 PC 机, Hardware Setup设置为USB-Blaster [USB-0],下载 sof 文件到 FPGA 开发板中。如下图所示。

    在串口助手中选择与开发板相连接的串口端口,设置波特率为9600,数据位为8,停止位为1,无校验位。通过点击“打开”按钮来打开串口。

    如下图所示。串口打开后,在发送文本框中输入数据 abc123 并点击发送,可以看到串口助手中接收到了数据。在接收设置为 ASCII 时,显示abc123;在接收设置为 Hex(十六进制)时,显示61 62 63 31 32 33。分析可知接收到的数据与发送的数据一致,设计验证正确。

    在波特率设置为 9600 时,串口调试助手中设置波特率为19200,这时发送数据,接收到的数据会有乱码。这时我们修改顶层设计的 baud_set 为001,此时对应波特率为19200,重新全编译整个项目,再下载 sof 文件到 FPGA 开发板,打开串口调试助手调试,测试结果如上图所示。分析可知,本设计实现了开发板与计算机串口助手之间数据的收发,通信格式正确,并且能自行调节波特率,符合设计预期,并且验证正确。

    设计报告+源码+资料:https://download.csdn.net/download/fyfugoyfa/20691109
    

    六、参考资料

    • 黄沛昱.EDA技术与VHDL设计实验指导.西安电子科技大学出版社,2012,08.
    • 杨英强.一种基于FPGA的UART电路实现[J].现代电子技术,2005,28(12):82-84.
    • 郭勇.EDA技术基础(第2版)[M].机械工业出版社,2005.3.14—320.
    • 褚振勇.FPGA设计及应用[M].西安:西安电子科技大学出版社.
    展开全文
  • FPGA专题——串口通信

    2020-05-03 05:19:48
    最近在利用空闲时间学FPGA,学到串口通信这里了,跟着例程做了个串口收发的实验,FPGA型号EP4CE6E22C8,FPGA接收PC发来的数据然后原样返回给PC,通过这个实验加深了对串口232通信协议的认识,同时在实验中发现了一些...

    目录

    一、前言

    二、串口RS232协议介绍

    三、程序设计

    四、实验分析

    五、总结

    六、参考资料


    一、前言

    最近在利用空闲时间学FPGA,学到串口通信这里了,跟着例程做了个串口收发的实验,FPGA型号EP4CE6E22C8,FPGA接收PC发来的数据然后原样返回给PC,通过这个实验加深了对串口232通信协议的认识,同时在实验中发现了一些关于串口模块的问题,在这里进行记录。

     

    二、串口RS232协议介绍

    1、协议介绍

    UART作为异步串口通信协议的一种,工作原理是将传输数据的每个字符一位接一位地传输。其中每一位(Bit)的意义如下:

    起始位:先发出一个逻辑”0”的信号,表示传输字符的开始。

    数据位:紧接着起始位之后。数据位的个数可以是4、5、6、7、8等,构成一个字符。通常采用ASCII码。从最低位开始传送,靠时钟定位。

    奇偶校验位:数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此来校验数据传送的正确性。

    停止位:它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。

    空闲位:处于逻辑“1”状态,表示当前线路上没有数据传送。

    2、传输过程

    发送数据过程:空闲状态,线路处于高电位;当收到发送数据指令后,拉低线路一个数据位的时间T,接着数据按低位到高位依次发送,数据发送完毕后,接着发送奇偶校验位和停止位(停止位为高电位),一帧数据发送结束。

    接收数据过程:空闲状态,线路处于高电位;当检测到线路的下降沿(线路电位由高电位变为低电位)时说明线路有数据传输,按照约定的波特率从低位到高位接收数据,数据接收完毕后,接着接收并比较奇偶校验位是否正确,如果正确则通知后续设备准备接收数据或存入缓存。

    由于UART是异步传输,没有传输同步时钟。为了能保证数据传输的正确性,UART对RX线上的数据采样时通常取中间的采样值或者是从中间取多个点的值然后取出现次数最多的电平值,以保证采样不会滑码或误码。
     

     

    三、程序设计

    程序总共分为4个文件,分别为my_uart_top.v、speed_select.v、my_uart_rx.v和my_uart_tx.v。模块框图如下:

    具体程序如下所示,解释都在注释里了。

    my_uart_top.v

    
    
    /*
    PC机上开串口调试助手.
    发送一个字符(波特率9600,数据位8位,停止位1位)
    到开发板(中间通过串口线相连)
    FPGA收到字符后,回发给PC机上,在串口助手上显示
    */
    
    
    
    `timescale 1ns / 1ps
    
    module my_uart_top(
    				clk,rst_n,
    				rs232_rx,rs232_tx
    				);
    
    input clk;			// 50MHz主时钟
    input rst_n;		//低电平复位信号
    
    input rs232_rx;		// RS232接收数据信号
    output rs232_tx;	//	RS232发送数据信号
    
    wire bps_start1,bps_start2;	//接收到数据后,波特率时钟启动信号置位
    wire clk_bps1,clk_bps2;		// clk_bps_r高电平为接收数据位的中间采样点,同时也作为发送数据的数据改变点 
    wire[7:0] rx_data;	//接收数据寄存器,保存直至下一个数据来到
    wire rx_int;		//接收数据中断信号,接收到数据期间始终为高电平
    //----------------------------------------------------
    //下面的四个模块中,speed_rx和speed_tx是两个完全独立的硬件模块,可称之为逻辑复制
    //(不是资源共享,和软件中的同一个子程序调用不能混为一谈)
    
    speed_select		speed_rx(	
    							.clk(clk),	//波特率选择模块
    							.rst_n(rst_n),
    							.bps_start(bps_start1),
    							.clk_bps(clk_bps1)
    						);
    
    my_uart_rx			my_uart_rx(		
    							.clk(clk),	//接收数据模块
    							.rst_n(rst_n),
    							.rs232_rx(rs232_rx),
    							.rx_data(rx_data),
    							.rx_int(rx_int),
    							.clk_bps(clk_bps1),
    							.bps_start(bps_start1)
    						);
    
    ///						
    speed_select		speed_tx(	
    							.clk(clk),	//波特率选择模块
    							.rst_n(rst_n),
    							.bps_start(bps_start2),
    							.clk_bps(clk_bps2)
    						);
    
    my_uart_tx			my_uart_tx(		
    							.clk(clk),	//发送数据模块
    							.rst_n(rst_n),
    							.rx_data(rx_data),
    							.rx_int(rx_int),
    							.rs232_tx(rs232_tx),
    							.clk_bps(clk_bps2),
    							.bps_start(bps_start2)
    						);
    
    endmodule
    

    speed_select.v

    `timescale 1ns / 1ps
    
    module speed_select(
    				clk,rst_n,
    				bps_start,clk_bps
    			);
    
    input clk;	// 50MHz主时钟
    input rst_n;	//低电平复位信号
    input bps_start;	//接收到数据后,波特率时钟启动信号置位
    output clk_bps;	// clk_bps的高电平为接收或者发送数据位的中间采样点 
    
    /*
    parameter 		bps9600 	= 5207,	//波特率为9600bps
    			 	bps19200 	= 2603,	//波特率为19200bps
    				bps38400 	= 1301,	//波特率为38400bps
    				bps57600 	= 867,	//波特率为57600bps
    				bps115200	= 433;	//波特率为115200bps
    				bps921600	= 54;	//波特率为921600bps
    				bps1000000	= 50;	//波特率为1000000bps
    
    parameter 		bps9600_2 	= 2603,
    				bps19200_2	= 1301,
    				bps38400_2	= 650,
    				bps57600_2	= 433,
    				bps115200_2 = 216;  
    				bps921600_2 = 27;  
    				bps1000000_2 = 25;  
    */
    
    	//以下波特率分频计数值可参照上面的参数进行更改
    `define		BPS_PARA		5207	//波特率为9600时的分频计数值
    `define 	BPS_PARA_2		2603	//波特率为9600时的分频计数值的一半,用于数据采样
    
    reg[12:0] cnt;			//分频计数
    reg clk_bps_r;			//波特率时钟寄存器
    
    //----------------------------------------------------------
    reg[2:0] uart_ctrl;	// uart波特率选择寄存器
    //----------------------------------------------------------
    
    always @ (posedge clk or negedge rst_n)
    	if(!rst_n) cnt <= 13'd0;
    	else if((cnt == `BPS_PARA) || !bps_start) cnt <= 13'd0;	//波特率计数清零
    	else cnt <= cnt+1'b1;			//波特率时钟计数启动
    
    always @ (posedge clk or negedge rst_n)
    	if(!rst_n) clk_bps_r <= 1'b0;
    	else if(cnt == `BPS_PARA_2 && bps_start) clk_bps_r <= 1'b1;	// clk_bps_r高电平为接收数据位的中间采样点,同时也作为发送数据的数据改变点
    	else clk_bps_r <= 1'b0;
    
    assign clk_bps = clk_bps_r;
    
    endmodule
    
    
    
    

    my_uart_rx.v

    `timescale 1ns / 1ps
    
    module my_uart_rx(
    				clk,rst_n,
    				rs232_rx,rx_data,rx_int,
    				clk_bps,bps_start
    			);
    
    input clk;		// 50MHz主时钟
    input rst_n;	//低电平复位信号
    input rs232_rx;	// RS232接收数据信号
    input clk_bps;	// clk_bps的高电平为接收或者发送数据位的中间采样点
    output bps_start;		//接收到数据后,波特率时钟启动信号置位
    output[7:0] rx_data;	//接收数据寄存器,保存直至下一个数据来到 
    output rx_int;	//接收数据中断信号,接收到数据期间始终为高电平
    
    //----------------------------------------------------------------
    reg rs232_rx0,rs232_rx1,rs232_rx2,rs232_rx3;	//接收数据寄存器,滤波用
    wire neg_rs232_rx;	//表示数据线接收到下降沿
    
    always @ (posedge clk or negedge rst_n) begin
    	if(!rst_n) begin
    			rs232_rx0 <= 1'b0;
    			rs232_rx1 <= 1'b0;
    			rs232_rx2 <= 1'b0;
    			rs232_rx3 <= 1'b0;
    		end
    	else begin
    			rs232_rx0 <= rs232_rx;
    			rs232_rx1 <= rs232_rx0;
    			rs232_rx2 <= rs232_rx1;
    			rs232_rx3 <= rs232_rx2;
    		end
    end
    	//下面的下降沿检测可以滤掉<20ns-40ns的毛刺(包括高脉冲和低脉冲毛刺),
    	//这里就是用资源换稳定(前提是我们对时间要求不是那么苛刻,因为输入信号打了好几拍) 
    	//(当然我们的有效低脉冲信号肯定是远远大于40ns的)
    assign neg_rs232_rx = rs232_rx3 & rs232_rx2 & ~rs232_rx1 & ~rs232_rx0;	//接收到下降沿后neg_rs232_rx置高一个时钟周期
    //----------------------------------------------------------------
    reg bps_start_r;
    reg[3:0] num;	//移位次数
    reg rx_int;		//接收数据中断信号,接收到数据期间始终为高电平
    
    always @ (posedge clk or negedge rst_n)
    	if(!rst_n) begin
    			bps_start_r <= 1'bz;
    			rx_int <= 1'b0;
    		end
    	else if(neg_rs232_rx) begin		//接收到串口接收线rs232_rx的下降沿标志信号
    			bps_start_r <= 1'b1;	//启动串口准备数据接收
    			rx_int <= 1'b1;			//接收数据中断信号使能
    		end
    	else if(num==4'd9) begin		//接收完有用数据信息	
    			bps_start_r <= 1'b0;	//数据接收完毕,释放波特率启动信号
    			rx_int <= 1'b0;			//接收数据中断信号关闭
    		end
    
    assign bps_start = bps_start_r;
    
    //----------------------------------------------------------------
    reg[7:0] rx_data_r;		//串口接收数据寄存器,保存直至下一个数据来到
    //----------------------------------------------------------------
    
    reg[7:0] rx_temp_data;	//当前接收数据寄存器
    
    always @ (posedge clk or negedge rst_n)
    	if(!rst_n) begin
    			rx_temp_data <= 8'd0;
    			num <= 4'd0;
    			rx_data_r <= 8'd0;
    		end
    	else if(rx_int) begin	//接收数据处理
    		if(clk_bps) begin	//读取并保存数据,接收数据为一个起始位,8bit数据,1或2个结束位		
    				num <= num+1'b1;
    				case (num)
    						4'd1: rx_temp_data[0] <= rs232_rx;	//锁存第0bit
    						4'd2: rx_temp_data[1] <= rs232_rx;	//锁存第1bit
    						4'd3: rx_temp_data[2] <= rs232_rx;	//锁存第2bit
    						4'd4: rx_temp_data[3] <= rs232_rx;	//锁存第3bit
    						4'd5: rx_temp_data[4] <= rs232_rx;	//锁存第4bit
    						4'd6: rx_temp_data[5] <= rs232_rx;	//锁存第5bit
    						4'd7: rx_temp_data[6] <= rs232_rx;	//锁存第6bit
    						4'd8: rx_temp_data[7] <= rs232_rx;	//锁存第7bit
    						default: ;
    					endcase
    			end
    		else if(num == 4'd9) begin		//我1+8=9bit的有效数据
    				num <= 4'd0;			//接收到STOP位后结束,num清零
    				rx_data_r <= rx_temp_data;	//把数据锁存到数据寄存器rx_data中
    			end
    		end
    
    assign rx_data = rx_data_r;	
    
    endmodule
    

    my_uart_tx.v

    `timescale 1ns / 1ps
    
    module my_uart_tx(
    				clk,rst_n,
    				rx_data,rx_int,rs232_tx,
    				clk_bps,bps_start
    			);
    
    input clk;			// 50MHz主时钟
    input rst_n;		//低电平复位信号
    input clk_bps;		// clk_bps_r高电平为接收数据位的中间采样点,同时也作为发送数据的数据改变点
    input[7:0] rx_data;	//接收数据寄存器
    input rx_int;		//接收数据中断信号,接收到数据期间始终为高电平,在该模块中利用它的下降沿来启动串口发送数据
    output rs232_tx;	// RS232发送数据信号
    output bps_start;	//接收或者要发送数据,波特率时钟启动信号置位
    
    //---------------------------------------------------------
    reg rx_int0,rx_int1,rx_int2;	//rx_int信号寄存器,捕捉下降沿滤波用
    wire neg_rx_int;	// rx_int下降沿标志位
    
    always @ (posedge clk or negedge rst_n) begin
    	if(!rst_n) begin
    			rx_int0 <= 1'b0;
    			rx_int1 <= 1'b0;
    			rx_int2 <= 1'b0;
    		end
    	else begin
    			rx_int0 <= rx_int;
    			rx_int1 <= rx_int0;
    			rx_int2 <= rx_int1;
    		end
    end
    
    assign neg_rx_int =  ~rx_int1 & rx_int2;	//捕捉到下降沿后,neg_rx_int拉高保持一个主时钟周期
    
    //---------------------------------------------------------
    reg[7:0] tx_data;	//待发送数据的寄存器
    //---------------------------------------------------------
    reg bps_start_r;
    reg tx_en;	//发送数据使能信号,高有效
    reg[3:0] num;
    
    always @ (posedge clk or negedge rst_n) begin
    	if(!rst_n) begin
    			bps_start_r <= 1'bz;
    			tx_en <= 1'b0;
    			tx_data <= 8'd0;
    		end
    	else if(neg_rx_int) begin	//接收数据完毕,准备把接收到的数据发回去
    			bps_start_r <= 1'b1;
    			tx_data <= rx_data;	//把接收到的数据存入发送数据寄存器
    			tx_en <= 1'b1;		//进入发送数据状态中
    		end
    	else if(num==4'd10) begin	//数据发送完成,复位
    			bps_start_r <= 1'b0;
    			tx_en <= 1'b0;
    		end
    end
    
    assign bps_start = bps_start_r;
    
    //---------------------------------------------------------
    reg rs232_tx_r;
    
    always @ (posedge clk or negedge rst_n) begin
    	if(!rst_n) begin
    			num <= 4'd0;
    			rs232_tx_r <= 1'b1;
    		end
    	else if(tx_en) begin
    			if(clk_bps)	begin
    					num <= num+1'b1;
    					case (num)
    						4'd0: rs232_tx_r <= 1'b0; 	//发送起始位
    						4'd1: rs232_tx_r <= tx_data[0];	//发送bit0
    						4'd2: rs232_tx_r <= tx_data[1];	//发送bit1
    						4'd3: rs232_tx_r <= tx_data[2];	//发送bit2
    						4'd4: rs232_tx_r <= tx_data[3];	//发送bit3
    						4'd5: rs232_tx_r <= tx_data[4];	//发送bit4
    						4'd6: rs232_tx_r <= tx_data[5];	//发送bit5
    						4'd7: rs232_tx_r <= tx_data[6];	//发送bit6
    						4'd8: rs232_tx_r <= tx_data[7];	//发送bit7
    						4'd9: rs232_tx_r <= 1'b1;	//发送结束位
    					 	default: rs232_tx_r <= 1'b1;
    						endcase
    				end
    			else if(num==4'd10) num <= 4'd0;	//复位
    		end
    end
    
    assign rs232_tx = rs232_tx_r;
    
    endmodule
    
    
    

     

    四、实验分析

    因为手里有逻辑分析仪,所以没有用ModelSim去仿真看时序,直接用逻辑分析仪抓的时序图。串口参数为波特率9600、无奇偶校验位、8位数据位、1个停止位,上位机发送字符“12”,用逻辑分析仪抓取FPGA的RX和TX线时序如下:

    因为用的是正点原子的XCOM串口助手V2.2,看到最高支持到3M的波特率,然后又把波特率调到了1M(1000000),抓了下时序如下图:

    之后又把波特率调到了2M(2000000),时序图如下:

    此时串口助手上没能返回正确的数据,可以发现波特率调到2M之后RX的时序不正常了,数据之间的间隙应该是500ns为单位的,但是这里数据之间的时间间隙太大了,RX线上时序混乱了,为什么会这样呢?

    先来分析一下可能是哪些方面的原因:

    (1)逻辑分析仪:逻辑分析仪本身是绝对没问题的,有1GHz的采样速率,在公司经常抓上百MHz的时序图,抓这条几MHz的RX线完全是杀鸡用牛刀,首先排除这个原因。

    (2)引脚问题:逻辑分析仪抓的是FPGA开发板上的引脚,后来测了下TX线直接25MHz输出方波,能抓到很漂亮的波形,应该也不是信号问题,2MHz的频率不算多高。

    (3)串口线问题:我用的是9针串口转USB的串口线,后来换了USB转TTL的串口模块来试试,也是1M波特率的时候可以,2M就不行了。

    (4)串口助手:试过了直接短路USB转TTL模块的RX和TX,给到3M波特率依然可以正常收发。串口助手应该是没问题的。

    可能原因是串口模块连了杜邦线后又接到开发板上信号变差了,后来直接用逻辑分析仪对着USB串口模块的TX引脚抓线,2MHz波特率,仍然发送“12”,发现在起始位处会有明显的电平跳动,即不稳定,试了很多次都是这样。如下图。导致FPGA接收数据时误判,这应该是我手里的这个几块钱一个的串口模块质量不太好。

    后来上网查了查,发现也有不少人反馈说串口上到2MHz左右的波特率之后就会出现收发不正确的问题,大概可能也是这个原因,毕竟RS232本来就是被当做一种低速通信方式来使用的,然后市面上卖的串口模块质量有好有坏,自然也很难跑到太高的速度吧,平常使用还是稳点别跑太高速度,真的需要跑很高的速度就不要用这种通信方式了。

     

    五、总结

    本文是在学习FPGA的串口通信时做的一个小实验,在做实验的过程中遇到了关于串口模块的一些使用问题,在此进行了总结和分析。

     

    六、参考资料

    《FPGA的串口通讯(UART)》https://blog.csdn.net/emperor_strange/article/details/89311933

    展开全文
  • 基于串口通信FPGA步进电机多状态控制,verilog语言编写,串口软件使用一般的串口调试助手即可。本程序是本人作FPGA课程设计的,已经通过硬件测试。
  • FPGA串口通信要想应用在实际的工业现场,需要一整套完整的协议,来确保数据传输的可靠性和系统的稳定性。基于协议,进行串口指令解析是控制的关键,对于串口指令解析,有两种方式:逻辑解析和软硬核(我用的Altera的...

    FPGA串口通信要想应用在实际的工业现场,需要一整套完整的协议,来确保数据传输的可靠性和系统的稳定性。基于协议,进行串口指令解析是控制的关键,对于串口指令解析,有两种方式:逻辑解析和软硬核(我用的Altera的嵌入式软核NIOS)解析。

    使用逻辑进行解析,往往使用逻辑进行数据收发,此处可参看小梅哥《FPGA数字系统设计教程》,其核心在于数据接收部分的设计,也即是数据帧接收状态机的设计。状态机大致包括以下状态:帧头、字节数、数据、帧尾。默认情况下,状态机处于帧头状态,如果检测到帧头,则进入下一状态,否则,状态机仍然处于帧头,依此循环,直至检测完一个数据帧,此时产生一个高信号,同时将检测到的数据帧锁存,用于解析指令状态,同时,开始下一数据帧的接收。

    使用Nios进行解析,通常使用Altera自带的Uart核来完成数据收发,此部分请参看我的博文《基于nios的串口通信uart设计》,地址如下:http://blog.chinaaet.com/helimin/p/5100018309

    具体协议参看我的博文《FPGA串口通信及数据解析》,地址如下:

    http://blog.chinaaet.com/helimin/p/5100051178

    使用中断的方式,每接收到1个字节,进行一次中断。接收到数据后,进行解析,思路如下:

    1. 定义一个指令缓存数组recBuf,每次进入中断,就将接收到的1字节数据存至数组,同时数组尾部索引recTailPointor自加1。

    void uart0_isr(void * context,alt_u32 id)
    {
        rxdata0 = IORD_ALTERA_AVALON_UART_RXDATA(UART_0_BASE);
        recBuf[recTailPointor] = rxdata0;//获取数据
        recTailPointor += 1;
    }

     

    2. 进入串口解析函数,在里面进行如下操作:调用当前字节数计算函数,计算放入数组中的字节数

     

    inline alt_u8 getRecLen(void)
    {
        alt_u8 lureclen = 0;
        if(recHeadPointor <= recTailPointor){
            lureclen = recTailPointor - recHeadPointor;
        }
        else{ //如果recHeadPointor > recTailPointor,说明recBuf[256]已经写到最后,又从头重写。
            lureclen = 256 - (recHeadPointor - recTailPointor);
        }
        return lureclen;
    }

    3. 在串口解析函数判断放入数组中字节数是否大于0,如果大于0则判断数组中第一个元素是否是帧头,如果是帧头,再根据第二个元素(字节数)来进行数据接收,直到接收完一帧数据。此时将接收数据索引的头指针移至尾部。

     

    alt_u8 UartAnalyze(void)
    {
        alt_u8 i = 0;
        alt_u8 luRecLen = 0;
        alt_u8 lutmp = 0;
        luRecLen = getRecLen();        //判断放入串口数据接收数组中的数据字节数
        if(luRecLen > 0){
            if(recBuf[recHeadPointor] == 0xcc){
                while(1){//一直等到接收完一帧指令跳出此循环
                    if(uartOverTimeFlag == 0){
                        luRecLen = getRecLen();
                        if(luRecLen >= 1){
                            if(luRecLen > (recBuf[recHeadPointor+1] + 3)){
                                break;//如果接收完一帧,则跳出循环,继续向下执行
                            }
                        }
                    }
                    else{
                        recHeadPointor = recTailPointor;//头移到尾处。
                        recTime = 0;
                        uartOverTimeFlag = 0;
                        return ERROR_OVERTIME;
                    }
                }
                //一帧数据长度足够则将数据转到uartCMDBuf[]中。
                for(i=0;i<=luRecLen;i++){
                    lutmp = recHeadPointor + i;
                    uartCMDBuf[i] = recBuf[lutmp];
                }
                recHeadPointor = recTailPointor;//头移到尾处。
                recTime = 0;
                uartOverTimeFlag = 0;
     
                if(Get_DRC(uartCMDBuf,uartCMDBuf[1]) == uartCMDBuf[uartCMDBuf[1]+1]){
                    return CMD_CORRECT;
                }
                else{//if(Get_DRC(uartCMDBuf,uartCMDBuf[1]) == uartCMDBuf[uartCMDBuf[1]+1])
                    return ERROR_DRC_NO_PASS;
                }
            }
            else{//if(recBuf[recHeadPointor] == 0xcc)&& if(recBuf[recHeadPointor] == 0xf0)
                recHeadPointor = recTailPointor;//头移到尾处。
                recTime = 0;
                uartOverTimeFlag = 0;
                return ERROR_SHAKE_HANDS;
            }
        }
        else{//luRecLen=0说明没有串口数据。
            recHeadPointor = recTailPointor;//头移到尾处。
            recTime = 0;
            uartOverTimeFlag = 0;
            return 0;
        }
    }

     

    在数据接收时,为了使得系统更稳定,使用定时器进行超时判断,如若一段时间指令都没有接收完毕,则放弃此次接收。

    一帧数据接收完毕后,进行CRC循环校验、帧尾检测,如果都ok,则将接收数组中的一帧数据转移,用于数据的解析,同时,开始下一帧数据的接收。

    当时写这个串口解析部分的程序,得到了高手的指点,花了两个多星期。其实基本思路比较简单,使用中断进行数据接收,在解析函数中根据协议进行接收,接收完后再根据协议进行解析。

    两种方式中,对于简单的工程应用,可以使用方式一;而对于比较复杂的协议,有时涉及到上百种控制,并且不同的数据包字节数可能不一样时,使用方式二会显得非常适合。

    重点是协议,制订一份好的协议至关重要。

    展开全文
  • FPGA串口通信基础知识 首先,明确以下内容: 一、串口、并口、USB接口 1、串口叫做串行通信接口,它是指数据一位一位的顺序传输,最少只需要一根传输线即可完成,成本低但传输速度慢。串行通讯的距离可以从几米到...
  • 串口通信及RAM读写一、工程简介二、功能介绍三、具体实现1....本工程就是利用串口通信的方式将上位机发送过来的数据接收后写入到FPGA的RAM中,等待发送信号出发后,在将RAM中的信息读取出来发送到上
  • (一)串口通信模块设计 1.串口通信协议 UART通信的一帧一般由11到12位数据组成。1bit的起始位,检测为低电平表示数据开始传输;紧接着8bits的数据;然后是1bit的奇偶校验位,可以是奇校验或者偶校验;最后是1bit或2...
  • 前几天刚开始学习FPGA,正好学到UART...接下来是正题UART通信协议,主要是通过状态机编写 发送部分之一 //状态机 always@(*) begin case(state) S_IDLE: if(tx_data_valid == 1'b1) next_state <= S_...
  • 基于fpga串口通信实现

    万次阅读 多人点赞 2017-12-18 19:10:42
    串行接口是连接FPGA和PC的一种简单方式。这个项目向大家展示了如果使用FPGA来创建RS-232收发器。 整个项目包括5个部分 1.  RS232是怎样工作的 2.  如何产生需要的波特率 3.  发送模块 4.  接收...
  • MCU与FPGA串口通信

    2017-05-19 16:00:00
    FPGA以9600的波特率向单片机发送32位数据,然后单片机对数据进行解析,显示在显示屏上面 波特率的产生 : 9600bps是指每秒钟发送9600个bit,即1bit的时间为1/9600,fpga板子自带50M晶振,那么一bit的时间时1/9600/1...
  • FPGA——UART串口通信2

    2019-08-29 22:05:50
    从PC的串口调试助手上发送32bit数据到FPGAFPGA接收到数据以后把接收的数据返回给串口调试助手显示 115200bps,32bit 顶层架构 比特率产生模块和数据接收模块参考UART串口收发的原理与Verilog实现 代码 数据缓存...
  • FPGA + labwindows/CVI 2017 串口通信 电子钟1. 描述2.环境3.整体框架4.具体实现思路a. 顶层模块概述串口收发模块时钟模块编码器模块5.上位机实现6. 总结 1. 描述 基于上一个FPGA的数字钟的实验,采用CVI2017制作了...
  • FPGA 常常用于执行基于序列和控制的行动, 比如实现一个简单的通信协议。对于设计人员来说,满足这些行动和序列要求的最佳方法则是使用状态机状态机是在数量有限的状态之间进行转换的逻辑结构。一个状态机在某个...
  • 基于FPGA的UART串口通信实验(VHDL语言实现)

    千次阅读 多人点赞 2019-09-03 23:09:32
    **基于FPGA的UART串口通信实验(VHDL语言实现)** 一、前言: 最近在做UART串口通信的相关实验,然后在网上查了很多资料,发现网上的大多数代码错误太多且难以理解。故在完成此实验后,起了写一篇博客的心思,以...
  • 1.在最初的应用中, RS-232 串口标准常用于计算机、路由与调制调解器(MODEN,俗称“猫” )之间的通讯,在这种通讯系统中,设备被分为数据终端设备 DTE(计算机、路由)和数据通讯设备 DCE(调制调解器)。 我们以这种...
  • FPGA实现串口通信 一、基本概念 1、并行通信:指数据的多个位用多条数据线同时传输,同时具备传输速度快的优点和占用引脚资源多的缺点。 2、串行通信:将数据分成一位一位的形式在一条传输线上逐个传输,优缺点与...
  • VB串口通信源码210个

    千次下载 热门讨论 2013-10-15 11:32:27
    022、VB编写的仿真实电子琴操作界面,包含与FPGA串口通信的功能 023、VB串口API通讯,附带BAS文件全部源码,实现与饭卡读卡器通讯 024、VB串口编程,关于上位机的应用,特别适合初级学习VB的学员 025、VB串口编程调试...
  • 上一篇博文:【入门学习三】基于 FPGA 使用 Verilog 实现按键状态机控制 LED 流水灯代码及原理详解 本文内容:从 PC 上位机通过 COM 发送数据给 FPGAFPGA 接收到数据后,将数据回传给 PC 上位机。 一、相关知识...
  • 本文引用地址:...实例的主要内容本节旨在通过分析UART控制器,设计实现了FPGA通过RS-232C接口与PC通信。设计过程中用Modelsim对UART控制器进行仿真,帮助读者进一步了解UART协议的具体时序...
  • 基于FPGA的红外遥控解码与PC串口通信 zouxy09@qq.com http://blog.csdn.net/zouxy09 这是我的《电子设计EDA》的课程设计作业(呵呵,这个月都拿来做大作业了,各种大作业,能发上来和大家分享的我会发上来,...
  • 1)资料下载:点击资料即可下载2)对正点原子Linux感兴趣的同学可以加群讨论:9354467413)关注正点原子公众号,获取...这一章我们将学习 STM32F4 的串口,教大家如何使用 STM32F4 的串口来发送和接收数据。本章将实...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,226
精华内容 490
关键字:

串口通信fpga状态机