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 总体框图 |
控制模块
主要实现的功能是:判断从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 发送接收流程图 |
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;
在串行通信中,无论发送或接收,都必须有时钟脉冲信号对所传送的数据进行定位和同步控制,设计中采用的时钟频率是波特率的两倍(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 发送控制字过程 |
图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读数据的过程 |