FPGA verilog uart串口收发代码
- 格式:docx
- 大小:43.03 KB
- 文档页数:7
踏雪无痕的博客串口通信是目前比较重要的一种通信方式,主要是用于计算机和外部的通信。
首先简单的介绍一下串口通信的原理:串口用于ASCII码字符的传输。
通信使用3根线完成:(1)地线(GND),(2)发送(TXD),(3)接收(RXD)。
由于串口通信是异步的,端口能够在一根线上发送数据同时在另一根线上接收数据。
其它线用于握手,但是不是必须的。
串口通信最重要的参数是波特率、数据位、停止位和奇偶校验位。
对于两个进行通信的端口,这些参数必须匹配:a,波特率:这是一个衡量通信速度的参数。
它表示每秒钟传送的bit的个数。
例如300波特表示每秒钟发送300个bit。
当我们提到时钟周期时,我们就是指波特率例如如果协议需要4800波特率,那么时钟是4800Hz。
这意味着串口通信在数据线上的采样率为4800Hz。
通常电话线的波特率为14400,28800和36600。
波特率可以远远大于这些值,但是波特率和距离成反比。
高波特率常常用于放置的很近的仪器间的通信,典型的例子就是GPIB 设备的通信。
b,数据位:这是衡量通信中实际数据位的参数。
当计算机发送一个信息包,实际的数据不会是8位的,标准的值是5、7和8位。
如何设置取决于你想传送的信息。
比如,标准的ASCII码是0~127(7位)。
扩展的ASCII 码是0~255(8位)。
如果数据使用简单的文本(标准ASCII码),那么每个数据包使用7位数据。
每个包是指一个字节,包括开始/停止位,数据位和奇偶校验位。
由于实际数据位取决于通信协议的选取,术语“包”指任何通信的情况。
c,停止位:用于表示单个包的最后一位。
典型的值为1,1.5和2位。
由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。
因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。
适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
基于FPGA的UART接口模块设计关键字:FPGAUART(UniversalAnynchronousReceiverTransmitter,通用异步接收发送器)是广泛应用的串行数据传输协议之一,其应用范围遍及计算机外设、工控自动化等场合。
虽然USB传输协议比UART协议有更高的性能,但电路复杂开发难度大,并且大多数的微处理器只集成了UART,因此UART仍然是目前数字系统之间进行串行通信的主要协议。
随着FPGA的广泛应用,经常需要FPGA与其他数字系统进行串行通信,专用的UART 集成电路如8250,8251等是比较复杂的,因为专用的UART集成电路既要考虑异步的收发功能,又要兼容RS232接口设计,在实际应用中,往往只需要用到UART的基本功能,使用专用芯片会造成资源浪费和成本提高。
可以将所需要的UART功能集成到FPGA内部,实现FPGA与其他数字系统的直接通信,从而简化了整个系统电路,提高了可靠性、稳定性和灵活性。
1 UART简介基本的UART通信只需要两条信号线(RXD,TXD)就可以完成数据的相互通信,接收与发送是全双工形式,其中TXD是UART发送端,RXD是UART接收端。
UART基本特点是:在信号线上有两种状态,可分别用逻辑1(高电平)和逻辑0(低电平)来区分。
在发送器空闲时,数据线应保持在逻辑高电平状态。
发送器是通过发送起始比特而开始一个字符传送,起始比特使数据线处于逻辑0状态,提示接收器数据传输即将开始。
数据位一般为8位一个字节的数(也有6位7位的情况),低位(LSB)在前,高位(MSB)在后。
校验位一般用来判断接收的数据位有无错误,一般是奇偶校验。
停止位在最后,用以标志UART 一个字符传送的结束,它对应于逻辑1状态,UART数据帧格式如图1所示。
图1 UART数据帧格式2 UART功能实现UART可以分解为3个子模块:波特率发生器模块;发送模块;接收模块。
UART的功能主要由VHDL硬件描述语言编程,图2是编译后生成的图元SCI,它包括了UART的最主要的部分,即发送模块和接收模块。
通用异步接收发送器UART (Universal Asyn2chronousReceiver and Transmitter )能够在串行方式下发送和接收数据,数据传送方式只需要一对线路就能实现远距离数据通信。
其以资源简单、传输距离远、易于实现等特点成为各种处理器的标准集成外设之一。
在实际的应用设计中,经常要用到UART 的多个主要功能,常用的标准通信波特率有9600bps 、15200bps 等。
本文使用Verilog HDL 语言将UART 器件的核心功能描述成一个异步串行数字收发模块,并且在Modelsim 上实现了其功能仿真。
1UART 原理及设计简介UART 的全称是通用异步收发器(Universal Asynchronous Receiver /Transmitter ),是实现设备之间低速数据通信的标准协议。
“异步”指不需要额外的时钟线进行数据的同步传输,是一种串行总线接口,只需占用2根线就可以完成数据的收发(1根接收数据,1根发送数据)。
UART 一帧由起始位、数据位、校验位和停止位组成。
数据逐位传输,示意图如图1所示。
异步通信要求发送的每一帧数据都必须按照图1给定的UART 格式进行格式化。
一帧数据由4部分组成,首先是起始位“0”;接着是发送的数据(这里采用8位);然后是可选的检验位来判断接收数据有无错误(这里选用奇检验);最后是停止位“1”(停止位可以为1位、1.5位和2位)。
若线路上没有传输的数据,则线路始终保持为“1”,即空闲。
接收端不断检测线路状态,非接收状态下如果检测到线路由“1”变为“0”(“0”至少保持8个内部时钟周期),则认为有发送数据需接收,接收器进入接收阶段。
UART 模块总结构如图2所示。
首先,发送时按照UART 帧格式,由发送写信号启动发送波特率发生器,先发送一位起始位,然后由发送波特率发生器时钟启动发送移位寄存器,将发送数据存入发送移位寄存器,并将发送一位寄存器中的数据发送,同时由发送波特率发生器时钟启动发送数据计数器,当计数器计数到第10位时,产生奇偶校验位,此时发送奇偶校验位,计数到第11位时,发送停止位,同时产生发送完成指示信号。
四川师范大学本科毕业设计基于FPGA的UART设计学生姓名院系名称专业名称班级学号指导教师完成时间基于FPGA的UART设计电子信息工程专业摘要:UART(通用异步收发器)是一种应用广泛,协议简单,易于调试的串行传输接口。
FPGA是能高密度,低消耗完成所需要的逻辑功能的一种在线可编程器件,是现在业内提高系统集成度最佳技术之一,其可反复配置,且使用灵活。
VHDL 是描述电路功能或行为的一种硬件语言。
本文首先阐述了运用FPGA实现UART接口的意义。
接着介绍了UART的波特率发生器,发送功能和接收三大核心功能的理论知识。
FPGA的工作原理,配置模式以及VHDL语言实现状态机的相关理论基础。
给出了运用VHDL语言将UART三大功能嵌入在FPGA上的模块化设计方法。
设计包括四大模块:顶层模块,波特率发生器,UART接收器,UART发生器。
在FPGA片上集成UART主要功能,减少了电路板体积,同时电路也增加了可移植和反复配置功能,有效提高了电路的集成度和灵活性。
最后运用Quartus ii 9.1实现了其功能仿真。
关键词:FPGA UART VHDL 有限状态机The Design of Universal Asynchronous Receiver Transmitter Based on FPGAAbstract:UART (Universal Asynchronous Receiver Transmitter) is a widely used, simple protocol, easy to debug serial transmission interface. FPGA is capable of high-density, low-cost needed to complete a line of programmable logic devices, is now one of the industry's best technologies to improve system integration, which can be repeated to configure and use and flexible. VHDL description of the circuit function or behavior is a hardware language. This paper first describes the use of FPGA to realize the significance of the UART interface. Then introduced the theory of knowledge UART baud rate generator, sending and receiving three core functions. FPGA works, configuration mode and the VHDL-based state machine theory. Gives the UART using VHDL language to three functions embedded in the FPGA design. Design includes four modules: the top-level module, baud rate generator, UART receivers, UART generator. Integrated on-chip UART FPGA basic functions, reducing board space, the circuit also increases the portability and repeated configuration capabilities, improve the integration and flexibility of the circuit. Finally, the use of Quartus ii 9.1 implements its functional simulation.Key Words:FPGA ;UART ;VHDL;FSM目录摘要: (I)Abstract: (II)1 绪论 (1)1.1 课题背景 (1)1.2 课题研究现状 (2)1.3 课题研究内容与主要工作 (3)1.4 课题内容结构 (3)2 UART理论基础 (4)2.1 接口技术简介 (4)2.2 UART基本结构 (4)2.3 UART数据帧格式 (5)2.4 涉及到的理论计算 (6)3 设计工具 (7)3.1 课题硬件平台——FPGA (7)3.2 设计工具QuartusII简介与使用 (7)3.3 VHDL语言简介 (7)4 UART实现方案 (8)4.1 系统总体结构 (8)4.2 顶层模块设计 (8)4.3 波特率发生模块设计 (9)4.4 接收模块 (10)4.5 发送模块设计 (12)5 UART设计的仿真与验证 (15)5.1波特率发生模块仿真 (15)5.2 接收模块仿真 (15)5.3 发送模块仿真 (16)6 总结 (17)参考文献 (18)致谢 (19)附录 (20)基于FPGA的UART设计1 绪论在计算机的数据通信中,外设一般不能与计算机直接相连,它们之间的信息交换主要存在以下问题:(1)速度不匹配。
一般教科书上提供的UART收发的程序往往是一段采用轮循(Polling)方式完成收发的简单代码。
但对于高速的AVR来讲,采用这种方式大大降低了MUC的效率。
在使用AVR时,应根据芯片本身的特点(片内大容量数据存储器RAM,更适合采用高级语言编写系统程序),编写高效可靠的UART收发接口(低层)程序。
下面是一个典型的ATmega128的软件USART的接口程序。
#include <mega128.h>#define RXB8 1#define TXB8 0#define UPE 2#define OVR 3#define FE 4#define UDRE 5#define RXC 7#define FRAMING_ERROR (1<<FE)#define PARITY_ERROR (1<<UPE)#define DATA_OVERRUN (1<<OVR)#define DATA_REGISTER_EMPTY (1<<UDRE)#define RX_COMPLEte (1<<RXC)// USART0 Receiver buffer#define RX_BUFFER_SIZE0 8char rx_buffer0[RX_BUFFER_SIZE0];unsigned char rx_wr_index0,rx_rd_index0,rx_counter0;// This flag is set ON USART0 Receiver buffer overflowbit rx_buffer_overflow0;// USART0 Receiver interrupt service routine#pragma savereg-interrupt [USART0_RXC] void uart0_rx_isr(void){char status,data;#asmpush r26push r27push r30push r31in r26,sregpush r26#endasmstatus=UCSR0A;data=UDR0;if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0){rx_buffer0[rx_wr_index0]=data;if (++rx_wr_index0 == RX_BUFFER_SIZE0) rx_wr_index0=0; if (++rx_counter0 == RX_BUFFER_SIZE0){rx_counter0=0;rx_buffer_overflow0=1;};};#asmpop r26out sreg,r26pop r31pop r30pop r27pop r26#endasm}#pragma savereg+#ifndef _DEBUG_TERMINAL_IO_// Get a character from the USART0 Receiver buffer#define _ALTERNATE_GETCHAR_#pragma used+char getchar(void){char data;while (rx_counter0==0);data=rx_buffer0[rx_rd_index0];if (++rx_rd_index0 == RX_BUFFER_SIZE0) rx_rd_index0=0;#asm("cli")--rx_counter0;#asm("sei")return data;}#pragma used-#endif// USART0 Transmitter buffer#define TX_BUFFER_SIZE0 8char tx_buffer0[TX_BUFFER_SIZE0];unsigned char tx_wr_index0,tx_rd_index0,tx_counter0;// USART0 Transmitter interrupt service routine#pragma savereg-interrupt [USART0_TXC] void uart0_tx_isr(void){#asmpush r26push r27push r30push r31in r26,sregpush r26#edasmif (tx_counter0){--tx_counter0;UDR0=tx_buffer0[tx_rd_index0];if (++tx_rd_index0 == TX_BUFFER_SIZE0) tx_rd_index0=0; };#asmpop r26out sreg,r26pop r31pop r30pop r27pop r26#endasm}#pragma savereg+#ifndef _DEBUG_TERMINAL_IO_// Write a character to the USART0 Transmitter buffer#define _ALTERNATE_PUTCHAR_#pragma used+void putchar(char c){while (tx_counter0 == TX_BUFFER_SIZE0);#asm("cli")if (tx_counter0 || ((UCSR0A & DATA_REGISTER_EMPTY)==0)) {tx_buffer0[tx_wr_index0]=c;if (++tx_wr_index0 == TX_BUFFER_SIZE0) tx_wr_index0=0; ++tx_counter0;}elseUDR0=c;#asm("sei")}#pragma used-#endif// Standard Input/Output functions#include <stdio.h>// Declare your global variables herevoid main(void){// USART0 initialization// Communication Parameters: 8 Data, 1 Stop, No Parity// USART0 Receiver: On// USART0 Transmitter: On// USART0 Mode: Asynchronous// USART0 Baud rate: 9600UCSR0A=0x00;UCSR0B=0xD8;UCSR0C=0x06;UBRR0H=0x00;UBRR0L=0x67;// Global enable interrupts#asm("sei")while (1){// Place your code here};}这段由CVAVR程序生成器产生的UART接口代码是一个非常好的、高效可靠,并且值得认真学习和体会的。
`timescale 1ns / 100psmodule uart (dout,data_ready,framing_error,parity_error,rxd,clk16x,rst,rdn,din,tbre,tsre,wrn,sdo);output tbre ;output tsre ;output sdo ;input [7:0] din ;input rst ;input clk16x ;input wrn ;input rxd ;input rdn ;output [7:0] dout ;output data_ready ;output framing_error ;output parity_error ;rcvr u1 (dout,data_ready,framing_error,parity_error,rxd,clk16x,rst,rdn) ;txmit u2 (din,tbre,tsre,rst,clk16x,wrn,sdo) ;endmodule********************************************************************************************`timescale 1 ns / 1 nsmodule rcvr (dout,data_ready,framing_error,parity_error,rxd,clk16x,rst,rdn) ;input rxd ;input clk16x ;input rst ;input rdn ;output [7:0] dout ;output data_ready ;output framing_error ;output parity_error ;reg rxd1 ;reg rxd2 ;reg clk1x_enable ;reg [3:0] clkdiv ;reg [7:0] rsr ;reg [7:0] rbr ;reg [3:0] no_bits_rcvd ;reg data_ready ;reg parity ;reg parity_error ;reg framing_error ;wire clk1x ;assign dout = !rdn ? rbr : 8'bz ;always @(posedge clk16x or posedge rst)beginif (rst)beginrxd1 <= 1'b1 ;rxd2 <= 1'b1 ;endelsebeginrxd1 <= rxd ;rxd2 <= rxd1 ;endendalways @(posedge clk16x or posedge rst)beginif (rst)clk1x_enable <= 1'b0;else if (!rxd1 && rxd2)clk1x_enable <= 1'b1 ;else if (no_bits_rcvd == 4'b1100)clk1x_enable <= 1'b0 ;endalways @(posedge clk16x or posedge rst or negedge rdn)beginif (rst)data_ready = 1'b0 ;else if (!rdn)data_ready = 1'b0 ;elseif (no_bits_rcvd == 4'b1011)data_ready = 1'b1 ;endalways @(posedge clk16x or posedge rst)beginif (rst)clkdiv = 4'b0000 ;else if (clk1x_enable)clkdiv = clkdiv +1 ;endassign clk1x = clkdiv[3] ;always @(posedge clk1x or posedge rst)if (rst)beginrsr <= 8'b0 ;rbr <= 8'b0 ;parity <= 1'b1 ;framing_error = 1'b0 ;parity_error = 1'b0 ;endelsebeginif (no_bits_rcvd >= 4'b0001 && no_bits_rcvd <= 4'b1001)beginrsr[0] <= rxd2 ;rsr[7:1] <= rsr[6:0] ;parity <= parity ^ rsr[7] ;endelse if (no_bits_rcvd == 4'b1010)beginrbr <= rsr ;endelse if (!parity)parity_error = 1'b1 ;else if ((no_bits_rcvd == 4'b1011) && (rxd2 != 1'b1))framing_error = 1'b1 ;elseframing_error = 1'b0 ;endalways @(posedge clk1x or posedge rst or negedge clk1x_enable)if (rst)no_bits_rcvd = 4'b0000;elseif (!clk1x_enable)no_bits_rcvd = 4'b0000 ;elseno_bits_rcvd = no_bits_rcvd + 1 ;endmodule********************************************************************************************`timescale 1 ns / 1 nsmodule txmit (din,tbre,tsre,rst,clk16x,wrn,sdo) ;output tbre ;output tsre ;output sdo ;input [7:0] din ;input rst ;input clk16x ;input wrn ;reg tbre ;reg tsre ;reg clk1x_enable ;reg [7:0] tsr ;reg [7:0] tbr ;reg parity ;reg[3:0] clkdiv ;wire clk1x ;reg sdo ;reg [3:0] no_bits_sent ;reg wrn1 ;reg wrn2 ;always @(posedge clk16x or posedge rst)beginif (rst)beginwrn1 <= 1'b1 ;wrn2 <= 1'b1 ;endelsebeginwrn1 <= wrn ;wrn2 <= wrn1 ;endendalways @(posedge clk16x or posedge rst)beginif (rst)begintbre <= 1'b0 ;clk1x_enable <= 1'b0 ;endelse if (!wrn1 && wrn2)beginclk1x_enable <= 1'b1 ;tbre <= 1'b1 ;endelse if (no_bits_sent == 4'b0010)tbre <= 1'b1 ;else if (no_bits_sent == 4'b1101)beginclk1x_enable <= 1'b0 ;tbre <= 1'b0 ;endendalways @(negedge wrn or posedge rst)beginif (rst)tbr = 8'b0 ;elsetbr = din ;endalways @(posedge clk16x or posedge rst)beginif (rst)clkdiv = 4'b0 ;else if (clk1x_enable)clkdiv = clkdiv + 1 ;endassign clk1x = clkdiv[3] ;always @(negedge clk1x or posedge rst)if (rst)beginsdo <= 1'b1 ;tsre <= 1'b1 ;parity <= 1'b1 ;tsr <= 8'b0 ;endelsebeginif (no_bits_sent == 4'b0001)begintsr <= tbr ;tsre <= 1'b0 ;endelse if (no_bits_sent == 4'b0010)beginsdo <= 1'b0 ;endelseif ((no_bits_sent >= 4'b0011) && (no_bits_sent <= 4'b1010))begintsr[7:1] <= tsr[6:0] ;tsr[0] <= 1'b0 ;sdo <= tsr[7] ;parity <= parity ^ tsr[7] ;endelse if (no_bits_sent == 4'b1011)beginsdo <= parity ;endelse if (no_bits_sent == 4'b1100)beginsdo <= 1'b1 ;tsre <= 1'b1 ;endendalways @(posedge clk1x or posedge rst or negedge clk1x_enable)if (rst)no_bits_sent = 4'b0000 ;else if (!clk1x_enable)no_bits_sent = 4'b0000 ;elseno_bits_sent = no_bits_sent + 1 ;endmodule*******************************************************************************************module txmit_tb ;// Inputsreg [7:0] din;reg rst;reg clk16x;reg wrn;// Outputswire tbre;wire tsre;wire sdo;// Bidirs// Instantiate the UUTtxmit d (.tbre(tbre),.tsre(tsre),.sdo(sdo),.din(din),.rst(rst),.clk16x(clk16x),.wrn(wrn));// Initialize Inputs// `ifdef auto_initinitial begindin = 0;rst = 0;clk16x = 0;wrn = 1;end// `endifalways #10 clk16x = ~clk16x ;initial begin#3 rst = 1'b1 ;din = 8'b11110000 ;#25 rst = 1'b0 ;#30 wrn = 1'b0 ;#150 wrn = 1'b1 ;#4000 din = 8'b10101010 ;#870 wrn = 1'b0 ;#200 wrn = 1'b1 ;#3000 rst = 1'b1 ;endendmodule**********************************************************************************module rcvr_tb ;// Inputsreg rxd;reg clk16x;reg rst;reg rdn;// Outputswire [7:0] dout;wire data_ready;wire framing_error;wire parity_error;// Bidirs// Instantiate the UUTrcvr d (.rxd(rxd),.clk16x(clk16x),.rst(rst),.rdn(rdn),.dout(dout),.data_ready(data_ready), .framing_error(framing_error), .parity_error(parity_error));// Initialize Inputs// `ifdef auto_initinitial beginrxd = 1;clk16x = 0;rst = 0;rdn = 1;end// `endifalways #10 clk16x = ~clk16x ;initial begin#1 rst = 1'b1 ;#21 rst = 1'b0 ;#350 rxd = 1'b0 ; #1800 rxd = 1'b1 ; #1800 rxd = 1'b0 ; #1000 rdn = 1'b0 ; #500 rdn = 1'b1 ;endendmodule。
带fifo的串口发送器FPGA实现带fifo的Uart发送器的FPGA实现FPGA使用Altera公司EP4C15F17C8N芯片,它的性能优越,支持大部分IP核使用。
常用它来学习NIOS II。
使用它实现带FIFO的串口。
首先,对uart的实现进行设计。
Uart的时序如下图所示:一般使用Uart时,配置为无校验,一个停止位,一个起始位,8位数据。
其中尤其重要的为波特率的设置。
波特率对应了Uart通信的位宽,用FPGA实现Uart其实就是对通信位宽的调制。
位宽通过基础时钟分频得到。
Uart波特率计算及实现以9600波特率为例计算:9600的意思为每秒传输的bit数为9600,所以位宽为:1/9600s ,换算为ns为:104167ns。
那么,如果时钟频率为25MHz,位宽系数为:104167/40 = 2604。
综合上述分析,可以得到一个公式,即:分频系数= 时钟频率/波特率。
例如:115200波特率,50MHz时钟其对应位宽为:50000000/115200 = 434。
那么以115200波特率,50MHz时钟为例实现Uart的发送。
计时器对时钟计数,经过434个周期之后,对计数器加1操作,对相应的信号进行操作,即可实现,它的源代码如下:always @(posedge clk or negedge rst)if(rst == 1'd0) count <= 17'd0;else if(count == 17'd434) count <= 17'd0;else count <= count + 1'd1;wire sendclk = (count == 17'd434);根据sendclk对Uart进行实现,代码如下:always @(posedge clk or negedge rst)if(rst == 1'b0) TX <= 1'b1;else if(FIFOempty == 1'b1) begin TX <= 1'b1; endelsecase(txcount)4'd0: TX <= 1'b0; //start bit is low4'd1: TX <= fifodata[0];4'd2: TX <= fifodata[1];4'd3: TX <= fifodata[2];4'd4: TX <= fifodata[3];4'd5: TX <= fifodata[4];4'd6: TX <= fifodata[5];4'd7: TX <= fifodata[6];4'd8: TX <= fifodata[7];4'd9: TX <= 1'b1; // stop bitsdefault: TX <= 1'b1 ;endcase它包含了一个有限状态机。
Verilog是一种硬件描述语言,用于设计和模拟数字电路。
UART(通用异步收发器)是一种常用的串行通信协议,用于在计算机和外部设备之间进行数据传输。
Verilog UART协议是指使用Verilog语言实现UART通信协议的硬件设计。
以下是Verilog中实现UART通信协议的一般步骤和基本思路:1.时钟和波特率设置:在Verilog中,首先需要定义时钟信号,以及基于波特率的时钟分频器。
波特率决定了数据传输的速率。
2.数据发送:实现UART协议的发送部分,包括数据线(TX)的输出。
需要考虑在每个数据位之间插入起始位、数据位和停止位。
3.数据接收:实现UART协议的接收部分,包括数据线(RX)的输入。
需要根据起始位和停止位来识别接收到的数据位。
4.数据缓冲:为了在发送和接收之间进行数据缓冲,可以设计一个FIFO(先进先出)缓冲区,以处理数据的流动。
5.状态机:使用状态机来控制数据发送和接收的不同阶段,包括等待起始位、传输数据位、等待停止位等。
6.校验:可以实现奇偶校验或其他校验机制,以确保数据的完整性和准确性。
7.中断:可以实现中断机制,以便在数据传输完成或接收到特定数据时触发相应的处理。
8.测试和仿真:使用Verilog仿真工具(如ModelSim、VCS等)对设计进行仿真和测试,确保UART协议的正确性和稳定性。
9.综合和实现:将Verilog代码综合到目标FPGA或ASIC平台上,生成实际的硬件电路。
Verilog UART协议的实现涉及到许多细节和硬件设计知识,需要具备硬件设计和Verilog编程的技能。
通常,有现成的UART IP核可以在设计中使用,从而简化实现的过程。
UART串⼝代码uart_cfg=XUartPs_LookupConfig(uart_id);XUartPs_CfgInitialize(&uart,uart_cfg,uart_cfg->BaseAddress);XUartPs_SetBaudRate(&uart,baud_rate); 设置波特率XUartPs_SetHandler(&uart,(XUartPs_Handler)uart1handler,&uart); UART有多种中断,触发对应的中断intrmask=XUARTPS_IXR_TOUT;XUartPs_SetInterruptMask(&uart,intrmask);XUartPs_SetOperMode(&uart,normal_mode);XUartPs_SetRecvTimeout(&uart,8); 间隔4*8共32个bit的时间内没有接收到数据触发XUartPs_Recv(&uart,recvbuf,32);// 读出32个数据到 recbufvoid XUartPs_SetFifoThreshold(XUartPs *InstancePtr, u8 TriggerLevel) 设置阈值,FIFO内数据达到阈值触发(产⽣事件)/** main.c** Created on: 2022年2⽉23⽇* Author: lht*/#include "stdio.h"#include "xparameters.h"#include "xscugic.h"#include "xuartps.h"#include "xil_exception.h"#include "xil_printf.h"#include <stdlib.h>#define uart_id XPAR_PS7_UART_1_DEVICE_ID#define baud_rate XUARTPS_DFT_BAUDRATE#define normal_mode XUARTPS_OPER_MODE_NORMAL#define gic_id XPAR_PS7_SCUGIC_0_DEVICE_ID#define uart1_intr XPAR_PS7_UART_1_INTR#define BUFFER_SIZE 1024unsigned char recvbuf[1024];u32 RecvCnt;u32 codecnt;//static u16 recvbuf_and[512];static u8 RecvBuffer[BUFFER_SIZE];u16 *recvbuf_and=NULL;u16 data_len;u8 sum_check;u8 xor_check;int right=0;/*typedef struct st_type //柔性数组{int j;u16 recvbuf_and[];}type_a; */u32 TotalRecvCnt;u8 *RecvBufferPtr;XUartPs uart;XUartPs_Config *uart_cfg;XScuGic scugic;XScuGic_Config * gic_cfg;void init_uart();void init_intr();void uart1handler(void *CallBackRef, u32 Event,u32 EventData);int main(){printf("hi\n\r");init_intr();init_uart();while(1){//int i=0;//for(i=0;i<codecnt;i++){// xil_printf("i:%x\r\n",recvbuf_and[i]);//}}return0;}void init_uart(){// XUartPs uart;// XUartPs_Config *uart_cfg;u32 intrmask=0;//RecvBufferPtr=RecvBuffer;uart_cfg=XUartPs_LookupConfig(uart_id);XUartPs_CfgInitialize(&uart,uart_cfg,uart_cfg->BaseAddress);XUartPs_SetBaudRate(&uart,baud_rate);XUartPs_SetHandler(&uart,(XUartPs_Handler)uart1handler,&uart); intrmask=XUARTPS_IXR_TOUT|XUARTPS_IXR_RXOVR;XUartPs_SetInterruptMask(&uart,intrmask);XUartPs_SetOperMode(&uart,normal_mode);XUartPs_SetFifoThreshold(&uart,32);XUartPs_SetRecvTimeout(&uart,8);//XUartPs_Recv(&uart,recvbuf,64);//}void init_intr(){// XScuGic scugic;// XScuGic_Config * gic_cfg;gic_cfg=XScuGic_LookupConfig(gic_id);XScuGic_CfgInitialize(&scugic,gic_cfg,gic_cfg->CpuBaseAddress);Xil_ExceptionInit();Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&scugic); Xil_ExceptionEnable();XScuGic_Connect(&scugic,uart1_intr,(Xil_ExceptionHandler)XUartPs_InterruptHandler,&uart);XScuGic_Enable(&scugic,uart1_intr);}void uart1handler(void *CallBackRef, u32 Event,u32 EventData){//u32 recvcnt;if(Event==XUARTPS_EVENT_RECV_DATA){/* 清除中断标志 */XUartPs_WriteReg(uart_cfg->BaseAddress, XUARTPS_ISR_OFFSET, XUARTPS_IXR_RXOVR) ;XUartPs_Recv(&uart, RecvBuffer, 500) ; //设置为最⼤,这样有多少就会接收多少//RecvCnt += EventData ;//RecvBufferPtr += EventData;xil_printf("1\r\n");}else if(Event==XUARTPS_EVENT_RECV_TOUT){int i;int j;int right=0;recvbuf_and = (u16 *) malloc(codecnt * sizeof(u16));/* 清除中断标志 */XUartPs_WriteReg(uart_cfg->BaseAddress, XUARTPS_ISR_OFFSET, XUARTPS_IXR_TOUT) ;XUartPs_Recv(&uart, RecvBuffer, 500) ;//RecvCnt += EventData ;if((RecvBuffer[0]==0x12) && (RecvBuffer[1]==0x34)){data_len=(((RecvBuffer[2])&0x00ff)<<8|(RecvBuffer[3]&0x00ff));for(j=2;j<(data_len+6);j++){sum_check+=RecvBuffer[j];xor_check^=RecvBuffer[j];//printf("RecvBuffer[j]=%d\n\r",RecvBuffer[j]);//printf("j=%d\n\r",j);}/*printf("RecvBuffer[1]=%d\n\r",RecvBuffer[1]);printf("RecvBuffer[2]=%d\n\r",RecvBuffer[2]);printf("RecvBuffer[3]=%d\n\r",RecvBuffer[3]);printf("DATA_LEN=%d\n\r",data_len);printf("SUM_CHECK=%d\n\r",sum_check);printf("XOR_CHECK=%d\n\r",xor_check);printf("RecvBuffer[4+data_len]=%d\n\r",RecvBuffer[4+data_len]);printf("RecvBuffer[5+data_len]=%d\n\r",RecvBuffer[5+data_len]);printf("RecvBuffer[6+data_len]=%d\n\r",RecvBuffer[6+data_len]);printf("RecvBuffer[7+data_len]=%d\n\r",RecvBuffer[7+data_len]);*/if((sum_check==RecvBuffer[4+data_len]) && (xor_check==RecvBuffer[5+data_len])){right=1;printf("right=%d\n\r",right);right=0;if((RecvBuffer[6+data_len]==0x43) && (RecvBuffer[7+data_len]==0x21) ){codecnt=data_len/2;for(i=2;i<codecnt+2;i++){recvbuf_and[i-2]=(((RecvBuffer[2*i])&0x00ff)<<8|(RecvBuffer[2*i+1]&0x00ff));printf("recvbuf_and[i]=%d\n\r",recvbuf_and[i-2]);printf("i=%d\n\r",i-2);}}else{printf("codecnt=%d\n\r",codecnt);}}else{printf("right=%d\n\r",right);}}else {for(i=0;i<BUFFER_SIZE;i++){RecvBuffer[i]=0;}}xil_printf("2:%d\r\n",RecvCnt);xil_printf("2:%d\r\n",data_len);sum_check=0;xor_check=0;RecvCnt=0;data_len=0;//recvcnt=EventData;// if(recvcnt==8 && recvbuf[0]==0x55 && recvbuf[1]==0x55){ // printf("recved frame1\n\r");//}}}。
目录串口接收模块的verilog设计 (1)1现场可编程门阵列FPGA (2)2 Verilog HDL简介 (3)3串行通信系统 (3)3.1串行通信概念 (4)3.3 RS-232总线 (5)3.3.1 RS-232接口特性 (5)3.3串行通信接口组成 (6)3.4 通信协议 (7)3.5系统整体结构 (8)4 UART简介 (9)4.1 接收模块功能设计描述 (11)4.2波特率模块 (16)4.3验证 (17)5总结体会 (18)参考文献 (19)串口接收模块的verilog设计摘要:UART(即Universal Asynchronous Receiver Transmitter)是数据通信及控制中广泛使用的一种全双工串行数据传输协议。
本设计基于 FPGA器件实现对UART的波特率产生器、UART发送器和接收器及其整合电路的模块化设计,采用Verilog HDL语言对三个功能模块进行硬件描述。
通过串口调试助手进行验证,其结果完全符合UART协议的要求和预期的结果。
关键词:UART FPGA Verilog HDL 验证1现场可编程门阵列FPGA20世纪80年代中期,FPGA刚出现时,大部分用来实现粘合逻辑、中等复杂度的状态机和相对有限的数据处理任务。
在20世纪90年代早期,FPGA的规模和复杂度开始增加,那时它们的主要场所在通信和网络领域。
到了20世纪90年代末,FPGA在消费、汽车和工业领域的应用经历了爆炸式增长。
21世纪早期,已经可以买到数百万容量的高性能FPGA。
今天FPGA几乎可以用来实现任何东西,包括通信设备和软件定义无线电,雷达、影像和其它数字信号处理的应用,直至包含硬件和软件的片上系统。
FPGA(field-Programmable Gate Array),即现场可编程门阵列,它是在PAL、GAL、CPLD等可编程器件的基础上进一步发展的产物。
它是作为专用集成电路(ASIC)领域中的一种半定制电路出现的,既解决了制定电路的不足,又克服了原有可编程器件门电路数有限的缺点。
ART波形图如下图所示,最简单的一种形式:1位起始位(低),8位数据位,1位停止位(高)。
UART没有同步时钟,按照约定好的时间(波特率)进行间隔采样。
1,UART 数据发送模块只需按照上图所示的波形图把需要传输的数据输出即可,由于发射时钟和系统时钟同步,我们使用一个计数器对50MHz系统时钟分频产生发送数据时钟。
建立一个发送状态机,共四种状态,在每个状态输出相应的信息。
本模块为了方便调试,输出一个闪烁灯,按下一个键,发送一个固定的数据。
代码如下:module UART_TX(tx,rx,sw0,sendkey0,clk,rst,led);output tx;input rx;input[3:0]sw0;input sendkey0;input clk,rst;output led;//output discribereg led;reg tx;// baud rate:38400parameter UART_CLK_DIV=50000000/38400/2;//额外的/2是因为产生时钟使用了~方式//发送数据状态机parameter sstate_idle=0;parameter sstate_begin=1;parameter sstate_sdat=2;parameter sstate_end=3;reg[2:0]curstate;//当前状态reg[7:0]senddata;//需要发送的数据reg[3:0]tosendcnt;//需要发送计数器计数reg[3:0]alsendcnt;//已经发送数据计数reg sendclk;//数据发送时钟reg[15:0]clk_divcnt; //发送时钟分频计数器reg[2:0]bitcnt;//发送数据的位选择//产生发送数据的对应时钟always@(posedge clk, negedge rst)beginif(!rst)beginsendclk<=1'b0;clk_divcnt<=0;end else beginif(clk_divcnt==UART_CLK_DIV-1)beginclk_divcnt<=0;sendclk<=~sendclk;end else beginclk_divcnt<=clk_divcnt+1;endendend//状态转换always@(posedge sendclk,negedge rst)beginif(!rst)beginalsendcnt<=0;curstate<=sstate_idle;end else begincase(curstate)sstate_idle:beginif(alsendcnt!=tosendcnt)begin // have data to sendalsendcnt<=alsendcnt+1;// count the number have sendcurstate<=sstate_begin;end else begincurstate<=sstate_idle;endendsstate_begin:begincurstate<=sstate_sdat;bitcnt<=3'b0;senddata<=sw0+8'd97;// get the data to send endsstate_sdat:beginif(bitcnt==7)begincurstate<=sstate_end;end else beginbitcnt<=bitcnt+1;endendsstate_end:begincurstate<=sstate_idle;enddefault:curstate<=sstate_idle;endcaseendend//输出数据always@(*)beginif(~rst)tx<=1;else begincase(curstate)sstate_idle:begintx<=1;endsstate_begin:begintx<=1'b0;endsstate_sdat:begintx<=senddata[bitcnt];endsstate_end:begintx<=1'b1;endendcaseendend//按下一个键发送一个数据always@(posedge sendkey0, negedge rst)beginif(!rst)begintosendcnt<=0;end else begintosendcnt<=tosendcnt+1'b1;endend// led debug usereg[31:0]ledcnt;always@(posedge clk,negedge rst)beginif(!rst)beginledcnt<=0;led<=1'b1;end else beginif(ledcnt<20000000)beginledcnt<=ledcnt+1'b1;end else beginled<=~led;ledcnt<=0;endendendendmodule2,UART数据接收模块由于不清楚何时数据到达,即时钟并不同步,使用和串口一样的时钟来判断输入数据并不适合。
这里为了稳定,在每个数据波形的中间进行采样。
代码如下:module UART_RX(tx,rx,hex1,//使用数码管显示收到的数据hex0,clk,rst,led);output tx;input rx;output[6:0]hex1,hex0;input clk,rst;output led;//output declarationreg led;reg[7:0]dispdat;assign tx=1'b1;// baud rate:38400//parameter UART_CLK_DIV=50000000/38400;//需要分频比例//状态parameter rstate_idle=0;parameter rstate_beginq=1;//是否真的有数据传输parameter rstate_begin=2;parameter rstate_rdata=3;parameter rstate_end=4;reg[2:0]curstate;reg[15:0]clkcnt;reg[2:0]bitcnt;reg[7:0]recdata;always@(posedge clk, negedge rst)beginif(!rst)begincurstate<=rstate_idle;clkcnt<=0;end else begincase(curstate)rstate_idle:beginif(~rx)begincurstate<=rstate_beginq;bitcnt<=0;clkcnt<=0;recdata<=0;end elsecurstate<=rstate_idle;endrstate_beginq:beginif(clkcnt==UART_CLK_DIV/2-1)begin//1/2是为了在每个波形的中间进行判断clkcnt<=0;if(~rx)begincurstate<=rstate_rdata;end else begincurstate<=rstate_idle;endend else beginclkcnt<=clkcnt+1'b1;endendrstate_rdata:beginif(clkcnt==UART_CLK_DIV-1)beginclkcnt<=0;recdata[bitcnt]<=rx;if(bitcnt==7)begincurstate<=rstate_end;end else beginbitcnt<=bitcnt+1'b1;endend else beginclkcnt<=clkcnt+1'b1;endendrstate_end:beginif(clkcnt==UART_CLK_DIV-1)beginclkcnt<=0;curstate<=rstate_idle;end else beginclkcnt<=clkcnt+1'b1;endenddefault:curstate<=rstate_idle;endcaseendendalways@(curstate,bitcnt)begincase(curstate)rstate_rdata:beginendrstate_end:begindispdat<=recdata;/////////////debug enddefault:beginendendcaseend// led debug usereg[31:0]ledcnt;always@(posedge clk,negedge rst)beginif(!rst)beginledcnt<=0;led<=1'b1;end else beginif(ledcnt<20000000)beginledcnt<=ledcnt+1'b1;end else beginled<=~led;ledcnt<=0;endendendSEG7 seg1(.oSEG(hex1),.Sel(dispdat[7:4]));SEG7 seg0(.oSEG(hex0),.Sel(dispdat[3:0]));endmodule。