基于8255的LED显示串口通信机设计
一、设计要求
(1)基本要求:
设计一串口通信程序,波特率9600,通过RS232串口自环。 自动循环发送数据串(设计在程序中)
接收并存储和显示该数据串
2 数据串单次发送由按键启动,接收端显示数据串和数据串数、正确接收数和错
误数。
1
3 数据串选择发送(预存10种数据串),通过按键选择发送,接收、存储并按序
显示。
(2)发挥部分:
4 5 6 7
数据串滚动显示。
数据串滚动显示方向和速度可控
单片机与PC机的通信,单片可以发送任意ASCII字符串到PC机,PC机发送数据串到单片机,并在显示器上显示,格式和方法自定。
设计波特率可变的通信程序,每次按键波特改变一次,对测通信。
8 接收侧波特率自动跟踪,当发送端改变波特率后,定时地重复发送一数据串,
检测接收端能正确接收和显示数据和波特率值。
二、要求分析
根据提供的51单片机的串口通信机的要求,编写相对应的程序,实现其功能,并利用已有的硬件V14开发板来检验和实现基于8255的LED串口通信机功能。要求熟悉单片机开发工具的使用,学会MeDwin,Keil,Protel等软件的使用方法。熟悉实验板资源,学会程序的下载和运行,系统硬件调试方法。用标准测试程序对实验板的各部分资源测试,并记录测试结果,维修不可用资源。
设计一能与PC机通信的串行通信终端.
要求:学习使用单片机的内置串行接口,实现两机(单片机与PC机)的多种通信方 法。
三、实验板功能模块结构
外 LED显示模块 4X4 RS232C口 部 键盘 扩 展 74LS373 8255 8155 DAC0832 IO输出口 IO口 IO/M口 模数转换器 ADC0805 地址分配GAL 数模转换器 6264 RAM 8031CPU 其它 图。实验板功能模块结构四、总体方案的确定
本次课程设计是在理论课程的基础上,目的在于培养我们的动手能力,通过电路设计、理论计算、实际编程、调试、测试、分析查找故障,解决在实际设计中的问题,使设计好的电路能正常工作,并能结合实际的实验板进行下载测试。在此基础上根据实验大纲的要求,按“51单片机课程设计选题表” 每两人一题(随机分配),实现其功能。
在考虑本次设计过程中,依据设计基本要求,并且根据所提供的实验板,研究其原理结构图,我们把8255器件用来扩展I/O口,外接一4X4键盘,以及通过该I/O口外接一LED显示器。 1、串行通信原理
串行通信是数据的各位在同一根数据线上依次逐位发送或接收。目前串行通信在单片机双机、多机以及单片机与PC机之间的通信等方面得到了广泛应用。
串口通信示意图
串行通信按同步方式可分为异步通信和同步通信两种基本通信方式。 同步通信是一种连续传送数据的通信方式,一次通信传送多个字符数据,称为一帧信息。数据传输速率较高,通常可达56000bps或更高。其缺点是要求发送时钟和接收时钟保持严格同步。
同步通信帧格式
在异步通信中,数据通常是以字符或字节为单位组成数据帧进行传送的。收、发端各有一套彼此独立,互不同步的通信机构,由于收发数据的帧格式相同,因此可以相互识别接收到的数据信息。
异步通信帧格式
(1) 起始位:
在没有数据传送时,通信线上处于逻辑“1”状态。当发送端要发送1个字符数据时,首先发送1个逻辑“0”信号,这个低电平便是帧格式的起始位。其作用是向接收端表示发送端开始发送一帧数据。接收端检测到这个低电平后,就准备接收数据信号。 (2) 数据位:
在起始位之后,发送端发出(或接收端接收)的是数据位,数据的位数没有严格的限制,5~8位均可。由低位到高位逐位传送。 (3) 奇偶校验位:
数据位发送完(接收完)之后,可发送一位用来检验数据在传送过程中是否出错的奇偶校验位。奇偶校验是收发双方预先约定好的有限差错检验方式之一。有时也可不用奇偶校验。 (4) 停止位:
字符帧格式的最后部分是停止位,逻辑“1”电平有效,它可占1/2位、1位或2位。停止位表示传送一帧信息的结束,也为发送下一帧信息作好准备。 2、AT89C51的串行接口
AT89C51内部有一个可编程全双工串行通信接口。该部件不仅能同时进行数据的发送和接收,也可作为一个同步移位寄存器使用。 下面将对其内部结构、工作方式以及波特率进行介绍。
串行接口结构示意图
1). 串行数据缓冲器SBUF
SBUF是串行口缓冲寄存器,包括发送寄存器和接收寄存器,以便能以全双工方式进行通信。此外,在接收寄存器之前还有移位寄存器,从而构成了串行接收的双缓冲结构,这样可以避免在数据接收过程中出现帧重叠错误。发送数据时,由于CPU是主动的,不会发生帧重叠错误,因此发送电路不需要双重缓冲结构。
在逻辑上,SBUF只有一个,它既表示发送寄存器,又表示接收寄存器,具有同一个单元地址99H。但在物理结构上,则有两个完全独立的SBUF,一个是发送缓冲寄存器SBUF,另一个是接收缓冲寄存器SBUF。如果CPU写SBUF,数据就会被送入发送寄存器准备发送;如果CPU读SBUF,则读入的数据一定来自接收缓冲器。即CPU对SBUF的读写,实际上是分别访问上述两个不同的寄存器。
2).串行控制寄存器SCON
串行控制寄存器SCON用于设置串行口的工作方式、监视串行口的工作状态、控制发送与接收的状态等。它是一个既可以字节寻址又可以位寻址的8位特殊功能寄存器。其格式如下所示。
串行口控制寄存器SCON
(1) SM0 SM1:串行口工作方式选择位。其状态组合所对应的工作方式如表所示
串行口工作方式
(2) SM2:多机通信控制器位。在方式0中,SM2必须设成0。在方式1中,当处于接收状态时,若SM2=1,则只有接收到有效的停止位“1”时,RI才能被激活成“1”(产生中断请求)。在方式2和方式3中,若SM2=0,串行口以单机发送或接收方式工作,TI和RI以正常方式被激活并产生中断请求;若SM2=1,RB8=1时,RI被激活并产生中断请求。
(3) REN:串行接受允许控制位。该位由软件置位或复位。当REN=1,允许接收;当REN=0,禁止接收。
(4) TB8:方式2和方式3中要发送的第9位数据。该位由软件置位或复位。在方式2和方式3时,TB8是发送的第9位数据。在多机通信中,以TB8位的状态表示主机发送的是地址还是数据:TB8=1表示地址,TB8=0表示数据。TB8还可用作奇偶校验位。
(5) RB8:接收数据第9位。在方式2和方式3时,RB8存放接收到的第9位数据。RB8也可用作奇偶校验位。在方式1中,若SM2=0,则RB8是接收到的停止位。在方式0中,该位未用。
(6)TI:发送中断标志位。TI=1,表示已结束一帧数据发送,可由软件查询TI位标志,也可以向CPU申请中断。
(7)RI:接收中断标志位。RI=1,表示一帧数据接收结束。可由软件查询RI位标志,也可以向CPU申请中断。 3).电源控制寄存器PCON
电源控制寄存器PCON的格式
SMOD:串行口波特率倍增位。在工作方式1~工作方式3时,若SMOD=1,则串行口波特率增加一倍。若SMOD=0,波特率不加倍。系统复位时,SMOD=0。 3、串行口的工作方式
AT89C51串行通信共有4种工作方式,它们分别是方式0、方式1、方式2和方式3,由串行控制寄存器SCON中的SM0 SM1决定,如前表 1). 工作方式0
(1)发送:TI=0时,执行“MOV SBUF,A”启动发送,8位数据由低位到高位从RXD引脚送出,TXD发送同步脉冲。发送完后,由硬件置位TI。
(2)接收:RI=0,REN=1时启动接收,数据从RXD输入,TXD输出同步脉冲。
8位数据接收完,由硬件置位RI。可通过“MOV A,SBUF”读取数据 方式0的波特率为fosc/12,即一个机器周期发送或接收一位数据。
应当指出:方式0并非是同步通信方式。它的主要用途是外接同步移位寄存器,以扩展并行I/O口。 2). 工作方式1
方式1是一帧10位的异步串行通信方式,包括1个起始位(0),8个数据位和一个停止位(1),其帧格式如下:
(1) 数据发送
当TI=0时,执行“MOV SBUF,A”指令后开始发送,由硬件自动加入起始位和停止位,构成一帧数据,然后由TXD端串行输出。发送完后,TXD输出线维持在“1”状态下,并将SCON中的TI置1,表示一帧数据发送完毕。 (2) 数据接收
RI=0,REN=1时,接收电路以波特率的16倍速度采样RXD引脚,如出现由“1”变“0”跳变,认为有数据正在发送。
在接收到第9位数据(即停止位)时,必须同时满足以下两个条件:RI=0和SM2=0或接收到的停止位为“1”,才把接收到的数据存入SBUF中,停止位送RB8,同时置位RI。若上述条件不满足,接收到的数据不装入SBUF被舍弃。在方式1下,SM2应设定为0。 (3)波特率
3). 工作方式2和方式3
工作方式2和方式3都是11位异步收发串行通信方式,两者的差异仅在波特率上有所不同。
(1) 数据发送
TI=0,发送数据前,先由软件设置TB8,可使用如下指令完成: SETB TB8 ; 将TB8位置1 CLR TB8 ; 将TB8位置0
然后再向SBUF写入8位数据,并以此来启动串行发送。一帧数据发送完毕后,CPU自动将TI置1,其过程与方式1相同。 (2) 数据接收
REN=1,RI=0时,启动接收
①若SM2=0,接收到的8位数据送SBUF,第9位数 据送RB8。
②若SM2=1,接收到的第9位数据为0,数据不送SBUF;接收到的第9位数据为1,数据送SBUF,第9位送RB8。
对波特率需要说明的是,当串行口工作在方式1或方式3,且要求波特率按规范取1200、2400、4800、9600„时,若采用晶振12MHz和6MHz,按上述公式算出的T1定时初值将不是一个整数,因此会产生波特率误差而影响串行通信的同步性能。解决的方法只有调整单片机的晶振频率fosc,为此有一种频率为
11.0592MHz的晶振,这样可使计算出的T1初值为整数。表9-2列出了串行方式1或方式3在不同晶振时的常用波特率和误差。
五、系统的调试结果
操作步骤
1、按数字键1接受数据
2、按数字键2一次性发送5组数据到显示
3、按数字键3、4查看已存储的前一组及后一组数据
除预先存入外,同样支持随机输入
六、实验总结
这次的课程设计,我觉得我学到了很多的东西,受益匪浅。因为之前已经有了一次单片机课程设计的经验,这次的课程设计相对于上次来说,明显不再是那么手足无措。因为,对于proteus软件的基本使用方法,以及利用这个软件来进行仿真,我都是已经比较了解来。此外,对于电路的设计也有了一定的理解。但是,通过本次的课程设计,我还是很很多体会。我更加懂得了如何利用C语言联系实际来进行编程,同时在编程的过程中,应当了解一些管脚的使用方法,以及在C语言中使用的代码和一些器件的初始化方法,同时也让我明白了自己的一些不足,认识到仅仅把书本上的知识学好是远远不够的,需要拓宽自己的知识面,将所学习到的东西运用到实践中去,这样才能够应付未来的挑战。
当然在本次课程设计中,我遇到了很多之前没有想到会遇到的困难。此时,图书馆和INTERNET成了我们很好的助手。在查阅资料的过程中,我们要判断优劣、取舍相关知识,不知不觉中我们查阅资料的能力也得到了很好的锻炼。我们学习的知识是有限的,在以后的工作中我们肯定会遇到许多未知的领域,这方面能力的提升便会使我们受益非浅。而且在设计过程中,总是遇到这样或那样的问题。有时发现一个问题的时候,需要做大量的工作来进行调试,然后才能解决。自然而然,我分析问题解决问题的能力得到了增强。为以后的工作积累了经验,增强了信心。通过这次的软件设计,我熟悉了单片机开发的每个步骤,它不但检查了我的整个知识面的掌握程度,知道了自己的不足,让我更加牢固的掌握了单片机方面的相关知识!这次的软件设计也让我学会了在遇到问题时,如何冷静的思考问题以及解决问题!更让我懂得了学习贵在坚持。我学到了更多以前没有学到过的知识。在这里,我很感谢指导老师和同学给我的帮助,使我能顺利完成我的软件设计。
总之通过本次设计,让我很好的锻炼了理论联系实际,如何把理论应用于实际,又如何实践中遇到的问题怎样用理论去解决。感谢学校给我们大家这样一次课程设计的机会,为我们以后的工作打下了坚实的基础。
附录 参考文献
雷晓平,李晓东,罗海天.2006.单片机原理及应用.机械工业出版社 曹柏荣.2003.单片机原理及其应用技术.上海:原子能出版社 魏泽鼎.2005.单片机应用技术与实例.北京:电子工业出版社 陈小忠,黄宁,单片机接口技术实用子程序.人民邮电出版社 吴金戍,等.2002. 8051 单片机实践与应用.北京:清华大学出版社 金奎焕.1997.如何使用KEIL8051C编译器.北京:电子工业出版社
附录 程序清单
头文件1
#include \"uart.h\"
/************************************ 串口初始化
************************************/ void Uart_Init(void) { SCON = 0x50;//设置串口8位UART,波特率由T1产生,允许接收 PCON = 0x80;//SMOD为1,波特率不加倍 TMOD = 0x20;//定时器1设置为方式2,8位自动重装载 ES = 1;//允许串口中断 TH1 = 0xfa;//设定重装载值 TL1 = 0xfa;// TR1 = 1;//启动定时器产生波特率 }
/************************************ 发送一个字符
************************************/ void PutChar(unsigned char dat) { SBUF = dat;//将待发送数据写入发送缓冲区 while(!TI);//等待发送结束 TI = 0; }
/************************************ 接收一个字符
************************************/ unsigned char GetChar(void) { while(!RI) ;//查询RI接收标志位,接收到数据时置位 RI = 0; return SBUF; }
/************************************** 发送一字符串,查询方式发送 参数:待发送字符串指针
***************************************/ void PutString(unsigned char * PutStringPtr) {
}
unsigned char * Ptr; Ptr = PutStringPtr; while(*Ptr!='\\0') { SBUF = *Ptr; while(!TI);//等待发送结束 TI = 0; Ptr++;//指向下一个待发送的字符 }
头文件2
#include \"keyscanmode.h\"
/************************** 8255端口地址分配定义
**************************/
xdata unsigned char PA8255ADDR _at_ (BASE8255+0); xdata unsigned char PB8255ADDR _at_ (BASE8255+1); xdata unsigned char PC8255ADDR _at_ (BASE8255+2);
xdata unsigned char CON8255ADDR _at_ (BASE8255+3);/************************ 延时函数
*************************/ void SoftDelay(unsigned int time) { unsigned char n; while(time--) { for(n=0;n<50;n++); } }
/************************ 8255工作方式选择
*************************
void Set_8255_Mode(unsigned char mode) { CON8255ADDR = mode; }
/************************************** 键盘扫描
**************************************/ unsigned char keyscan(void) { unsigned char key_value=0,temp=1,i;
Set8255_Mode(0x81);//模式0,PA口输出,下PC口输入(PA为矩阵键盘的行,下PC口为列) PA8255ADDR = 0x00;//PA口输出低电平 PC8255ADDR = 0x0f;// if((PC8255ADDR&&0x0f)!=0x0f)//读取8255的下PC口,即读取矩阵按键的列状态 { SoftDelay(50);//软件延时去抖 if((PC8255ADDR&0x0f)!=0x0f)//如果矩阵按键列状态不为0x0f,则说明有键按下 { //开始逐行扫描程序 for(i=0;i<4;i++) { PA8255ADDR = ~temp;//仅PA0口输出低电平,即键盘的第一行输出为0电平 switch (PC8255ADDR&0x0f) { case 0x0e ://看是否为第一列 return (key_value); case 0x0d ://看是否为第二列 return (key_value+1); case 0x0b ://看是否为第三列 return (key_value+2); case 0x07 ://看是否为第四列 return (key_value+3); default : temp<<=1;//左移一位,为扫描下一行作好准备 key_value+=4; break; } } } else return 0xff;//说明无键被按下 } else return 0xff;//说明无键被按下 }
主程序
#include #include\"keyscanmode.h\" #include \"uart.h\" /*************************************** 数码管显示编码 ***************************************/ const unsigned char display_numb[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8, //0 1 2 3 4 5 6 7 0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e, //8 9 A b C d E F 0x7f,0xff};// //点 全灭 const unsigned char seg_position[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xc0};//数码管的位选择编码 //1 2 3 4 5 6 全选 位数码管 unsigned char send_data[10][6]={5,4,3,2,1,0, 6,5,4,3,2,1, 7,6,5,4,3,2, 8,7,6,5,4,3, 9,8,7,6,5,4, 0,9,8,7,6,5, 1,0,9,8,7,6, 2,1,0,9,8,7, 3,2,1,0,9,8, 4,3,2,1,0,9}; unsigned char current[6]=0; unsigned char n=0; #define SEG P0 /************************************* 声明为外部变量 *************************************/ extern xdata unsigned char PA8255ADDR ; extern xdata unsigned char PB8255ADDR ; extern xdata unsigned char PC8255ADDR ; extern xdata unsigned char CON8255ADDR; /************************************************** 延时函数 **************************************************/ void DelayMs(unsigned int count1) { while (count1--) { unsigned char count2 = 0; for (; count2<30; count2++); } } /****************************** 数码管显示函数 ******************************/ void display(unsigned char num[]) { unsigned char i; for(i=0;i<6;i++) { P1 = seg_position[i];//6位数码管全部显示 SEG = display_numb[num[i]]; DelayMs(10); } } /************************************************* 变量定义 ************************************************/ unsigned char dat_temp; unsigned char flag=0,count=0; /************************************** 串口中断函数 **************************************/ void Uart_ISR(void) interrupt 4 { if(TI)//判断是否为发送中断 { TI = 0; } if(RI)//判断是否为接收中断 { RI = 0; } } void KeyPress(unsigned char press) { unsigned char i,rev; switch(press) { case(0): //由0开始发送全部10组数据 { for(n=0;n<10;n++) { for(i=0;i<6;i++) { PutChar(send_data[n][5-i]+48); } PutChar('\\n'); } n=9; break; } case(1): //从COM口接收一串数据 { for(i=0;i<6;i++) { rev=GetChar(); current[5-i]=rev-48; send_data[n][5-i]=rev-48; } break; } case(2): { for(i=0;i<6;i++) { PutChar(send_data[n][5-i]+48); } PutChar('\\n'); break; } case(3): { break; } case(4): { n++; if(n==10) n=0; break; } case(5): { if(n==0) n=9; else n--; break; } default: break; } } /************************************** 主函数 **************************************/ void main(void) { unsigned char KeyValue=0xff,temp=0xff; Uart_Init();//串口初始化 EA = 0; PutString(\"Happy new year !\\n\"); while(1) { if((KeyValue=keyscan())!=0xff)//判断是否有按键按下,如有读取键值 { KeyPress(KeyValue); } display(send_data[n]); SoftDelay(1);//软件延时以利用数码管的显示 } } 因篇幅问题不能全部显示,请点此查看更多更全内容