基于8255的LED显示串口通信机设计实验报告
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
基于8255的LED显示串口通信机设计
一、设计要求
(1)基本要求:
1设计一串口通信程序,波特率9600,通过RS232串口自环。
自动循环发送数据串(设计在程序中)
接收并存储和显示该数据串
2数据串单次发送由按键启动,接收端显示数据串和数据串数、正确接收数和错误数。
3数据串选择发送(预存10种数据串),通过按键选择发送,接收、存储并按序显示。
(2)发挥部分:
4数据串滚动显示。
5数据串滚动显示方向和速度可控
6单片机与PC机的通信,单片可以发送任意ASCII字符串到PC机,PC机发送数据串到单片机,并在显示器上显示,格式和方法自定。
7设计波特率可变的通信程序,每次按键波特改变一次,对测通信。
8接收侧波特率自动跟踪,当发送端改变波特率后,定时地重复发送一数据串,检测接收端能正确接收和显示数据和波特率值。
二、要求分析
根据提供的51单片机的串口通信机的要求,编写相对应的程序,实现其功能,并利用已有的硬件V14开发板来检验和实现基于8255的LED串口通信机功能。
要求熟悉单片机开发工具的使用,学会MeDwin,Keil,Protel等软件的使用方法。
熟悉实验板资源,学会程序的下载和运行,系统硬件调试方法。
用标准测试程序对实验板的各部分资源测试,并记录测试结果,维修不可用资源。
设计一能与PC机通信的串行通信终端.
要求:学习使用单片机的内置串行接口,实现两机(单片机与PC机)的多种通信方法。
三、实验板功能模块结构
四、总体方案的确定
本次课程设计是在理论课程的基础上,目的在于培养我们的动手能力,通过电路设计、理论计算、实际编程、调试、测试、分析查找故障,解决在实际设计中的问题,使设计好的电路能正常工作,并能结合实际的实验板进行下载测试。
在此基础上根据实验大纲的要求,按“51单片机课程设计选题表” 每两人一题(随机分配),实现其功能。
在考虑本次设计过程中,依据设计基本要求,并且根据所提供的实验板,研究其原理结构图,我们把8255器件用来扩展I/O 口,外接一4X4键盘,以及通过该I/O 口外接一LED 显示器。
1、串行通信原理
串行通信是数据的各位在同一根数据线上依次逐位发送或接收。
目前串行通信在单片机双机、多机以及单片机与PC 机之间的通信等方面得到了广泛应用。
串口通信示意图 外 LED 显示模块 4X4 RS232C 口
部 键盘
扩
展
74LS373 8255 8155 DAC0832
IO 输出口 IO 口 IO/M 口 模数转换器
ADC0805
地址分配GAL 数模转换器
6264
RAM 8031CPU
其它
图。
实验板功能模块结构
串行通信按同步方式可分为异步通信和同步通信两种基本通信方式。
同步通信是一种连续传送数据的通信方式,一次通信传送多个字符数据,称为一帧信息。
数据传输速率较高,通常可达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<reg51.h>
#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);//软件延时以利用数码管的显示
}
}。