VHDL实现简单串口通信

目的:使用VHDL实现串口通信

使用芯片:EP1C3T144C8

实现结果:两块FPGA通信板均可作为发送方和接收方。按下复位键后,发送方根据拨码开关显示选择发送的数据,按下发送键后发送;接收方立马接收到数据并显示(特别快)

一、具体步骤

1.数码管显示

这个地方个人使用的是测试法,大家也可以查看原理图一步到位。

  1. 分清位码/段码引脚,给位码引脚0/1判断0/1谁是使能点亮?各个位码对应哪个数码管?
  2. 给段码引脚0/1判断0/1谁是使能点亮?各个段码对应数码管的哪条边?

测试结果表明0是点亮。

--------------------数据输入及显示部分-----------------------------------------------------
process(Rx_Valid,CLK)
begin
	if RESET='0' then	
		case button is
			when "000"=>Datarx<="01000000";
			when "001"=>Datarx<="01111001";
			when "010"=>Datarx<="00100100";
			when "011"=>Datarx<="00110000";
			when "100"=>Datarx<="00011001";
			when "101"=>Datarx<="00010010";
			when "110"=>Datarx<="00000010";
			when "111"=>Datarx<="01111000"; 
			when others=>null; 
		end case;
		Data00<='1'&Datarx&'0';
	else
		if rising_edge(Rx_Valid) then	
			Datarx<=Rx_Data;
		end if;
	end if;
end process;
--------------------数据输入及显示部分-----------------------------------------------------

2.波特率发生器

        波特率发生器实质是设计一个分频器,用于产生和RS232通信同步的时钟。在系统中用一个计数器来完成这个功能,系统时钟为40MHz,9600波特率时钟的周期约等于4167个系统时钟周期,则需要计数器为4167时输出高电平,同时令计数器为0,计数器为其他值时输出时钟保持低电平不变。由于接收方隔16个接收时钟接收一次,所以接受方的频率应该是发送方的16倍,即接受方需生成16倍于波特率的时钟,4167/16≈260。

--------------------产生波特率-----------------------------------------------------
process(CLK,RESET,Rx_Hold)         --时钟产生进程
			variable	ClockCount	    :	integer range 0 to 4167:=0;
			variable	ClockCount_Rx	:	integer range 0 to 260:=0;
begin				  
if (rising_edge(CLK)) then 
	ClockCount:=ClockCount+1;
	ClockCount_Rx:=ClockCount_Rx+1;
	
	  --产生9600时钟,1/40MHZ*4167=1/9600
	 if ClockCount=4167 then Clock9600<='1';ClockCount:=0;
	 else	Clock9600<='0';
	 end if;
	 --4167/16=260		  
	 if ClockCount_Rx=260 then	Clock3<='1';ClockCount_Rx:=0;
	 else Clock3<='0';
	 end if;
 end if;
end process;	
--------------------产生波特率-----------------------------------------------------

3.串口发送部分

        发送器的设计较容易,设置发送使能Send_en以及发送结束Send_over标志位,当复位键按下时,发送不使能,发送信号为"1000000000";复位键未按下时,当Clock9600上升沿到来时,发送使能,并按照拨码开关模式选择发送信号。如果发送使能,则发送起始位‘1’,并开始计数发送Bit数目,当Clock9600上升沿到来时,依次发送数据。如果发送Bit计数器为9时,则发送结束位‘0’,同时复位发送Bit计数器,并使能发送结束Send_over标志位。

变量

功能说明

Send_count

位发送计数器

Send_data

发送数据

Send_en

发送使能

Send_over

发送结束标志位

UART0_TX

发送信号

RESET

复位信号

rx_begin_key

开始发送按键

Clock9600

发送方波特率时钟

Data00

根据拨码开关所选择的发送数据


--------------------串口发送部分-----------------------------------------------------
process(Clock9600)	                                   
	--variable Send_count:integer range 0 to 9 :=0;	--位发送计数						  
begin
	if Send_en='1' then	
		Send_count<=0;UART0_TX<='1';Send_over<='1';
	elsif rising_edge(Clock9600) then
		if Send_count=9 then  
			UART0_TX<=Send_data(9);Send_over<='0';
		else 
			UART0_TX<=Send_data(Send_count);  
			Send_count<=Send_count+1; 
		end if;
	end if;
end process;

process(rx_begin_key,Clock9600,RESET) 	                                --发送激励
begin
	if RESET='0' then	 Send_en<='1';Send_data<="1000000000";
	else
		if (rx_begin_key='1') then 
			Send_en<='0'; Send_data<=Data00; --发送赋初值
		end if;
		if Send_over='0'	 then  Send_en<='1';  end if;
end if;
end process;
--------------------串口发送部分-----------------------------------------------------

4.串口接收部分

        串行数据帧和接收时钟是异步的,发送来的数据由高电平1变为低电平0可以视为一个数据帧的开始。接收器首先要捕捉起始位,然而,通信线上的噪音也极有可能使信号‘1’跳变到‘0’。所以接收器要以16倍的波特率对这种跳变进行检测,确定UART0_RX输入由‘1’到‘0’,低电平‘0’要保持8个Clock3(16倍的波特率时钟)周期,才是正常的起始位,而不是噪音引起的,其中若有依次采样得到高电平则认为起始信号无效,返回初始状态重新等待起始信号的到来。

        采到正确的起始位后,就开始接收数据,最可靠的接收应该是接收时钟的出现时刻正好对着数据位的中央,因此设置Clock3上升沿计数器m,m为24时接收第一位有效bit,此时接收时刻正对数据位的中央。接受方设置接收使能标志位Rx_Hold,当Rx_Hold为‘1’时开始接收;Rx_Hold为‘0’接收方挂起,不进行数据接收。同时设置接收结束标志位Rx_Valid,当接收到8位信号后,Rx_Valid置高。如果Rx_Hold为‘1’便开始接收,设置Clock3上升沿计数器m,当m为16的倍数时,接收寄存器接收数据,接收到9位信号后,接收bit计数器m清零,Rx_Valid置低。

变量

功能说明

RESET

复位

Rx_Valid

接收有效信号

Rx_Hold

接收方使能信号

Clock3

接收方时钟频率

m

接收方时钟计数器

--------------------串口接收部分-----------------------------------------------------
process(RESET,Rx_Valid)	                                   --串口接收检测起始位
begin
	if RESET='0' then Rx_Hold<='0';
	else 
		if UART0_RX='0' and  Rx_Hold='0' then Rx_Hold<='1';  --开始串口接收
		elsif	 Rx_Valid'event and Rx_Valid='0' then Rx_Hold<='0';
		end if;
	end if;					  	
end process;

process(RESET,Clock3) 
	variable m:integer range 0 to 168 :=0;
begin
	if RESET='0' then Rx_Valid<='0';m:=0;
	elsif rising_edge(Clock3) and Rx_Hold='1'
	then 	
		case m is
			when 24 =>	Rx_Data(0)<=UART0_RX;
			when 40 =>	Rx_Data(1)<=UART0_RX;
			when 56 =>	Rx_Data(2)<=UART0_RX;
			when 72 =>	Rx_Data(3)<=UART0_RX;
			when 88 =>	Rx_Data(4)<=UART0_RX;
			when 104 =>Rx_Data(5)<=UART0_RX;
			when 120 =>Rx_Data(6)<=UART0_RX;
			when 136=>	Rx_Data(7)<=UART0_RX;  
			when 152 =>Rx_Valid<='1';
			when 168=> m:=0;Rx_Valid<='0';
			when others => null;
		end case;
		m:=m+1;
	end if;
end process;
--------------------串口接收部分-----------------------------------------------------

二、完整代码

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity txrx is
port
(	RESET	:in  std_logic;                      --复位信号
	button : in std_logic_vector(2 downto 0);
	CLK	:	in  std_logic;                     --时钟输入,50MHZ
	rx_begin_key		:  	in	std_logic;      --发送使能按键
	Send_count:buffer integer range 0 to 9 :=0;	--位发送计数					
	--Data00     : buffer std_logic_vector(9 downto 0);
	Datarx:buffer std_logic_vector(7 downto 0):="00000000";
	UART0_RX	:	in	std_logic:='0';					--接收的引脚
	UART0_TX:out std_logic:='0'						--发送的引脚
);
end txrx;

architecture behave of txrx is     
	Signal Clock9600	:	std_logic:='0';  --9600波特率时钟,发送
	Signal Clock3		:	std_logic:='0'; --9600三倍频采样时钟,用于接收采用
	signal Data00 :std_logic_vector(9 downto 0);
 	Signal Send_data	:	std_logic_vector(9 downto 0);   --发送寄存器
	Signal Send_en     	:   std_logic:='0';     --串口发送使能,0有效
	Signal Send_over    :   std_logic:='1';    --串口发送完成,0有效
	Signal Rx_Hold		:   std_logic:='1';     --串口接收到起始位标志
	Signal Rx_Valid		:	std_logic:='0';     --标志接收到一字节有效数据
	Signal Rx_Data		:	std_logic_vector(7 downto 0);	-接收寄存器                 
begin
--------------------产生波特率-----------------------------------------------------
process(CLK,RESET,Rx_Hold)         --时钟产生进程
			variable	ClockCount	    :	integer range 0 to 4167:=0;
			variable	ClockCount_Rx	:	integer range 0 to 260:=0;
begin				  
if (rising_edge(CLK)) then 
	ClockCount:=ClockCount+1;
	ClockCount_Rx:=ClockCount_Rx+1;
	
	  --产生9600时钟,1/40MHZ*4167=1/9600
	 if ClockCount=4167 then Clock9600<='1';ClockCount:=0;
	 else	Clock9600<='0';
	 end if;
	 --4167/16=260		  
	 if ClockCount_Rx=260 then	Clock3<='1';ClockCount_Rx:=0;
	 else Clock3<='0';
	 end if;
 end if;
end process;	
--------------------产生波特率-----------------------------------------------------

--------------------串口发送部分-----------------------------------------------------
process(Clock9600)	                                   
	--variable Send_count:integer range 0 to 9 :=0;	--位发送计数						  
begin
	if Send_en='1' then	
		Send_count<=0;UART0_TX<='1';Send_over<='1';
	elsif rising_edge(Clock9600) then
		if Send_count=9 then  
			UART0_TX<=Send_data(9);Send_over<='0';
		else 
			UART0_TX<=Send_data(Send_count);  
			Send_count<=Send_count+1; 
		end if;
	end if;
end process;

process(rx_begin_key,Clock9600,RESET) 	                                --发送激励
begin
	if RESET='0' then	 Send_en<='1';Send_data<="1000000000";
	else
		if (rx_begin_key='1') then 
			Send_en<='0'; Send_data<=Data00; --发送赋初值
		end if;
		if Send_over='0'	 then  Send_en<='1';  end if;
end if;
end process;
--------------------串口发送部分-----------------------------------------------------

--------------------串口接收部分-----------------------------------------------------
process(RESET,Rx_Valid)	                                   --串口接收检测起始位
begin
	if RESET='0' then Rx_Hold<='0';
	else 
		if UART0_RX='0' and  Rx_Hold='0' then Rx_Hold<='1';  --开始串口接收
		elsif	 Rx_Valid'event and Rx_Valid='0' then Rx_Hold<='0';
		end if;
	end if;					  	
end process;

process(RESET,Clock3) 
	variable m:integer range 0 to 168 :=0;
begin
	if RESET='0' then Rx_Valid<='0';m:=0;
	elsif rising_edge(Clock3) and Rx_Hold='1'
	then 	
		case m is
			when 24 =>	Rx_Data(0)<=UART0_RX;
			when 40 =>	Rx_Data(1)<=UART0_RX;
			when 56 =>	Rx_Data(2)<=UART0_RX;
			when 72 =>	Rx_Data(3)<=UART0_RX;
			when 88 =>	Rx_Data(4)<=UART0_RX;
			when 104 =>Rx_Data(5)<=UART0_RX;
			when 120 =>Rx_Data(6)<=UART0_RX;
			when 136=>	Rx_Data(7)<=UART0_RX;  
			when 152 =>Rx_Valid<='1';
			when 168=> m:=0;Rx_Valid<='0';
			when others => null;
		end case;
		m:=m+1;
	end if;
end process;
--------------------串口接收部分-----------------------------------------------------
--------------------数据输入及显示部分-----------------------------------------------------
process(Rx_Valid,CLK)
begin
	if RESET='0' then	
		case button is
			when "000"=>Datarx<="01000000";
			when "001"=>Datarx<="01111001";
			when "010"=>Datarx<="00100100";
			when "011"=>Datarx<="00110000";
			when "100"=>Datarx<="00011001";
			when "101"=>Datarx<="00010010";
			when "110"=>Datarx<="00000010";
			when "111"=>Datarx<="01111000"; 
			when others=>null; 
		end case;
		Data00<='1'&Datarx&'0';
	else
		if rising_edge(Rx_Valid) then	
			Datarx<=Rx_Data;
		end if;
	end if;
end process;
--------------------数据输入及显示部分-----------------------------------------------------
end behave;

  • 10
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值