视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
基于FPGA的串口通信
2025-09-30 23:23:11 责编:小OO
文档
一.设计要求:

1.掌握FPGA的设计与使用。

2.基于FPGA实现与PC机的串口通信。

二.设计步骤:

1.用VHDL语言设计逻辑电路,再通过QUARTUS II 9.1软件,将各个模块的电路封装成器件,在顶层设计中通过连线,完成整个系统的设计。

串行通信即串行数据传输,实现FPGA与PC的串行通信在实际中,特别是在FPGA的调试中有着很重要的应用。调试过程一般是先进行软件编程仿真,然后将程序下载到芯片中验证设计的正确性,目前还没有更好的工具可以在下载后实时地对FPGA的工作情况和数据进行分析。通过串行通信,可以向FPGA发控制命令让其执行相应的操作,同时把需要的数据通过串口发到PC上进行相应的数据处理和分析,以此来判断FPGA是否按设计要求工作。这样给FPGA的调试带来了很大方便,在不需要DSP等其他额外的硬件条件下,只通过串口就可以完成对FPGA的调试。本文采用QuartusⅡ3.0开发平台,使用Altera公司的FPGA,设计实现了与PC的串行通信。

总体设计

主要设计思想:PC向串口发送命令,FPGA通过判断接收的控制字执行相应的操作,总体框图如图1所示。

图1 总体框图

设计包括三部分:1、通过向I/O端口发送高低电平以达到控制外部硬件的要求。2、完成芯片内部逻辑的变化。3、将需要的数据先存起来(一般采用内部或外部FIFO),然后通过串口将数据发送到PC,PC将接收的数据进行处理和分析。串口采用标准的RS-232协议,主要参数的选择:波特率28800bit/s、8位有效位、无奇偶校验位、1位停止位。

控制模块

主要实现的功能是:判断从PC接收的数据,根据预先设计的逻辑进行相应的状态转换。例如:给端口预置一个状态;送开始发送的标志位,送准备发送的数据;给DDS送配置信号,控制FIFO的读写。程序中状态机设计如图3所示。

图3 状态机变换

设计中需要注意的问题

波特率的选择对于串口通信是很重要的,波特率不应太大,这样数据才会更稳定。整个发送接收过程中起始位的判别和发送是数据传输的前提。为了避免误码的产生,在FPGA设计中的串行输入和输出端口都应该加上一个数据锁存器。

仿真结果

clk是时钟信号(57600 bit/s);start_xmit是开始发送标志位;sin是串行输入;datain是并行输出;read_bit是接收结束标志位;xmit_bit是发送结束标志位;sout是串行输出;dataout是并行输出;rcv_bit 是接收位数寄存器。发送接收模块主要完成把从sin端口接收的串行数据变为并行数据送给dataout;把并行数据datain变成串行数据通过sout端口串行发送。

分频模块

library IEEE;

use IEEE.STD_LOGIC_11.ALL;

use IEEE.STD_LOGIC_arith.ALL;

use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity Clk_DIV is

 port (clk           : in  std_logic;

       CLK_O         : out  std_logic  );

end Clk_DIV;

architecture Clk_DIV_arch  of  Clk_DIV is

signal clk1,clk2    : std_logic;   

signal s1,s2    : integer range 0 to 53; 

begin

process(clk)

begin

if    rising_edge(clk)    then

if s1 < 53 then

s1<= s1+1;

            else  

s1<=0;

            end if;

if s1 < 28 then

clk1 <= '1';

            else

clk1 <= '0';

            end if;

   end if;

end process;

process(clk)

begin

if    falling_edge(clk)    then

if s2 < 53 then

s2<= s2+1;

            else  

s2<=0;

            end if;

if s2 < 28 then

clk2 <= '1';

            else

clk2 <= '0';

            end if;

   end if;

end process;

CLK_O <=clk1 or clk2;

end Clk_DIV_arch;

设计中需要将3.68MHz的时钟进行分频变为57600 波特作为其他模块的时钟基准。具体实现时采用一个6位计数器,将计数器的溢出作为时钟的输出即可实现整数分频。

发送接收模块

此模块是整个设计的核心部分。设计流程如图2所示。

图2 发送接收流程图

library IEEE;

use IEEE.std_logic_11.all;

use IEEE.std_logic_arith.all;

use IEEE.std_logic_unsigned.all;

entity UART_RX is

 port (

       reset_n      : in std_logic;

       clk           : in std_logic;

  RD_x        : in std_logic;                        RD_x,接收数据线

   dout         : out std_logic_vector(7 downto 0);    模块接收到得1字节数据

       dav          : out std_logic                        传输成功应答

 );

end UART_RX;

architecture UART_RX_arch of UART_RX is

 type UART_RX_STATE_TYPE is (WAIT_START, DATA, STOP);

 signal curState    : UART_RX_STATE_TYPE;                      接收状态机状态

 signal bits     : std_logic_vector(7 downto 0);               接收数据暂存

  signal smpCnt    : integer range 0 to 7;                      8次采样计数

 signal bitCnt    : integer range 0 to 15;                     接收位数计数

begin

 process(reset_n, clk)

 begin

 if reset_n = '0' then

curState <= WAIT_START;

bits <= (others => '0');

smpCnt <= 0;

bitCnt <= 0;

   elsif  rising_edge(clk)  then

     case curState is

          when WAIT_START =>

               if RD_x = '0' then

                   if smpCnt = 3 then

                   curState <= DATA; 3次采样低电平证明起始位,下一个状态接收数据

                   smpCnt <= 0;           

                   else

                   curState <= WAIT_START;

                   smpCnt <= smpCnt + 1;

                   end if;

               else

                   curState <= WAIT_START;

                   smpCnt <= 0;

               end if;

               bits <= (others => '0');

        bitCnt <= 0;

           when DATA =>

              if smpCnt = 7 then           如果采样八次,则保存一位数据

                   if bitCnt = 7 then      如果已经接收八位则下一个状态停止接收

                     curState <= STOP;

                   else

                     curState <= DATA;

                   end if;

                   smpCnt <= 0;

                   bits <= RD_x & bits(7 downto 1);   完成接收

                   bitCnt <= bitCnt + 1;

              else

                   curState <= DATA;

                   smpCnt <= smpCnt + 1;

                   bits <= bits;

                   bitCnt <= bitCnt;

              end if;

          when STOP =>

              if smpCnt = 7 then

                   curState <= WAIT_START;

                   smpCnt <= 0;

              else

                   curState <= STOP;

                   smpCnt <= smpCnt + 1;

              end if;

              bits <= bits;

              bitCnt <= 0;

          when others =>

              curState <= WAIT_START;

              bits <= (others => '0');

              smpCnt <= 0;

              bitCnt <= 0;

          end case;

end if;

end process;

dout <= bits;

 process(reset_n, clk)

 begin

if reset_n = '0' then

      dav <= '0';

      elsif rising_edge(clk) then

   if curState = STOP and smpCnt = 7 and RD_x = '1' then

             dav <= '1';                                       应答接收成功

             else

             dav <= '0';

          end if;

end if;

end process;

end UART_RX_arch; 

接收:判断接收的串行数据sin是否是连续的两个0,如果是则进入接收过程;每两个时钟周期接收1个比特的数据,依次接收到01101010,如果接收到停止位表明这个接收过程结束read_bit=1。根据串行通信协议,数据是按照先低位,后高位的顺序发送的,所以实际接收的是01010110。发送:待发送的并行数据为01010110,当start_xmit=1发送有效,进入发送过程;首先发送两个起始位0,保证长度为两个时钟周期,然后依次发送01101010,每两个时钟周期发送1比特,最后发送停止位,发送过程结束xmit_bit为1。

在串行通信中,无论发送或接收,都必须有时钟脉冲信号对所传送的数据进行定位和同步控制,设计中采用的时钟频率是波特率的两倍(57600 bit/s)。接收过程:初始状态是等待状态,当检测到0时进入检验状态,在检验状态下如果再检测到0则进入接收数据状态,当接收完8位比特数后判断是否有停止位,如果有则结束接收过程重新进入等待状态。发送过程:初始状态是等待状态,当接收到开始发送的信号则进入发送过程,先发送起始位,再发送8位比特数,每位宽度为2个周期,当一个字节发送完毕后发送一个停止位,发送结束,重新回到等待状态。

发送程序

library IEEE;

use IEEE.std_logic_11.all;

use IEEE.std_logic_arith.all;

use IEEE.std_logic_unsigned.all;

entity UART_TX is

 port (

  EN          : in std_logic;

  clk         : in std_logic; 

  TD_x        : out  std_logic;

  Data_in         : in  std_logic_vector(7 downto 0)

 );

end UART_TX;

architecture UART_TX_arch of UART_TX is

type UART_TX_STATE_TYPE is (WAIT_START, DATA, STOP);

signal curState  : UART_TX_STATE_TYPE; 

signal bits      : std_logic_vector(7 downto 0);

signal D_bit     : std_logic;

signal bitCnt    : integer range 0 to 7;

signal i         : integer range 0 to 7;

begin

process(EN, clk)

begin

               

if rising_edge(clk) then i <= i+1;

        if i=7 then

i <= 0;

        case curState  is

when WAIT_START =>

              if EN='0' then 

curState <=WAIT_START;

D_bit <='1';

              else  

curState <=DATA;

bitCnt <= 0;

bits <=Data_in;

D_bit <='0';

              end  if;  

when DATA =>

               if  bitCnt = 7  then           

curState <= STOP;

D_bit <= bits(0);

              else

curState <= DATA;

bitCnt <= bitCnt + 1;

D_bit <= bits(7);

bits (7 downto 1)<= bits(6 downto 0);

              end if;

when STOP =>

              if EN='1' then 

curState <=WAIT_START;

D_bit <='1';

              else  

curState <=STOP;

bits <=Data_in;

D_bit <='1';

              end  if;

when others =>null;

          end case;

          end  if;

end  if;

TD_x <=D_bit;

end  process; 

end  UART_TX_arch; 

图5中clk是时钟信号;a是PC发来的16进制的控制字,也就是图4中的并行输出dataout; ma1cnt、ma2cnt、ma3cnt是三个寄存器;clrr是系统清零信号;ddsclr是DDS配置信号;fifo_clk,fifo_rd,fifo_wr,ram_rst是FIFO的时钟、读、写、清零信号;start_xmit是发送开始标志位;b是准备发送的数据。当接收a为1时,fifo_wr置1;当a为18时,把ma1cnt的值送到b。其他的操作类似,主要是端口的置位,FIFO读写状态的控制。

图5 发送控制字过程

 从FIFO中读写数据

图6中SER_CLOCK是系统时钟3.68MHz,sa是分频后的频率57600bit/s;SIN是串行输入;data是准备输出的数据;SOUT是串行输出;fifoclk、fifowr、fiford是FIFO的读时钟、写、读使能。读过程:读使能有效,先产生6个读时钟,但是不往SOUT发送数据,因为FIFO的前6个周期不是有效数据。然后产生一个读时钟,将FIFO的数据送到data,按照通信协议通过SOUT发送出去,发送结束再产生一个读时钟,读取FIFO的数据,进行下一次串行输出。

图6 从FIFO读数据的过程

下载本文
显示全文
专题