HighSpeedLogic专题: RS232串口通信

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

HighSpeedLogic专题: RS232串口通信

图1 总体框图

 

   设计包括三部分:

1、通过向I/O端口发送高低电平以达到控制外部硬件的要求。

2、完成芯片内部逻辑的变化。

3、将需要的数据先存起来(一般采用内部或外部FIFO),然后通过串口将数据发送到PC,PC将接收的数据进行处理和分析。串口采用标准的RS-232协议,主要参数的选择:波特率28800bit/s、8位有效位、无奇偶校验位、1位停止位。

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

 

HighSpeedLogic专题: RS232串口通信

图2 状态机变换

设计中需要注意的问题
   波特率的选择对于串口通信是很重要的,波特率不应太大,这样数据才会更稳定。整个发送接收过程中起始位的判别和发送是数据传输的前提。为了避免误码的产生,在FPGA设计中的串行输入和输出端口都应该加上一个数据锁存器。
仿真结果
   clk是时钟信号(57600bit/s);start_xmit是开始发送标志位;sin是串行输入;datain是并行输出;read_bit是接收结束标志位;xmit_bit是发送结束标志位;sout是串行输出;dataout是并行输出;rcv_bit是接收位数寄存器。发送接收模块主要完成把从sin端口接收的串行数据变为并行数据送给dataout;把并行数据datain变成串行数据通过sout端口串行发送。

分频模块

library IEEE;

use IEEE.STD_LOGIC_1164.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 issignal clk1,clk2    : std_logic;   

signal s1,s2    : integer range 0 to 53; 

beginprocess(clk)beginif    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)beginif    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.6864MHz的时钟进行64分频变为57600波特作为其他模块的时钟基准。具体实现时采用一个6位计数器,将计数器的溢出作为时钟的输出即可实现整数分频。
发送接收模块
   此模块是整个设计的核心部分。设计流程如图2所示。

HighSpeedLogic专题: RS232串口通信

图3 发送接收流程图

library IEEE;use IEEE.std_logic_1164.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' thencurState <= 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) beginif 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;  

HighSpeedLogic专题: RS232串口通信

 

   接收:判断接收的串行数据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_1164.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 istype 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读写状态的控制。

HighSpeedLogic专题: RS232串口通信

图5 发送控制字过程

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

HighSpeedLogic专题: RS232串口通信

图6 从FIFO读数据的过程