串口通信FPGA实现
- 格式:doc
- 大小:459.34 KB
- 文档页数:12
带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它包含了一个有限状态机。
FPGA实现SPIFPGA(Field Programmable Gate Array)是一种可编程逻辑器件,可以实现不同的数字电路功能。
SPI(Serial Peripheral Interface)是一种同步串行通信协议,常用于连接外围设备和主控制器。
在本文中,将介绍如何使用FPGA实现SPI。
1.确定硬件资源:首先,需要确定FPGA中可用的IO资源。
SPI需要至少4个IO口,分别是主设备的时钟引脚(SCK),主设备输出的数据引脚(MOSI),主设备输入的数据引脚(MISO)和片选引脚(SS)。
根据所用的FPGA型号,可以查找对应的引脚定义。
2.确定SPI时序:SPI的时序是非常重要的,不同设备可能有不同的时序规范。
一般情况下,SPI的时序包括时钟下降沿数据采样、时钟上升沿数据输出等。
SPI的时序图可以在设备的数据手册中找到。
3. 编写SPI控制器:SPI控制器可以用硬件描述语言如VHDL或Verilog编写。
控制器的功能包括生成时钟、控制数据的发送和接收、以及处理片选信号。
a.时钟生成:SPI通信需要一个时钟信号来驱动数据的传输。
可以通过计数器模块来生成控制器的时钟信号。
计数器的频率一般是SPI时钟频率的若干倍。
b. 数据发送:对于主设备(Master),要发送数据给外设,可以使用移位寄存器(Shift Register)来存储要发送的数据。
可以使用计数器生成移位寄存器的时钟信号,通过串行输入数据,并在时钟的上升沿时将数据发送到MOSI引脚。
c.数据接收:对于主设备,要接收外设发送的数据,可以使用另一个移位寄存器来接收MISO引脚传输的数据。
可以使用计数器生成移位寄存器的时钟信号,通过MISO引脚接收数据,并在时钟的下降沿时将数据存储到接收寄存器。
d.片选控制:SPI通信需要一个片选信号来选择要与主设备通信的外设。
可以通过一个时序控制器实现片选信号的生成。
在与一些外设通信时,使能片选信号,否则禁用片选信号。
module chuankou_1(clk,rxd,dig_bit,dig_seg);input clk,rxd;output[1:0]dig_bit;output[7:0]dig_seg;reg[1:0]dig_bit;reg[7:0]dig_seg;reg flag_bit;//数码管位标志//使用9600bps的波特率,使用其16倍频进行读取;reg clk_rxd;//16倍频,对于50MHZ的时钟,其分频系数为8'hA1;reg[7:0]cnt_clk_rxd;reg[3:0]cnt_read;//对16倍频的周期信号进行计数,每16次清零,并开始读取下一位数据;reg r1,r2,r3;//对每一位数读三次,以保证准确率;reg[7:0]data;//用来逐位接收串口的数据;reg[3:0]cnt_bit;//对data进行位选择;reg[2:0]state;reg clk_led;//数码管显示使用reg[11:0]cnt_clk_led;reg[3:0]num;//产生16倍频的时钟;always@(posedge clk)beginif(cnt_clk_rxd>=8'ha1)begincnt_clk_rxd<=8'd0;clk_rxd<=~clk_rxd;endelsecnt_clk_rxd<=cnt_clk_rxd+1'b1;endparameter IDLE=3'b000,READ=3'b001,WRITE=3'b010;always@(posedge clk_rxd)begincase(state)IDLE:beginif(rxd==1'b1)beginstate<=IDLE;cnt_read<=4'd0;endelsebeginif(cnt_read<4'd15)begincnt_read<=cnt_read+1'b1;state<=IDLE;endelsebegincnt_read<=4'd0;state<=READ;endendendREAD:beginif(cnt_bit<=4'd7)beginif(cnt_read==4'd4)begincnt_read<=cnt_read+1'b1;r1<=rxd;state<=READ;endelse if(cnt_read==4'd9)begincnt_read<=cnt_read+1'b1;r2<=rxd;state<=READ;endelse if(cnt_read==4'd12)begincnt_read<=cnt_read+1'b1;r3<=rxd;state<=READ;endelse if(cnt_read==4'd15)beginif((r1==r2)&&(r2==r3)&&(r1==r3))begindata[cnt_bit]<=r3;//取值;state<=READ;cnt_read<=4'd0;cnt_bit<=cnt_bit+1'b1;endelsebegincnt_bit<=4'd0;state<=IDLE;cnt_read<=4'd0;endendelsebeginstate<=READ;cnt_read<=cnt_read+1'b1;endendelsebeginstate<=WRITE;cnt_bit<=4'd0;endendWRITE://此状态暂时未用;beginstate<=IDLE;enddefault:state<=IDLE;endcaseend//以下是数码管显示部分;parameter segcode0=8'b1100_0000;parameter segcode1=8'b1111_1001;parameter segcode2=8'b1010_0100;parameter segcode3=8'b1011_0000;parameter segcode4=8'b1001_1001;parameter segcode5=8'b1001_0010;parameter segcode6=8'b1000_0010;parameter segcode7=8'b1111_1000;parameter segcode8=8'b1000_0000;parameter segcode9=8'b1001_0000;parameter segcodea=8'h88;parameter segcodeb=8'h83;parameter segcodec=8'hc6;parameter segcoded=8'ha1;parameter segcodee=8'h86;parameter segcodef=8'h8e;always@(posedge clk)beginif(cnt_clk_led>=12'h9c3)begincnt_clk_led<=12'd0;clk_led<=~clk_led;endelsecnt_clk_led<=cnt_clk_led+1'b1;endalways@(posedge clk_led)beginflag_bit<=flag_bit+1'b1;case(flag_bit)1'b0:dig_bit<=2'b10;1'b1:dig_bit<=2'b01;endcaseendalways@(posedge clk_led)begincase(flag_bit)1'b0:num<=data[3:0];1'b1:num<=data[7:4];endcaseendalways@(num)begincase(num)4'd0:dig_seg<=segcode0;4'd1:dig_seg<=segcode1;4'd2:dig_seg<=segcode2;4'd3:dig_seg<=segcode3;4'd4:dig_seg<=segcode4;4'd5:dig_seg<=segcode5;4'd6:dig_seg<=segcode6;4'd7:dig_seg<=segcode7;4'd8:dig_seg<=segcode8;4'd9:dig_seg<=segcode9;4'd10:dig_seg<=segcodea;4'd11:dig_seg<=segcodeb;4'd12:dig_seg<=segcodec;4'd13:dig_seg<=segcoded;4'd14:dig_seg<=segcodee;4'd15:dig_seg<=segcodef;endcaseendendmodule。
FPGA和单片机串行通信接口的实现FPGA(Field-Programmable Gate Array)和单片机(Microcontroller)是两种常用的数字电子设备,它们在串行通信接口方面有不同的实现方式。
首先,我们需要了解串行通信是一种将数据以位的形式逐个传输的通信方式。
常见的串行通信协议包括UART(Universal Asynchronous Receiver/Transmitter)、SPI(Serial Peripheral Interface)和I2C (Inter-Integrated Circuit)等。
对于FPGA和单片机之间的串行通信,我们可以基于以下几种方式进行实现:1. UART:UART是一种常见的串行通信协议,可以实现全双工的通信。
在FPGA和单片机之间建立UART通信,需要在FPGA中实现UART模块,并将其与单片机的UART接口连接。
在FPGA中,我们可以使用硬件语言(如Verilog或VHDL)来实现UART模块,该模块负责将FPGA内部的数据通过UART协议进行封装和解封装。
单片机与FPGA之间通过TX(发送)和RX (接收)引脚建立连接。
单片机可以通过串口发送数据给FPGA,FPGA接收到数据后进行处理,然后再通过串口将处理后的数据发送给单片机。
2.SPI:SPI是一种用于片上外设之间通信的串行通信协议,常用于FPGA与外部设备(例如传感器、显示器等)之间的通信。
在FPGA和单片机之间建立SPI通信,需要在FPGA中实现SPI控制器,并将其与单片机的SPI接口连接。
FPGA通过把数据写入SPI发送缓冲区或从SPI接收缓冲区读取数据来实现与单片机的通信。
单片机通过控制SPI接口的时钟、数据和使能信号来与FPGA进行数据传输。
3.I2C:I2C是一种双线制串行总线,常用于连接多个设备的系统,例如FPGA、单片机和其他外部设备之间的通信。
在FPGA和单片机之间建立I2C通信,需要在FPGA中实现I2C控制器,并将其与单片机的I2C接口连接。
FPGA串⼝UART学习笔记1串⼝通信串⼝通信1、串⼝简介串⾏接⼝,COM接⼝,只需要两根线就能实现两台设备之间的通信。
UART指的是异步的串⾏接⼝,通⽤异步收发。
标准常⽤的是RS-232标准接⼝现在电脑上没有串⼝了,所以使⽤的是USB转串⼝芯⽚,CH340芯⽚。
换句话说,只需要两根数据线UART_RXD和UART_TXD,就能完成两台设备之间的通信。
2、串⼝时序两根数据线各⾃独⽴互不影响,⼆者的时序是相同的。
不同之处是UART_RXD是主机MASTER发送给从机SLAVE,UART_TXD是SLAVE 发送给MASTER。
由于两根线的时序完全相同且独⽴,下⾯以UART_TXD为例。
空闲状态时,UART_TXD⼀直拉⾼,当要传输数据之前,拉低⼀个数据位,此后开始传输数据。
数据之后有⼀个校验位,校验位之后是停⽌位,停⽌位之后进⼊下⼀个传输周期。
⾄此,完成了⼀个数据包的传输。
注意:(1)、传输的数据是从低⽐特位开始传,⽐如101010,接受端的接受顺序是010101。
(2)、传输数据的位数是MASTER与SLAVE约定好的,可以是4、5、6、7、8位,时序图中是以⼋位为例。
(3)、校验位⼀般是奇偶检验。
当然,也可以选择没有检验位,前提是MASTER与SLAVE约定好,在SLAVE解析接收到的数据的时候,不安排校验位的解析。
(4)、停⽌位,停⽌位是保证两段传输之间⼀定要有间隔。
两段传输之间可以没有空闲时间,但是,停⽌位⼀定要有。
3、时间的问题从时序图上可以看出,串⼝的发送和接受是没有时钟的,换句话说,这是⼀个异步时序。
那么如何确定每个位所需要的时间就尤为重要。
这个问题的要点是波特率,每秒发送/接受单位的个数。
我们使⽤的串⼝是以⽐特为单位,所以这⾥波特率与我们的⽐特率相同。
常见的波特率的数值有9600,19200,38400,57600,115200等。
以9600为例,表⽰⼀秒钟发送/接受9600个⽐特。
FPGA设计与应用串口通信实验报告班级:1105103姓名:苏林效学号:1110510321日期:2014年10月29日实验性质:验证性实验类型:必做开课单位:电信院学时:2学时一、实验目的1、了解串口通信的基本原理;2、掌握锁相环的基本原理和使用方法;3、掌握起始位和停止位的含义及实现方法;4、掌握VHDL状态机的基本使用方法;5、掌握基本的接口设计和调试技巧;二、实验准备2.1 串口通信原理(1分)串口通信是以字节为单位,按位传输数据和接收数据的。
先看一下发送数据:串行线缆的两端事先约定好串行传输的参数(传输速度、传输格式等),之后进行传输,①当没有数据传输的时候,发送端向数据线上发送“1”;②当要传输数据时,发送端先发送一个“0”来表示要传输数据了,这样当接收端检测到“0”便可以知道有数据到来了;③开始传输后,数据以约定的速度和格式传输;④每次传输完成一个字节之后,都在其后发送一个停止位“1”。
这样,发送数据就结束了。
再来看接收数据,初始状态是等待状态,接收的一直是“1”,当检测到0时进入检验状态,在检验状态下如果再检测到0(一般采用过采样)则进入接收数据状态,当接收完8位比特数后判断是否有停止位,如果有则此字节接收结束,重新进入等待状态准备接受下一字节。
2.2 锁相环的基本原理(1分)锁相环是以锁定输入载波信号的相位为目标的一种载波环实现形式。
它由三部分组成:鉴相器(PD)、环路滤波器(LPF)和压控振荡器(VCO)。
如上图。
锁相环的工作原理:压控振荡器的输出经过采集并分频,后与基准信号同时输入鉴相器。
鉴相器通过比较上述两个信号的相位差,输出结果通过环路滤波器,滤除噪声和高频分量,然后输出一个直流脉冲电压,用它来控制VCO ,使它的频率改变,使()lim 0c p t θ=。
经过较短时间后,VCO 的输出稳定于某一值。
环路一旦进入锁定状态后,压控振荡器的输出信号与环路的输入信号(参考信号)相位时刻保持一致。
三、代码及测试3.1程序及分析(2分)顶层原理图如上所示,主要包括三个模块,锁相环(输出57.69MHz )、分频(输出接受时的10倍采样速率和数码管显示速率)、接受(按57.6kbps 接受从串口发来的数据)。
分频模块代码: LIBRARY IEEE;USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL;ENTITY DIV ISPORT(CLK : IN STD_LOGIC; --锁相环输出时钟57.69MCOUT1 : OUT STD_LOGIC;--分频器输出采样时钟576kCOUT2 : OUT STD_LOGIC);--分频器产产生扫描时钟100hzEND DIV;ARCHITECTURE BEHAV OF DIV ISBEGINPROCESS(CLK)V ARIABLE COUT1_NUM : STD_LOGIC_VECTOR(5 DOWNTO 0):=(OTHERS => '0'); --输出时钟1 计数器V ARIABLE COUT2_NUM : STD_LOGIC_VECTOR(18 DOWNTO 0):=(OTHERS => '0'); --输出时钟2 计数?V ARIABLE COUT1_1 : STD_LOGIC;V ARIABLE COUT2_2 : STD_LOGIC;BEGINIF RISING_EDGE(CLK) THENIF COUT1_NUM >= 49 THENCOUT1_1 := NOT COUT1_1;COUT1_NUM := (OTHERS => '0');ELSECOUT1_NUM := COUT1_NUM + 1;END IF;IF COUT2_NUM >= 288450 THENCOUT2_2 := NOT COUT2_2;COUT2_NUM := (OTHERS => '0');ELSECOUT2_NUM := COUT2_NUM + 1;END IF;COUT1 <= COUT1_1;COUT2 <= COUT2_2;END IF;END PROCESS;END BEHAV;接受模块代码:LIBRARY IEEE;USE IEEE.STD_LOGIC_1164.ALL;USE IEEE.STD_LOGIC_UNSIGNED.ALL;ENTITY COM_REC ISPORT(CLK_10 : IN STD_LOGIC;--输入时钟,按10倍采样,是rs232串口速率的10倍CLK_DIS : IN STD_LOGIC;-- DISPLAY CLKINNUM : IN STD_LOGIC;--输入的值LED_EN : OUT STD_LOGIC_VECTOR(3 DOWNTO 0);LED : OUT STD_LOGIC_VECTOR(6 DOWNTO 0)); END;ARCHITECTURE BEHAV OF COM_REC ISTYPE STATE IS (SPARE, READY, REC, STOP);--定义接受4状态SIGNAL TR_INNUM : STD_LOGIC_VECTOR(7 DOWNTO 0); --接收到8位数SIGNAL LED_L : STD_LOGIC_VECTOR(6 DOWNTO 0);--控制两位数码管显示SIGNAL LED_H : STD_LOGIC_VECTOR(6 DOWNTO 0);BEGINPROCESS(CLK_10)V ARIABLE TR_INNUM_NUM : INTEGER RANGE 0 TO 15 := 0; --对接受到得每一位数计数V ARIABLE TRUE : STD_LOGIC := '0';V ARIABLE CURRENT_STATE : STATE := SPARE;--当前状态V ARIABLE SAM_NUM_0 : INTEGER RANGE 0 TO 15 := 0;--对采样次数计数V ARIABLE SAM_NUM_1 : INTEGER RANGE 0 TO 15 := 0;BEGINIF CLK_10'EVENT AND CLK_10 = '1' THENIF CURRENT_STATE = SPARE THENIF INNUM = '0' THENSAM_NUM_0 := SAM_NUM_0 + 1;--第一次采样到零点,即改变状态CURRENT_STATE := READY;ELSECURRENT_STATE := SPARE;END IF;ELSIF CURRENT_STA TE = READY THENIF INNUM = '0' THENSAM_NUM_0 := SAM_NUM_0 + 1;ELSESAM_NUM_1 := SAM_NUM_1 + 1;END IF;IF (SAM_NUM_0 + SAM_NUM_1) >= 10 THEN--判断是否真开端IF SAM_NUM_0 >= 6 THENCURRENT_STATE := REC;SAM_NUM_0 := 0;SAM_NUM_1 := 0;ELSECURRENT_STATE := SPARE;END IF;ELSECURRENT_STATE := READY;END IF;ELSIF CURRENT_STA TE = REC THENIF INNUM = '0' THENSAM_NUM_0 := SAM_NUM_0 + 1;ELSESAM_NUM_1 := SAM_NUM_1 + 1;END IF;IF (SAM_NUM_0 + SAM_NUM_1) >= 10 THEN --接受数据判断IF SAM_NUM_0 >= 6 THENTR_INNUM(TR_INNUM_NUM) <= '0';ELSETR_INNUM(TR_INNUM_NUM) <= '1';END IF;SAM_NUM_0 := 0;SAM_NUM_1 := 0;TR_INNUM_NUM := TR_INNUM_NUM + 1;IF TR_INNUM_NUM >= 8 THENCURRENT_STATE := STOP;TR_INNUM_NUM := 0;ELSECURRENT_STATE := REC;END IF;ELSECURRENT_STATE := REC;END IF;ELSIF CURRENT_STA TE = STOP THENIF INNUM = '1' THENSAM_NUM_1 := SAM_NUM_1 + 1;ELSESAM_NUM_0 := SAM_NUM_0 + 1;END IF;IF (SAM_NUM_0 + SAM_NUM_1) >= 10 THEN IF SAM_NUM_1 >= 6 THENTRUE := '1';CURRENT_STATE := SPARE;END IF;SAM_NUM_0 := 0;SAM_NUM_1 := 0;ELSECURRENT_STATE := STOP;END IF;ELSECURRENT_STATE := SPARE;END IF;END PROCESS;LED_H <="0111111" when TR_INNUM(7 downto 4) = 0 else "0000110" when TR_INNUM(7 downto 4) = 1 else"1011011" when TR_INNUM(7 downto 4) = 2 else"1001111" when TR_INNUM(7 downto 4) = 3 else"1100110" when TR_INNUM(7 downto 4) = 4 else"1101101" when TR_INNUM(7 downto 4) = 5 else"1111101" when TR_INNUM(7 downto 4) = 6 else"0000111" when TR_INNUM(7 downto 4) = 7 else"1111111" when TR_INNUM(7 downto 4) = 8 else"1101111" when TR_INNUM(7 downto 4) = 9 else"1110111" when TR_INNUM(7 downto 4) = 10 else"1111100" when TR_INNUM(7 downto 4) = 11 else"0111001" when TR_INNUM(7 downto 4) = 12 else"1011110" when TR_INNUM(7 downto 4) = 13 else"1111001" when TR_INNUM(7 downto 4) = 14 else"1110001";LED_L <="0111111" when TR_INNUM(3 downto 0) = 0 else "0000110" when TR_INNUM(3 downto 0) = 1 else"1011011" when TR_INNUM(3 downto 0) = 2 else"1001111" when TR_INNUM(3 downto 0) = 3 else"1100110" when TR_INNUM(3 downto 0) = 4 else"1101101" when TR_INNUM(3 downto 0) = 5 else"1111101" when TR_INNUM(3 downto 0) = 6 else"0000111" when TR_INNUM(3 downto 0) = 7 else"1111111" when TR_INNUM(3 downto 0) = 8 else"1101111" when TR_INNUM(3 downto 0) = 9 else"1110111" when TR_INNUM(3 downto 0) = 10 else"1111100" when TR_INNUM(3 downto 0) = 11 else"0111001" when TR_INNUM(3 downto 0) = 12 else"1011110" when TR_INNUM(3 downto 0) = 13 else"1111001" when TR_INNUM(3 downto 0) = 14 else"1110001";PROCESS(CLK_DIS)V ARIABLE A : STD_LOGIC;BEGINIF RISING_EDGE(CLK_DIS) THENIF A = '0' THENLED <= LED_L;LED_EN <= "0001";A := '1';ELSELED <= LED_H;LED_EN <= "0010";A := '0';END IF;END IF;END PROCESS;END BEHAV;3.2 SignalTap II波形测试结果(1分)此实验要求发送16进制的21H,实际发送的是十进制的33,而数码管却显示21,高四位(0010)、低四位(0001)。