资源描述
UART 通用异步收发器的设计
———————————————————————————————— 作者:
———————————————————————————————— 日期:
21
个人收集整理 勿做商业用途
综合课程设计实验报告
院 系: 信息科学与工程学院
学号: 04006531 04006525
姓 名: 蓝渊明 陈新明
教 师: 张圣清
时 间: 2010.01.15
1 引言
由于微电子学和计算机科学的迅速发展,给EDA(电子设计自动化)行业带来了巨大的变化。特别是进入20世纪90年代后,电子系统已经从电路板级系统集成发展成为包括ASIC、FPGA/CPLD和嵌入系统的多种模式。可以说EDA产业已经成为电子信息类产品的支柱产业。EDA之所以能蓬勃发展的关键因素之一就是采用了硬件描述语言(HDL)描述电路系统。就FPGA和CPLD开发而言,比较流行的HDL主要有Verilog HDL、VHDL、ABEL-HDL和 AHDL 等,其中VHDL和Verilog HDL因适合标准化的发展方向而最终成为IEEE标准。下面的设计就是用VHDL来完成实现的。
2. UART设计实例
通常设计数字电路大都采用自顶向下将系统按功能逐层分割的层次化设计方法,这比传统自下向上的EDA设计方法有更明显的优势(当时的主要设计文件是电路图).因为由自顶向下的设计过程可以看出,从总体行为设计开始到最终逻辑综合,形成网络表为止。每一步都要进行仿真检查,这样有利于尽早发现系统设计中存在的问题,从而可以大大缩短系统硬件的设计周期。
UART(即Universal Asynchronous Receiver Transmitter 通用异步收发器)是一种应用广泛的短距离串行传输接口。UART允许在串行链路上进行全双工的通信。串行外设用到的RS232-C异步串行接口,一般采用专用的集成电路即UART实现。如8250、8251、NS16450等芯片都是常见的UART器件,这类芯片已经相当复杂,有的含有许多辅助的模块(如FIFO),有时我们不需要使用完整的UART的功能和这些辅助功能。或者设计上用到了FPGA/CPLD器件,那么我们就可以将所需要的UART功能集成到FPGA内部。使用VHDL将UART的核心功能集成,从而使整个设计更加紧凑、稳定且可靠。本文应用EDA技术,基于FPGA/CPLD器件设计与实现UART.
2.1.1 UART结构
UART主要有由数据总线接口、控制逻辑、波特率发生器、发送部分和接收部分等组成。本设计主要设计UART中最重要的发送部分和接收部分
图1
2。1.2 UART的帧格式
UART的帧格式如图2所示。
图2发送数据过程:空闲状态,线路处于高电位;当收到发送数据指令后,拉低线路一个数据位的时间T,接着数据按低位到高位依次发送,数据发送完毕后,接着发送奇偶校验位和停止位(停止位为高电位),一帧资料发送结束.
接收数据过程:空闲状态,线路处于高电位;当检测到线路的下降沿(线路电位由高电位变为低电位)时说明线路有数据传输,按照约定的波特率从低位到高位接收数据,数据接收完毕后,接着接收并比较奇偶校验位是否正确,如果正确则通知后续设备准备接收数据或存入缓存.
由于UART是异步传输,没有传输同步时钟。为了能保证数据传输的正确性,UART采用16倍数据波特率的时钟进行采样.每个数据有16个时钟采样,取中间的采样值,以保证采样不会滑码或误码。一般UART一帧的数据位数为8,这样即使每个数据有一个时钟的误差,接收端也能正确地采样到数据。
UART的接收数据时序为:当检测到数据的下降沿时,表明线路上有数据进行传输,这时计数器CNT开始计数,当计数器为24=16+8时,采样的值为第0位数据;当计数器的值为40时,采样的值为第1位数据,依此类推,进行后面6个数据的采样.如果需要进行奇偶校验,则当计数器的值为152时,采样的值即为奇偶位;当计数器的值为168时,采样的值为“1”表示停止位,一帧数据接收完成。
2.2 UART的设计与实现
2.2.1 UART分频器
假设数据的波特率为p,则所需时钟的频率为16*p 以波特率p为9600为例,系统时钟为60MHz 代码如下:
library IEEE;
use IEEE.STD_LOGIC_1164。ALL;
use IEEE。STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED。ALL;
entity baud is
Port (clk,resetb:in std_logic;
bclk:out std_logic);
end baud;
architecture behavioral of baud is
begin
process(clk,resetb)
variable cnt:integer;
begin
if resetb=’1’ then -- resetb=’1'时复位
cnt:=0; bclk〈='0’;
elsif rising_edge(clk) then
if cnt〉=38 then cnt:=0; bclk〈=’1'; ——设置分频系数
else cnt:=cnt+1; bclk〈=’0’;
end if;
--if cnt〈=72 then bclk<=’1’;cnt:=cnt+1; -—设置分频系数
——elsif cnt〈103 then cnt:=cnt+1; bclk<=’0';
—-else cnt:=0;bclk<='0’;
end if;
-—end if ;
—-end if;
end process;
end behavioral;
2。2.2 UART发送器
UART发送模块的功能:接收到发送指令后,把数据按UART协议输出,先输出一个低电平的起始位,然后从低到高输出8个数据位,接着是可选的奇偶校验位,最后是高电平的停止位
library IEEE;
use IEEE。STD_LOGIC_1164。ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity transfer is
generic(framlent:integer:=8);
Port (bclkt,resett,xmit_cmd_p:in std_logic; ——定义输入输出信号
txdbuf:in std_logic_vector(7 downto 0):=”11001010”;
txd:out std_logic;
txd_done:out std_logic);
end transfer;
architecture behavioral of transfer is
type states is (x_idle,x_start,x_wait,x_shift,x_stop); —-定义个子状态
signal state:states:=x_idle;
signal tcnt:integer:=0;
begin
process(bclkt,resett,xmit_cmd_p,txdbuf) ——主控时序、组合进程
variable xcnt16:std_logic_vector(4 downto 0):="00000”; --定义中间变量
variable xbitcnt:integer:=0;
variable txds:std_logic;
-—---#################################################
variable txdbuf_add :std_logic_vector(7 downto 0);
variable temp:std_logic;
————---##################################################
begin
if resett=’1' then ——复位
state〈=x_idle; txd_done〈=’0'; txds:='1’;
elsif rising_edge(bclkt) then
if xmit_cmd_p=’1’then
temp:=’1’;
end if;
case state is
when x_idle=>if temp='1’ then state〈=x_start; temp:='0';txd_done<='0’; txdbuf_add:=txdbuf+"00000001”;
else state〈=x_idle; -—状态1,等待数据帧发送命令
end if;
when x_start=〉if xcnt16>=”01110” then state〈=x_wait; xcnt16:=”01110”;
else xcnt16:=xcnt16+1; txds:=’0’; state<=x_start;
end if; --状态2,发送信号至起始位
when x_wait=〉if xcnt16〉="01110” then
if xbitcnt=framlent then state〈=x_stop; xbitcnt:=0;
else state<=x_shift;
end if;
xcnt16:="00000";
else xcnt16:=xcnt16+1; state〈=x_wait;
end if; -—状态3,等待状态
when x_shift=〉txds:=txdbuf_add(xbitcnt); xbitcnt:=xbitcnt+1; state<=x_wait; -—状态4,将待发数据进行并串转换
when x_stop=〉if xcnt16〉="01111" then
if xmit_cmd_p=’0' then state〈=x_idle; xcnt16:=”00000";
else xcnt16:=xcnt16; state〈=x_stop;
end if; txd_done<='1';
else xcnt16:=xcnt16+1; txds:=’1’; state<=x_stop;
end if; ——状态5,停止位发送状态
when others=>state<=x_idle;
end case;
end if;
txd<=txds;
end process;
end behavioral;
2。2。3 UART接收器
UART接收模块的功能:时时检测线路,当线路产生下降沿时,即认为线路有数据传输,启动接收数据进程进行接收,按从低位到高位接收数据
library IEEE;
use IEEE。STD_LOGIC_1164。ALL;
use IEEE。STD_LOGIC_ARITH。ALL;
use IEEE.STD_LOGIC_UNSIGNED。ALL;
entity reciever is
generic(framlenr:integer:=8);
Port (bclkr,resetr,rxdr:in std_logic; --定义输入输出信号
r_ready:out std_logic;
rbuf:out std_logic_vector(7 downto 0));
end reciever;
architecture behavioral of reciever 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
pro1:process(rxdr)
begin
if rxdr=’0’ then
rxd_sync〈=’0';
else rxd_sync<='1’;
end if;
end process;
pro2:process(bclkr,resetr,rxd_sync) ——主控时序、组合进程
variable count:std_logic_vector(3 downto 0); --定义中间变量
variable rcnt:integer:=0;
variable rbufs:std_logic_vector(7 downto 0);
begin
if resetr=’1’ then --复位
state〈=r_start; count:=”0000";
elsif rising_edge(bclkr) then
case state is
when r_start=〉if rxd_sync='0' then
state〈=r_center; r_ready<='0’; rcnt:=0;
else state<=r_start; r_ready〈='0';
end if; --状态1,等待起始位
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; ——状态2,求出每位的中点
when r_wait=>if count〉="1110” then
if rcnt=framlenr then state<=r_stop;
else state<=r_sample;
end if;
count:="0000"; -—状态3,等待状态
else count:=count+1; state〈=r_wait;
end if;
when r_sample=〉rbufs(rcnt):=rxd_sync; rcnt:=rcnt+1;
state<=r_wait; --状态4,数据位采样检测
when r_stop=〉r_ready〈='1'; rbuf<=rbufs;
state〈=r_start; —-状态4,输出帧接收完毕信号
when others=〉state〈=r_start;
end case;
end if;
end process;
end behavioral;
2.2。4 显示模块
单片机和FPGA接口模块,把接收到的数据送给单片机,并显示在LCD上
#include <reg52.H〉
#include 〈ctype.h>
#include 〈string.h〉
#include <stdlib。h〉
#include <stdio.h〉
#include <math。h>
#include 〈INTRINS.H>
/***********************************************************************/
#define uchar unsigned char
#define uint unsigned int
#define Disp_On 0x3f // 显示开指令 //
#define Disp_Off 0x3e // 显示关指令 //
#define Col_Add 0x40 // 定位到第0列指令 //
#define Page_Add 0xb8 // 定位到第0页指令 //
#define Start_Line 0xc0 // 定位从DDROM中的第0行开始往屏幕上显示 //
/***********************************************************************/
sbit CS1 = P2^3; // 右屏使能 //
sbit CS2 = P2^4; // 左屏使能 //
sbit E = P2^0; // 使能端 //
sbit RS = P2^2; // 数据或指令寄存器选择端 //
sbit RW = P2^1; // 读写控制端 //
/*———-—--—-————--—-——字模--—-—-—-—--——-—-——-—--—-*/
char code ling[]={/* 数字0 8x16—----————----*/
0,240,8,4,4,8,240,0,0,7,8,16,16,8,7,0};
char code yi[]={/* 数字1 8x16-—-———-—-——--*/
0,0,8,8,252,0,0,0,0,0,16,16,31,16,16,0};
char code er[]={/* 数字2 8x16-——————-——---*/
0,48,8,4,132,72,48,0,0,28,18,17,16,24,4,0};
char code san[]={/* 数字3 8x16——------—-———*/
0,48,8,132,132,72,48,0,0,4,8,16,16,9,6,0};
char code si[]={/* 数字4 8x16——----——--—--*/
0,0,192,48,12,252,0,0,0,3,2,2,18,31,18,2};
char code wu[]={/* 数字5 8x16-——---——-———-*/
0,252,68,68,68,132,4,0,0,4,8,16,16,8,7,0};
char code liu[]={/* 数字6 8x16————-—-————--*/
0,240,136,68,68,136,48,0,0,7,8,16,16,8,7,0};
char code qi[]={/* 数字7 8x16-———-—————---*/
16,12,4,4,132,100,28,0,0,0,0,30,1,0,0,0};
char code ba[]={/* 数字8 8x16—-——---——----*/
0,48,72,132,132,72,48,0,0,6,9,16,16,9,6,0};
char code jiu[]={/* 数字9 8x16——-—-—-—-—--—*/
0,112,136,4,4,136,240,0,0,4,8,17,17,8,7,0};
/*————-—---——----—-—-延时子程序———--—---—-——--————--——-—-——-*/
void delay(unsigned char t)
{
while(t--);
}
void delays(unsigned char a){
unsigned char i;
while( -—a != 0)
{ for(i = 0; i 〈 125; i++); //一个 ; 表示空语句,CPU空转。
} //i 从0加到125,CPU大概就耗时1毫秒
}
/*void delay1ms(unsigned char z)
{unsigned char x,y;
for(x=z;x>0;x-—)
for(y=110;y>0;y—-);
} */
/*------——-—---—--——-——检查状态--——-—-----——-————-—-———-—-—--*/
void check_busy()
{
RS=0;
RW=1; //数据读到db7—db0,rs=0表示在e下降元指令写到ir
E=1;
delay(1);
while((bit)P0 & 0x80);
E=0;
}
/*-———-——-—--———-——-—-———-—写命令到LCD-—-—-————-——----—----———--—--—*/
void write_command(unsigned char cmdcode)
{check_busy();
//delay(1);
RS = 0;
RW = 0;
//P1 = cmdcode;
//
P0 = cmdcode;
delay(1);
E = 1;
delay(1);
E = 0;
delay(1);
}
/*--—--——-—--———----—--————写数据到LCD—-—-—------——-————--————-——-*/
void write_data(unsigned char Dispdata)
{
check_busy();
// delay(1);
RS = 1;
RW = 0;
//P1 = Dispdata;
//
P0 = Dispdata;
delay(1);
E = 1;
delay(1);
E = 0;
delay(1);
}
/*—--——--——---———----—-—--清显示屏---——--—----——-————----——-—---——*/
void Clr_Scr()
{
unsigned char j,k;
CS2=0;CS1=1; // 清左半屏 //
{ for(k=0;k〈8;k++)
{write_command(Page_Add+k);
write_command(Col_Add+0);
for(j=0;j<64;j++)
write_data(0x00);
}
}
CS2=1;CS1=0;
{ for(k=0;k〈8;k++)
{write_command(Page_Add+k);
write_command(Col_Add+0);
for(j=0;j<64;j++)
write_data(0x00);
}
}
}
/*-———--—----—-—---—---指定位置显示数字8*16—--——-—-—————----—-—-—-*/
void sz_disp16(unsigned char pag,unsigned char col, unsigned char shu)
{
unsigned char code *szk;
unsigned char i,j;
switch(shu)
{case 0:szk=ling;
break;
case 1:szk=yi;
break;
case 2:szk=er;
break;
case 3:szk=san;
break;
case 4:szk=si;
break;
case 5:szk=wu;
break;
case 6:szk=liu;
break;
case 7:szk=qi;
break;
case 8:szk=ba;
break;
case 9:szk=jiu;
break;
}
for(j=0;j<2;j++)
{ write_command(Page_Add+pag+j);
write_command(Col_Add+col);
for(i=0;i〈8;i++)
write_data(szk[8*j+i]);
}
}
/*—----——-—-————-—-——--指定位置显示汉字16*16-————-———-—-————-——-—--*/
void hz_disp16(unsigned char pag,unsigned char col, unsigned char code *hzk)
{
unsigned char j=0,i=0;
for(j=0;j<2;j++)
{ write_command(Page_Add+pag+j);
write_command(Col_Add+col);
for(i=0;i〈16;i++)
write_data(hzk[16*j+i]);
}
}
/*-———-———---—-—---—初始化LCD屏——-———---—-—--——-—-—--————*/
void init_lcd()
{CS2=1;
CS1=0;
delay(100);
write_command(Disp_Off);
write_command(Page_Add+0);
write_command(Start_Line+0);
write_command(Col_Add+0);
write_command(Disp_On);
CS2=0;
CS1=1;
delay(100);
write_command(Disp_Off);
write_command(Page_Add+0);
write_command(Start_Line+0);
write_command(Col_Add+0);
write_command(Disp_On);
}
/*-——-————-—---—-——--——显示数据(右边)—--—-—--*/
void display_data(unsigned char data0)
{ unsigned char a,b,c,temp;
Clr_Scr(); // 先清屏 //
a=0;b=0;c=0;temp=data0; //其中 a表示输入的第一个数,b c为第二,三个有效数字,temp为中间变量,sum为最后总和
CS2=1;CS1=0;
a=temp/100; temp=temp%100;
b=temp/10;temp=temp%10;
c=temp;
if(a!=0)
{sz_disp16(3,8,a);
sz_disp16(3,16,b);
sz_disp16(3,24,c);
}
if(a==0&&b!=0)
{sz_disp16(3,16,b);
sz_disp16(3,24,c);
}
if(a==0&&b==0) sz_disp16(3,24,c);
}
/*---——-—--—-———--———-—--—-- 显示主界面——————-—-—-————--—-—--——*/
void start(void)
{Clr_Scr(); // 先清屏 //
CS2=0;CS1=1;
sz_disp16(2,48,0); // 零
main()
{ init_lcd(); //初始化
start(); //显示主界面
while(1)
{
if(P1!=0) display_data(P1);//P1为FPGA与单片机相连的接口
}
}
2。2。5 UART的硬件测试
为了测试UART与PC通信的正确性,本例测试方法是,PC将数据发送到FPGA,FPGA接收到数据再发送给PC。FPGA与PC通信模块连接原理图如图
引脚配置如图
配FPGA引脚,编译工程,连接好开发板及下载线缆,接上电源,下载配置FPGA,打开PC的串口调试工具,发送数据,观察接收到的数据,
实现了从PC中接收数据 加1后再从发送模块传输到PC的功能。
接上显示模块,编译,下载成功后,在LCD显示器上,可以显示出程序中预设的值。再在串口调试工具里发送数据,调试多次,但没有出现所要的结果。分析发现,两个模块单独运行都可以正常工作,但由于电平冲突的存在,两个模块连接后,会对最后的实验结果造成很严重的影响。
3 总结
本设计由于采用了VHDL语言作为输入方式并结合FPGA/CPLD,大大缩短了设计周期,提高了设计的可靠性、灵活性,使用户可根据自己的需求,方便、高效地设计出适合的串行接口电路。用FPGA 来对UART接口进行开发,可以减小系统的PCB面积,降低系统的功耗,提高设计的稳定性和可靠性,并可以充分利用了FPGA 的剩余资源。该设计具有很大的灵活性,虽然这一UART接口工作在每秒9600波特,但通过调整锁相环的参数,就可以使其工作在其他频率,十分方便。这仅仅是一个简单的通信接口,可根据不同系统的需要,增加FIFO等内容。该模块也可以作为一个完整的IP核,灵活地移植进各种型号的FPGA中,通用性很强。
展开阅读全文