标准的串口是接触到的RSR232串口,工业上用的比较多,而我们实际中使用的串口则是在这基础上的简化,只用其中三根线TXD,RXD和地线。
串口通信是单线通信,数据在一根线上进行传输,而传输协议为:
一帧数据的开始拉低电平,表示开始传输数据;随后串行发送8位bit位,随后紧跟1到2个码元的高电平停止位,此时一帧数据传输完毕,等待下一帧数据传输。
数据的传输时间与码元宽度直接相关,而码元宽度由传输的波特率决定。
一个串口可以分为三个部分:波特率发生器,接受模块,发送模块
1.波特率发生器
该模块直接就是一个分频模块,直接对总时钟进行分频,我们预先规定一个码元的宽度为16个波特时钟,则波特率模块所要产生的波特时钟分频系数为sys_clk/波特率再乘以16,实际工程中系统时钟为50Mhz,所以分频系数为27.
2.接受模块
该模块实现依赖有限状态机,状态机共分为5个状态,分别为R_START,R_CENTER,R_WAIT,R_SAMPLE,R_STOP。
R_START:等待RX信号变为低电平,发生转变则说明数据开始准备接受,进入R_CENTER状态。
R_CENTER:计数8个时钟到达第一个bit中间位置,跳转到R_WAIT状态(码元宽度为16个波特时钟,8为半个码元,开始信号0的持续时间也为一个码元宽度,所以一共8个波特时钟进入)。
R_WAIT:等待16个波特时钟开始采样,进入R_SAMPLE状态,采样时刻对应到每一个数据的中点位置(中点位置判断最准确)。
R_SAMPLE:进行取值,输入到串并转换。取样8次则进入R_STOP状态。
R_STOP:不做具体检测,直接跳转R_START.
3.发送模块
该模块也分为5个状态,T_NONE,T_START,T_WAIT,T_SHIFT,T_STOP。具体的实现方法以及状态机的转换与接受相类似,这里就不再重复。
最后将三个模块进行整合就形成完整的串口模块,可以数据口共用,也可分开,由于是直接实现的,自由性比较大。
废话不多说,直接上代码:
波特率模块
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity baud is
generic(b_temp:integer:=27);
port(clk:in std_logic;
bclk:out std_logic);
end baud;
architecture behavior of baud is
begin
process(clk)
variable cnt:integer range 0 to b_temp;
begin
if clk'event and clk='1' then
if cnt=b_temp then cnt:=0;bclk<='1';
else cnt:=cnt+1;bclk<='0';
end if;
end if;
end process;
end behavior;
接受模块
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity rxd is
generic(data_bits:integer:=8);
port(bclk_in,rxd_in:in std_logic;
rx_ready:out std_logic;
rx_buffer:out std_logic_vector(7 downto 0));
end rxd;
architecture beheavior of rxd is
type states is(r_start,r_center,r_wait,r_sample,r_stop);
signal state:states:=r_start;
signal rxd_sync:std_logic;
begin
process(rxd_in)
begin
if rxd_in='0' then rxd_sync<='0';
else rxd_sync<='1';
end if;
end process;
process(bclk_in,rxd_sync)
variable count:std_logic_vector(3 downto 0);
variable r_cnt:integer:=0;
variable buf:std_logic_vector(7 downto 0);
begin
if bclk_in'event and bclk_in='1' then
case state is
when r_start=>
if rxd_sync='0' then
state<=r_center;
rx_ready<='0';
r_cnt:=0;
else state<=r_start;rx_ready<='0';
end if;
when r_center=>
if rxd_sync='0' then
if count="0100"then
state<=r_wait;
count:="0000";
else
count:=count+'1';
state<=r_center;
end if;
else
state<=r_start;
end if;
when r_wait=>
if count="1110" then
if r_cnt=data_bits then
state<=r_stop;
else
state<=r_sample;
end if;
count:="0000";
else
count:=count+'1';
state<=r_wait;
end if;
when r_sample=>
buf(r_cnt):=rxd_sync;
r_cnt:=r_cnt+1;
state<=r_wait;
when r_stop=>
rx_ready<='1';
rx_buffer<=buf;
state<=r_start;
when others=>
state<=r_start;
end case;
end if;
end process;
end beheavior;
发送模块
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity txd is
generic(data_bits:integer:=8);
port(bclk_in:in std_logic;
tx_cmd:in std_logic;
txd_out:out std_logic;
txd_done:out std_logic;
tx_buffer:in std_logic_vector(7 downto 0));
end txd;
architecture beheavior of txd is
type states is(t_none,t_start,t_wait,t_shift,t_stop);
signal state:states:=t_none;
signal t_cnt:integer:=0;
begin
process(bclk_in,tx_cmd,tx_buffer)
variable count:std_logic_vector(4 downto 0);
variable t_bitcnt:integer:=0;
variable txds:std_logic;
begin
if bclk_in'event and bclk_in='1' then
case state is
when t_none=>
if tx_cmd='1' then
state<=t_start;
txd_done<='0';
else
state<=t_none;
end if;
when t_start=>
if count="01111" then
state<=t_shift;
count:="00000";
else
count:=count+'1';
txds:='0';
state<=t_start;
end if;
when t_wait=>
if count="01110" then
if t_bitcnt=data_bits then
state<=t_stop;
t_bitcnt:=0;
count:="00000";
else
state<=t_shift;
end if;
count:="00000";
else
count:=count+'1';
state<=t_wait;
end if;
when t_shift=>
txds:=tx_buffer(t_bitcnt);
t_bitcnt:=t_bitcnt+1;
state<=t_wait;
when t_stop=>
if count="01111" then
if tx_cmd='0' then
state<=t_none;
count:="00000";
else
count:=count;
state<=t_stop;
end if;
txd_done<='1';
else
count:=count+'1';
txds:='1';
state<=t_stop;
end if;
when others=>state<=t_none;
end case;
end if;
txd_out<=txds;
end process;
end beheavior;
顶层模块
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity my_uart is
port(clk_in:in std_logic;
rx:in std_logic;
tx:out std_logic;
tcmd:in std_logic;
tx_done:out std_logic;
rx_ready:out std_logic;
t_data:in std_logic_vector(7 downto 0);
r_data:out std_logic_vector(7 downto 0));
end my_uart;
architecture beheavior of my_uart is
component baud is
port(clk:in std_logic;
bclk:out std_logic);
end component;
component rxd is
port(bclk_in,rxd_in:in std_logic;
rx_ready:out std_logic;
rx_buffer:out std_logic_vector(7 downto 0));
end component;
component txd is
port(bclk_in:in std_logic;
tx_cmd:in std_logic;
txd_out:out std_logic;
txd_done:out std_logic;
tx_buffer:in std_logic_vector(7 downto 0));
end component;
signal baud_clk:std_logic;
begin
B:baud
port map(clk_in,baud_clk);
R:rxd
port map(baud_clk,rx,rx_ready,r_data);
T:txd
port map(baud_clk,tcmd,tx,tx_done,t_data);
end beheavior;
代码没有注释,凑合看吧,嘻嘻