一、 实验原理:
1、 概述
UART的全称是通用异步收发器(Universal Asynchronous Receiver/Transmitter),是实现设备之间低速数据通信的标准协议。“异步”指不需要额外的时钟线进行数据的同步传输,是一种串行总线接口,只需占用两根线就可以完成数据的收发(一根接收数据,一根发送数据),常用的标准通信波特率有9600bps、115200bps等。
2、 通信协议与帧格式
UART一帧由起始位、数据位、校验位和停止位组成。数据逐位传输,示意图如图 1.1所示。
a) 起始位
UART空闲时(没有数据传输),总线为高电平(逻辑1),当需要数据传输时,首先发送一个“起始位”,起始位为一个低电平“逻辑0”。
为什么需要起始位?
因为UART没有控制线,要让接收方知道什么时候开始接收数据,需要一些手段,在UART,数据的传输只有一根线,所以在发送数据之前,先发一位逻辑“0”作为数据发送的起始标志,接收方在空闲时,当检测到有一个低电平,则开始接逐位接收数据。
b) 数据位
如图 1.1的“2.”所示,紧挨着“起始位”的是数据位,它可以是5、6、7或8位,收/发双方在数据开始传输前,需要对双方数据位位数作一致的定义,否则会导致数据的传输错误;数据位的发送采用低位(LSB)先发送。
为什么数据位可变?
因为UART是一种低速总线,每多发一位都占用不少的时间(由传输波特波决定),所以可以传输数据的特点,采用不同位数据的波特率以节约时间。如在Modbus协议中,Modbus-ASCII是以ASCII码传输的,因为ASCII是不会大于0x7F,所以使用7位的数据位,而Modbus-RTU则采用8位的数据位。
c) 校验位
UART的校验位紧挨着数据位,采用奇/偶位校验方式,可有可无,是为了为了验证数据传输的安全性而设置的,在收/发双方进行数据传输前要预设好是否需要校验位,如果需要则是奇校验还是偶校验。如图 1.1的“3.”所示。
为什么需要校验?
UART长距离数据传输时,使用RS-232、RS-485传输,一般使用较长的导线连接收/发设备,其工作现场因存在不同程序的干扰,可能会导致数据的传输错误,加个校验位可作初步的校验,如果校验出错,说明数据是不可靠的,直接丢弃;如果校验通过,则数据有一定的可靠性,可进入下一级的校验,通常加入通信协议,如Modbus-RTU引入了CRC校验。
d) 停止位
UART的帧以停止位作为停止标志,是在数据位(没有校验位)和校验位(有校验位)之后发送1~2位的逻辑“1”。停止为可以为1位、1.5位和2位。当发送完停止位之后,UART总线进入空闲。
e) 空闲
空闲指UART总线上没有数据进行传输,表现为发送方输出逻辑“1”,在空闲时,接收方时刻监视UART总线上电平变化,当发现起始化,则进入数据接收状态,直至接收完一帧数据,如果最后没有检测到停止位,则标志帧错误。
f) 波特率(Baudrate)
由于UART没有同步时钟线,收/发双方如果需要进行正确的数据传输,则要在收/发双方定义一致的位时钟,位时钟可以理解为UART总线一个位所占用的时间,即“波特率”。在定义上,收/发双方的波特率可以是随意的,只需要保持一致,如双方都是1000bps,但是,这不能兼容现有常用的设备,兼容性差。所以在工程应用中,常用一些特定的波特率真,如4800bps、9600bps或115200bps等。
如果波特率是9600bps,则传输速度最大是多少?
波特率的单位是bps(位每秒),是指发送一位所占用的时间。如果UART定义8位数数位、无校验、1位停止位。则一帧数据的位数N如公式(3)所示:
N = 1位起始位+8位数据位+0位校验位+1位停止位=10bits 公式(3)
所以最大数据传输率为:9600bps / N = 960BPS(字节每秒)。
即当波特率为9600rps,UART帧格式为8位数据位、无校验、1位停止位时,最大的数据传输率为0.96KBPS。
g) 常用帧格式
如图 1.3所示为8位数据位、无校验位、1位停止位的帧格式示意图,一帧共有10位。发送的数据为0xA5。
3、 硬件接口
a)RS-232连接器
RS-232的连接器常用的是DB-9,其连接器示意图如图 1.6所示,为九针连接器。对于常用的简单应用,使用到的有三根线:RxD、TxD和GND。串口接口头分公母接头,下面所示为母的接头。
实物图如图 1.7所示。
b) 电平转换电路(与计算机连接)
二、实验过程
有了上面的理论基础我们就可以开始设计一个UART的串行传输模块了。
1、 系统框图的设计:
系统的传输取决于波特率的大小,所以要设计一个专门产生波特率计数,实质上是对系统时钟频率进行分频。由于是单线发送和接收,实现了全双工传输,实质上是相当于串行序列接收与发生,只不过这个序列是有一定的通信格式而已。下面是两个系统的框图设计:
图2.1接收模块系统框图
图2.2发送模块的系统框图
2、 波特率计数器设计:
我们以9600的波特率,50MHZ的时钟晶振举例说明:由上面的理论知识我们可以知道,9600bps,则表示传输一位的时间需要1/9600s。时钟晶振为50MHZ,则一个时钟脉冲周期为:1/50M s。则串行传输一位的时间长度需要50M/9600=5208个时钟脉冲周期,半周期为2604个时钟脉冲周期。这个波特率时钟脉冲就可以由系统时钟脉冲分频所得。
程序设计:
--工程名:时钟分频器
--功能:实现对时钟脉冲的分频,产生9600波特率时钟脉冲
--时间:2010-12-10
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;
entity speed_select is
port(clk:in std_logic;--时钟信号
rst:in std_logic;--复位信号,低电平有效
bps_start:in std_logic;--信号开始传输信号
clk_bps:out std_logic);--波特率信号
end entity;
architecture behav of speed_select is
constant bps_para: integer :=5207;
constant bps_para_2: integer :=2603;
signal cnt:integer range 0 to bps_para:=0;
begin
process(clk) --计数进程
begin
if(rst='0') then
cnt<=0;
elsif rising_edge(clk) then
clk_bps<='1'; --后半周期置1
if (cnt<bps_para_2) then --前半周期置0
clk_bps<='0';
cnt<=cnt+1; --不断自加计数
elsif cnt<bps_para then
cnt<=cnt+1;
else cnt<=0; --计数完成对cnt进行清零
end if;
if(bps_start='0') then ---数据开始传输对cnt进行清零
cnt<=0;
end if;
end if;
end process;
end behav;
仿真结果如下:
3、 串行数据接收器设计:
串行数据是在波特率时钟脉冲下传输的。根据UART的常用串行传输协议(1位低电平起始位,8位数据位,无检验位,1位停止位),我们可以设计出如下的状态机:
程序设计如下:
--工程名:UART串行接收器
--功能:实现对RXD串行数据接收,转换成8位数据并行输出
--时间:2010-12-10
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;
entity uart_rx is
port(
rst:in std_logic; --复位信号,低电平有效
rx:in std_logic;--RXD串行数据输入
clk_bps:in std_logic;--波特率信号
rx_data:out std_logic_vector(7 downto 0); --8位并行数据输出
rx_int:out std_logic;--中断传输信号
bps_start:out std_logic--信号开始传输信号
);
end entity;
architecture asm of uart_rx is
signal shifter:std_logic_vector(7 downto 0);
signal bitstate:integer range 0 to 10:=0;
begin
process(clk_bps,rst,rx) --接收进程
begin
if(rst='0') then --复位信号
bitstate<=0;
shifter<="ZZZZZZZZ";
elsif(rising_edge(clk_bps)) then
if(bitstate=0) then --状态0 判断有没有数据输入
if(rx='0') then
bitstate<=bitstate+1;
bps_start<='1';
end if;
elsif(bitstate=9) then --状态9 数据接收完成
bitstate<=0;
bps_start<='0';
else --其他状态接收1-8为数据,停止位不检查
shifter(0)<=rx;
shifter(7 downto 1)<=shifter(6 downto 0);
bps_start<='0';
bitstate<=bitstate+1;
end if;
end if;
end process;
process(clk_bps,rst) --输出进程
begin
if(rst='0') then
rx_data<="00000000";
rx_int<='0';
elsif(bitstate=9) then
rx_data<=shifter; --数据输出
rx_int<='1';
else
rx_int<='0';
rx_data<="ZZZZZZZZ";
end if;
end process;
end asm;
仿真结果如下:
4、 串行数据发送器设计:
--工程名:UART串行发送器
--功能:实现对8位并行输入数据,转换成TXD串行数据发送
--时间:2010-12-10
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;
entity uart_tx is
port(
rst:in std_logic; --复位信号,低电平有效
rx_data:in std_logic_vector(7 downto 0); --8位并行数据输入
rx_int:in std_logic;--中断传输信号
clk_bps:in std_logic;--波特率信号
tx:out std_logic;--TXD串行数据输出
bps_start:out std_logic--信号开始传输信号
);
end entity;
architecture asm of uart_tx is
signal shifter:std_logic_vector(7 downto 0);
type state is(s0,s1,s2,s3);
begin
process(clk_bps,rst)
variable cnt:integer range 0 to 8;
variable present_state:state;
begin
if(rst='0') then
tx<='1';
bps_start<='0';
elsif(rising_edge(clk_bps)) then
case present_state is
when s0=> --判定有没有并行数据输入
if(rx_int='1') then
shifter<=rx_data;
bps_start<='1';
tx<='1';
present_state:=s1;
else
tx<='1';
present_state:=s0;
end if;
when s1=> --状态1,低电平起始位输出
tx<='0';
bps_start<='0';
present_state:=s2;
when s2=> --状态2,8位数据移位输出
if(cnt<8) then
tx<=shifter(cnt);
bps_start<='0';
cnt:=cnt+1;
present_state:=s2;
else
cnt:=0;
bps_start<='0';
present_state:=s3;
end if;
when s3=> --状态3,高电平停止位输出
tx<='1';
bps_start<='0';
present_state:=s0;
end case;
end if;
end process;
end asm;
仿真结果如下:
图形化设计:
利用Quartus集成的仿真软件,比较难检验生成的总体图形文件,这里就不做仿真了。
每个模块都是经过仿真测试的。估计没有什么问题了。
实验总结:通过本次实验进一步熟悉了对FPGA的开发流程。之前一直用这个UART通信,但是由于单片机将这个模块集成化了,也只是在用,没有深入去了解,通过本次实验对这个串口通信协议有了刨根式的了解。通过本次实验,我也深刻的体会到一个项目的开展过程,首先要对项目的理论知识深入理解,才能很好的开展项目,同时在遇到从来没有遇到过的问题时,要将这个问题化解成自己熟悉的模块,一步一步的实现,不能操之过急。在这个工程中,我们将可以将这个系统化解成三个模块进行开展:波特率发生器(分频器),串行数据接收器(序列接收器),串行数据发送器(序列发生器)。这样的话,不但目标明确,而且将项目分工,加快了项目的进度。这是最后一次实验了,期待着课程设计了,希望这个课程在以后能派上大用场。
主要遇到的问题:
1, 对UART通信协议的理论知识理解:虽然一直在用这个模块,但是却对其如何实现没有真正的了解过,通过本次实验恶补这方面的知识了
2, 项目的构架,对这个工程如何构架,如何将分割成自己熟悉的模块。这个开展这个工程所首要解决的,同时也要花中功夫下去,因为这一步直接关系到下一步开展的。
3, 已经开始熟练VHDL编程了,在语法上面,没有什么好纠结的了,经过上两次的实验后,这次实验还是挺顺利的,慢慢在成长着。
文章评论(0条评论)
登录后参与讨论