单片机原理及应用实践教程 课件 徐锋 第5-8章 串口通信-单片机应用系统设计_第1页
单片机原理及应用实践教程 课件 徐锋 第5-8章 串口通信-单片机应用系统设计_第2页
单片机原理及应用实践教程 课件 徐锋 第5-8章 串口通信-单片机应用系统设计_第3页
单片机原理及应用实践教程 课件 徐锋 第5-8章 串口通信-单片机应用系统设计_第4页
单片机原理及应用实践教程 课件 徐锋 第5-8章 串口通信-单片机应用系统设计_第5页
已阅读5页,还剩229页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第五章

串口通信

目录CONTENTS01串口通信原理串行通信基础概念、通信协议、数据帧格式与波特率原理04串口初始化编程寄存器配置流程、收发函数编写、

stdio

库函数的串口重定向03串口工作方式与波特率配置4种工作方式的特点、定时器波特率计算与配置方法02

51单片机串口结构串口寄存器、数据收发机制、控制位与状态位解析05基础串口通信实践串并行转换、单片机与PC双向数据收发的仿真实现06进阶应用与头文件制作多字符通信、通用串口通信头文件的封装与复用学习目标01基础认知掌握理解串口通信的概念与原理,熟悉51单片机串口的内部结构、工作方式及相关寄存器配置逻辑。02核心参数精通掌握波特率的计算方法,能够根据需求合理设置定时器/计数器参数,实现稳定的串口通信速率配置。03编程能力落地掌握51单片机串口初始化、收发数据的编程方法,理解

stdio.h

库函数在串口通信中的应用方式。04工程实践应用能够搭建单片机与计算机的双向串口通信电路,实现数据收发功能,并可制作通用串口通信头文件。5.1串行异步通信原理1、计算机通信是指将计算机技术与通信技术相结合,完成计算机与外设或计算机与计算机之间的信息交换。计算机通信可以分为两大类:并行通信与串行通信。2、调制解调器利用调制器把数字信号转换成模拟信号,发送到通信线路上,再由解调器把从通信线路上接收到的模拟信号转换成数字信号。由于通信是双向的,调制器和解调器合并在一个装置中,这就是调制解调器。3、并行通信与串行通信并行通信:多数据线同时传输,速度快但成本高串行通信:单数据线逐位传输,成本低但控制复杂串行通信又可以分同步串行通信和异步串行通信。在多微机系统及现代测控系统中,信息交换多采用串行通信方式。4、同步串行通信

进行同步串行通信时,要建立发送方时钟对接收方时钟的直接控制,使双方达到完全同步。此时,传输数据的位之间的距离均为“位间隔”的整数倍,同时传送的字符间不留间隙,即保持位同步关系,也保持字符同步关系。5、异步串行通信

以字符(帧)为单位传输字符间时间间隔任意每个字符包含起始位、数据位、停止位6、单片机和计算机的串口通信单片机要实现与计算机的串行通信一般采用两种方式:一是串口接串口,但需要在串口间进行电平转换,即RS-232电平与TTL电平转换;二是计算机的USB口接单片机的TTL串口,即进行USB口与TTL串口之间的信号转换。(1)RS-232电平与TTL电平转换计算机的串口是RS-232电平(-15~-5V为1,+5~+15V为0),而单片机的串口是TTL电平(大于+2.4V为1,0V或小于-0.7V为0),两者之间需要有一个电平转换电路实现RS-232电平与TTL电平的相互转换。(2)串行总线标准RS-232目前最常用的串口标准,用来实现计算机与计算机之间、计算机与外设之间的数据通信。RS-232协议以-15~-5V表示逻辑1;以+5~+15V表示逻辑0。RS-232串口总线适用于设备之间的通信距离不大于15m,传输速率最大为20kbit/s的应用场景。(3)USB口转串口CH340是USB总线的转接芯片,内置了USB通信协议的基本固件、处理海量存储设备的专用通信协议的固件、SD卡的通信接口固件,以及FAT16、FAT32和FAT12文件系统的管理固件。这些固件使得单片机系统能够无须了解底层的USB协议、SD卡操作及FAT文件系统,即可通过简单的命令快速读写U盘或SD卡中的文件,实现USB口转串口或者USB口转打印口。

在串口方式下,CH340提供常用的MODEM联络信号,用于为计算机扩展异步串口,或者将普通的串口设备直接升级为USB设备USB口转TTL串口的电路采用的是USB总线转接芯片CH340C,该芯片内置晶振,可以省去晶振电路,节省

成本。主要原理是CH430C芯片与单片机采用UART协议进行通信,计算机与CH340C芯片之间采用USB协议传递UART数据,计算机端安装的CH340驱动则负责解析USB协议以获取单片机的UART数据。5.2.1串口结构

51单片机的UART串口内部结构主要由串口数据缓冲寄存器SBUF、移位寄存器、串口控制寄存器SCON和波特率发生器等组成,其中特殊功能寄存器SBUF和SCON供软件访问和控制串口。5.2单片机的串口通信1、串口数据缓冲寄存器SBUF

串口数据缓冲寄存器实际上是两个寄存器,即接收缓冲寄存器与发送缓冲寄存器同名且占用同一个地址99H,虽然二者地址相同,但由于发送数据采用的是写指令,接收数据采用的是读指令,因此不会产生混淆。2、串口控制寄存器SCON51单片机串行通信方式的选择、接收和发送控制,以及串口的标志都由特殊功能寄存器SCON控制和指示。SCON可位寻址,格式如表所示。(1)SM0、SM1:串口工作方式选择位,可设置以下4种工作方式。①方式0:同步移位寄存器,波特率为fosc/12。②方式1:8位数据UART,波特率可变。③方式2:9位数据UART,波特率为2SMOD×fosc/64。④方式3:9位数据UART,波特率可变。(2)SM2:多机通信控制位,主要用于方式2和方式3。REN:接收允许标志位,由软件置1或清0。若REN=1,则允许串口接收数据;若REN=0,则禁止串口接收数据。(3)REN:接收允许标志位,由软件置1或清0。若REN=1,则允许串口接收数据;若REN=0,则禁止串口接收数据。(4)TB8:在方式2或方式3中,发送数据的第9位,需要由软件写入1或0。在方式0和方式1中,该位不使用。(5)RB8:在方式2或方式3中,接收数据的第9位,由硬件将接收到的第9位数据存入RB8,作为奇偶校验位或地址帧/数据帧的标志位。在方式1中,若SM2=0,则RB8接收到的是停止位,方式0不使用该位。(6)TI(TransmitInterrupt,发送中断):发送中断标志位。发送数据前必须用软件将该位清0,发送过程中TI保持低电平,发送完一帧数据后,由硬件自动将该位置1。如果再次发送数据,必须由软件再次将该位清0。在方式0中,当串行发送第8位数据结束时,或在其他方式中,串行发送停止位开始时,由内部硬件将TI置1,向CPU发送中断请求。在中断服务程序中,必须用软件将其清0,取消此中断请求。(7)RI(ReceiveInterrupt,接收中断):接收中断标志位。接收数据前必须用软件将该位清0,接收过程中RI保持低电平,接收完一帧数据后,由硬件自动将该位置1。如果再次接收数据,必须由软件再次将该位清0。在方式0中,当串行接收第8位数据结束时,或在其他方式中,串行接收停止位中间时,由内部硬件将RI置1,向CPU发送中断请求。在中断服务程序中,必须用软件将其清0,取消此中断请求。PCON是用于电源控制的特殊功能寄存器,可位寻址,其标志位如表所示。PCON中只有最高位SMOD与串口工作有关。SMOD(PCON.7)为波特率倍增选择位。在串口工作于方式1、方式2、方式3时,波特率与SMOD有关,当SMOD=1时,波特率提高一倍。单片机复位时,SMOD=0。3、电源控制寄存器PCON5.2.2串口工作方式1、方式0方式0为同步移位寄存器的I/O方式,主要用于扩展并行输入或输出口。数据由RXD(P3.0)引脚输入或输出,同步移位脉冲由TXD(P3.1)引脚输出。以8位数据为一帧,从低位开始发送和接收。每个机器周期发送或接收1位,波特率固定为fosc/12。在方式0下,发送和接收数据无起始位和停止位。发送过程从写SBUF开始,当8位数据传送完时,TI被置1在接收过程中,当8位数据接收完时,RI被置1,可通过读取SBUF,将串行数据读入2、方式1方式1是以10位数据为一帧的异步通信方式。TXD为数据发送引脚,RXD为数据接收引脚,数据帧的格式(方式1)如图所示,一帧数据包括1位起始位(为0)、8位数据位、1位停止位(为1)。在方式1下发送数据时,数据写入SBUF后,开始发送,此时由硬件加入起始位和停止位,构成一帧数据,由TXD引脚串行输出。输出一帧数据后,TXD引脚保持在高电平状态,并将TI置1,通知CPU可以进行下一个字符或数据的发送在方式1下接收数据时,当串口控制寄存器SCON的接收允许标志位REN=1,且接收到起始位后,在位采样脉冲(移位脉冲)的控制下,把接收到的数据移入接收SBUF中,停止位到来后,把停止位送入串口控制寄存器SCON的RB8位中,并置位RI,通知CPU已接收完一个字符或数据3、方式2和方式3方式2和方式3都是以11位数据为一帧的异步通信方式,两者的区别在于波特率设置方法不同。数据帧的格式(方式2和方式3)如图所示。方式2和方式3的数据帧包括1位起始位(为0)、9位数据位(含1位附加的第9位,发送时为SCON中的TB8,接收时为RB8)、1位停止位(为1),一帧数据为11位。方式2的波特率固定为晶振频率的1/64或1/32,方式3的波特率由定时/计数器T1的溢出率决定。

发送数据前,先设置SCON中的TB8,然后将要发送的数据写入SBUF即可启动发送。TB8被当作数据的第9位,自动叠加到数据帧的D8位上,这一位数据可以作为奇偶校验位使用,在多机通信系统中,该位也可以作为区分地址和数据的特征标志位使用。接收数据时,先设置接收允许标志位REN为1,使串口处于允许接收状态;再根据SM2和所接收到的RB8的状态,决定接收完数据后是否置位RI。当SM2=0时,无论RB8是0还是1,RI都置1,串口接收数据。当SM2=1,RB8=1时,表明是多机通信,所接收的数据是地址帧,RI置1,通过串口接收发送来的地址信息。当SM2=1,RB8=0时,表明接收到的为数据帧,RI不置1,丢弃SBUF中所接收的数据帧。在多机通信系统中,只有与主机发送的地址相匹配的从机才能进行数据的接收。5.2.3串口初始化编程1.串口波特率的设置

51单片机串口在方式0和方式2下的波特率是固定的,在方式0下,串口的波特率=fosc/12;在方式2下,串口的波特率=(2SMOD/64)×fosc。fosc为系统晶振频率,SMOD是电源控制寄存器PCON的第7位,即波特率倍增选择位。方式1和方式3使用定时/计数器T1作为波特率发生器,由于T1工作于方式2时具有自动重载功能,所以T1采用方式2,方式1、方式3的波特率=(2SMOD/32)×(T1溢出率)。其中T1溢出率=1/(T1溢出周期),T1溢出周期=(256–T1计数初值)×12/fosc。由上面的公式可知,波特率和T1的计数初值、晶振频率有关,如果晶振频率选择不当,可能会造成求得的T1计数初值为非整数,这样势必造成波特率误差,影响通信的可靠性和效果。在实际应用中,可以采用频率与计算机通信控制时钟频率(1.8432MHz)成整数倍的晶振作为单片机系统晶振校准这一问题。可以选用11.0592MHz(11.0592=6×1.8432)作为晶振频率,则可计算得到定时/计数器T1的计数初值。常用波特率与计数初值的关系如表。2.比特率和波特率比特率是每秒钟传输二进制代码的位数,单位是bit/s(位/秒,bitspersecond)。波特率是一个衡量通信速度的参数,是指每秒传输的符号数,若每个符号所含信息量为1比特(即一位二进制数码),则波特率等于比特率。在计算机通信中,常将比特率称为波特率。3.串口初始化及应用编程根据波特率确定定时/计数器T1的计数初值后,串口初始化操作步骤:(1)设置定时/计数器T1工作于方式2,并设置TMOD。(2)确定串口工作方式,设置SCON。(3)若需要波特率倍增,则设置PCON。(4)对TH1和TL1送初值。(5)开总中断EA、串口中断ES。(6)启动定时/计数器T1,将TR1置1。①以串口工作于方式1,波特率为9600bit/s为例,串口初始化函数代码。voidcom_initialize(void){TMOD|=0x20;//T1工作于方式2(自动重载模式)SCON=0x50;//串口工作于方式1PCON=0x00;TH1=0xFD;//设置波特率为9600bit/sTL1=0xFD;EA=1;//开总中断ES=1;//开串口中断TR1=1;//启动T1}发送数据时,要发送的数据通过内部累加器A送入发送SBUF,在发送控制器的控制下组成数据帧结构,并自动以串行方式从TXD引脚输出,发送完一帧数据,将TI置1。通过中断或查询TI来了解数据的发送情况。需要注意的是,TI只能用软件清0。②通过串口发送一个字符或1B数据的函数代码voidsend(unsignedcharc)//发送字符c{SBUF=c;//将字符c送入发送SBUFwhile(TI==0);//查询TI是否置1,等待发送结束TI=0;//发送结束,将TI清0,为下次发送做准备}CPU通过RXD引脚接收数据,每接收完一帧数据,自动置位RI,通过中断或查询RI来了解数据的接收情况,然后将接收SBUF中的数据读回。RI与TI一样,只能用软件清0。③串口通过中断方式接收一个字符或1B数据的函数代码voidreceive()interrupt4//串口中断服务程序{if(RI==1)//判断是否接收完一帧数据{RI=0;//接收完,将RI清0c=SBUF;//从接收SBUF中读回数据给c}else;//未接收完则返回}任务1方式0应用:串并行转换任务2单片机与计算机的双向串口通信任务3多字符串口通信任务4串口通信头文件任务1方式0应用:串并行转换

串口工作于方式0时,可通过外接移位寄存器实现串并行转换。在这种方式下,数据为8位,只能从RXD引脚输入或输出,TXD引脚用于输出同步移位脉冲,其波特率固定为晶振频率的1/12。由软件置位SCON中的REN位后才能启动串行接收,在CPU将数据写入发送SBUF后,立即启动发送。待8位数据传输完后,由硬件将SCON中的TI置1,TI必须由软件清0。

基于Proteus平台设计单片机串口外接一个8位串入并出移位寄存器74LS164芯片,构成单片机输出接口电路。5.3工程实践参考例程

的代码如下。#include<reg51.h>#include<intrins.h>#defineuintunsignedint#defineucharunsignedcharvoiddelay(uintx){uchari;while(x--)for(i=0;i<120;i++);}voidmain(){ucharc=0x80;SCON=0x00;//串口工作于方式0while(1){c=_crol_(c,1);//c循环左移

SBUF=c;while(TI==0);//等待发送结束TI=0;//发送结束,将TI清0delay(400);}}任务2单片机与计算机的双向串口通信

单片机与计算机通信时,其硬件接口技术主要是电平转换,示。在DOS操作环境下,要实现单片机与计算机的通信,只要直接对计算机串口的通信芯片8250进行串口地址操作即可。在Windows操作环境下,由于系统硬件的无关性,不再允许用户直接操作串口地址。如果用户要进行串行通信,可以调用Windows操作系统中的API函数,但其使用较为复杂。本任务中,我们使用计算机的串口调试程序SSCOM32或STC-ISP内嵌的串口调试助手。要注意的是,在发送区中输入要发送的数据,如果选中“十六进制发送”单选按钮,那么发送的数据是十六进制的,必须输入两位数据;如果没有选中“十六进制发送”单选按钮,则发送的是ASCII。参考例程的代码如下。#include<reg52.h>#defineucharunsignedchar#defineuintunsignedintucharbuf;voidsenddata(uchardat){SBUF=dat;while(!TI);TI=0;}voidmain(void){SCON=0x50;//设定串口的工作方式PCON=0x00;//波特率不倍增TMOD=0x20;//T1工作于方式2,用于产生波特率EA=1;ES=1;//允许串口中断TL1=0xfd;TH1=0xfd;//设置波特率为9600bit/sTR1=1;while(1);}/*********************************************************

串口中断服务程序*********************************************************/voidserial()interrupt4{ES=0;//关闭串口中断RI=0;//清除串口接收中断标志位buf=SBUF;//从串口缓冲区接收数据senddata(buf);ES=1;//允许串口中断}思考单片机如何将接收到的数据进行一定的处理之后再发送给计算机,如将计算机发送来的数据进行简单的加密处理后再发送给计算机。组队讨论试一试吧!任务3多字符串口通信计算机向串口发送1B数据,将波特率设置为9600bit/s。设置好后,按下S2键,单片机向计算机发送一串字符。参考例程的代码如下。#include<reg52.h>#definejingzhen11059200UL/*使用频率为11.0592MHz的晶振*/#definebotelv9600UL/*将波特率设置为9600bit/s*/unsignedcharzifuchuan[]="你好51MCU!";//待显示字符volatileunsignedcharsending;sbits2=P3^4;voiddelay(unsignedchari){unsignedcharj,k;for(j=i;j>0;j--)for(k=90;k>0;k--);}voidinit(void)//串口初始化{EA=0;//暂时关闭总中断TMOD&=0x0F;//T1的工作方式由TMOD的高4位确定TMOD|=0x20;//T1工作于方式2(自动重装模式)SCON=0x50;//串口工作于方式1TH1=256-jingzhen/(botelv*12*16);//计算T1重装值TL1=256-jingzhen/(botelv*12*16);PCON|=0x80;//串口波特率加倍ES=1;//允许串口中断TR1=1;//启动T1REN=1;//允许接收

EA=1;//开总中断}voidsend(unsignedchard)//发送1B数据,形参d即为待发送的数据{SBUF=d;//将数据写入发送SBUFsending=1;//设置发送标志while(sending);//等待发送完毕}voidsendc(unsignedchar*pd){while((*pd)!='\0')//发送字符串,直到遇到空字符才结束{send(*pd);//发送一个字符pd++;//指针指向下一个字符}}voidmain(){init();while(1){if(s2==0){delay(20);if(!s2){while(!s2);sendc(zifuchuan);}}}}voiduart(void)interrupt4//串口发送中断服务程序{if(RI)//接收到数据{RI=0;//清除接收中断标志位}else//发送完1B数据{TI=0;sending=0;//清除发送标志}}

注意函数sendc()的形参是指针变量,而实参是数组名,即指针。在串口调试助手的接收/键盘发送缓冲区观察单片机发送来的字符串。

思考计算机如何发送多个字符给单片机,组队讨论试一试吧!任务4串口通信头文件参考例程的代码如下。#include<reg52.h>#include<stdio.h>#defineXTAL12000000#definebaudrate9600#defineOLEN8//串口发送SBUF大小unsignedcharostart;//发送SBUF起始索引unsignedcharoend;//发送SBUF结束索引charidataoutbuf[OLEN];//发送SBUF存储数组#defineILEN8//串口接收SBUF大小unsignedcharistart;//接收SBUF起始索引unsignedchariend;//接收SBUF结束索引charidatainbuf[ILEN];//接收SBUF存储数组bitbdatasendfull;//发送SBUF满标志bitbdatasendactive;//发送有效标志/*串口中断服务程序*/staticvoidcom_isr(void)interrupt4using1{charc;if(RI)//接收中断{c=SBUF;//读字符RI=0;//清除接收中断标志位if(istart+ILEN!=iend){inbuf[iend++&(ILEN-1)]=c;//从接收SBUF中接收数据}}if(TI)//发送中断{TI=0;//清除发送中断标志位if(ostart!=oend){SBUF=outbuf[ostart++&(OLEN-1)];//向发送SBUF传送字符sendfull=0;//设置发送SBUF满标志位头文件是扩展名为“.h”的文件,包含C函数声明和宏定义,可被多个源文件引用共享。头文件可分为程序员编写的头文件和编译器自带的头文件。若想在程序中使用头文件中的函数,需要使用预处理命令#include将头文件包含在程序中。编写一个单片机与计算机进行双向串口通信的通用程序,保存为头文件。}else{sendactive=0;//设置发送无效}}}//写字符到发送SBUFvoidputbuf(charc){if(!sendfull)//如果发送SBUF不满就发送{if(!sendactive){sendactive=1;//直接发送一个字符SBUF=c;//写到发送SBUF,启动发送}else{ES=0;//暂时关闭串口中断outbuf[oend++&(OLEN-1)]=c;//向发送SBUF传送字符if(((oend^ostart)&(OLEN-1))==0){sendfull=1;}//设置发送SBUF满标志ES=1;//打开串口中断}}}//putchar:中断服务程序控制本函数的执行//当其他程序中包含此头文件时,使用printf函数会反复调用putchar函数charputchar(charc){if(c=='\n')//增加新的行{while(sendfull);//等待发送SBUF空putbuf(0x0D);//对于新行,在发送换行符(0x0A)前插入回车符(0x0D)}while(sendfull);putbuf(c);return(c);}//_getkey:中断服务程序控制本函数的执行//替换标准库函数_getkeychar_getkey(void){charc;while(iend==istart)//判断接收SBUF起始索引是否等于接收SBUF结束索引{;}ES=0;c=inbuf[istart++&(ILEN-1)];ES=1;return(c);}/*初始化串口和UART波特率函数*/voidcom_initialize(void){istart=0;iend=0;ostart=0;oend=0;sendactive=0;sendfull=0;TMOD|=0x20;//设置定时/计数器T1工作于方式2(自动重载模式)SCON=0x50;//设置串口工作于方式1TH1=0xFD;//设置波特率为9600bit/sTL1=0xFD;PCON=0x00;TR1=1;//启动T1ES=1;//开串口中断}voiduart_Init(){com_initialize();EA=1;//开总中断}

bdata区允许软件以位为单位访问存储器,这是一项非常有用的功能,仅以一条指令就可以实现对位的置位、清除、与、或等操作,简化了设计,如:bitbdatasendfull;//发送SBUF满标志bitbdatasendactive;//发送有效标志#defineXTAL12000000#definebaudrate9600

这两条语句分别用于声明所使用晶振的频率为12MHz,串口的波特率为9600bit/s。根据波特率公式反推计数初值可得计数初值=2n-[(2SMOD/32)·(fosc/12)/波特率]=28-[(20/32)×(12×106/12)/9600]=253=0xFD#defineOLEN8#defineILEN8

这两条语句分别用于定义串口发送SBUF和串口接收SBUF的位数是8位。AT89C52内部有附加的128B数据存储器,称为idata区,其地址与特殊功能寄存器是重叠的。这个空间通常用于存储使用频繁的数据,如:charidataoutbuf[OLEN];//发送SBUF存储数组charidatainbuf[ILEN];//接收SBUF存储数组函数com_initialize()对串口进行了初始化并设置波特率为9600bit/s,串口将工作于方式1;函数uart_Init调用了com_initialize()并打开了总中断。putbuf函数用于写字符到发送SBUF,_getkey函数用于从AT89C52的串口中读入一个字符,然后等待字符输入,putchar函数则通过调用putbuf函数输出字符。注意,putchar函数只能输出一个字符,printf函数通过调用putchar函数来输出字符串。com_isr函数是串口中断服务程序,它的作用就是进行串口中断处理,接收和发送数据。4是串口的中断编号,1表示使用第1组工作寄存器。在C编译系统中,Keil对程序进行编译之前,要先对某些程序进行预处理,然后将预处理的结果和源程序一起进行正常的编译处理,进而得到目标代码,通常的预处理命令都用“#”开头。预处理命令包括以下几种。(1)宏定义。宏定义即#define指令,形式为:#define宏名

字符串(2)头文件包含。是指一个源文件可以将另一个源文件的全部内容包含进来。但要注意的是,对文件包含并不是把两个文件连接起来,而是编译时将其作为一个源程序进行编译,得到一个目标文件,如hex文件。被包含的文件常在文件的头部,所以称为“头文件”,可以以“.h”为后缀,也可以以“.c”为后缀。在源程序中,任何形如#include"file"或#include<file>的行都被替换成由file(文件名)所指定的文件的内容。另外应注意,引用头文件时,如果一个头文件被引用两次,编译器会处理两次头文件的内容,这将产生错误。为了防止这种情况发生,标准的做法是把文件的整个内容放在条件编译语句中,也就是在程序的开始和结尾加上如下代码:#ifndefHEADER_FILE#defineHEADER_FILEtheentireheaderfilefile#endif更多头文件的使用语法和问题,请搜索网络进行查阅。我们之前完成过一个通过按键输入控制三轮小车运行的任务,下面考虑计算机通过串口向单片机发出指令控制三轮小车的运行状态和时间,即编程实现单片机串口接收输入的功能验证。参考例程mcu505.c的代码如下。#include<delaytime.h>#include<uart51.h>#include<AT89X52.H>sbitIN1=P1^0;sbitIN2=P1^1;//左直流电动机sbitIN3=P1^4;sbitIN4=P1^5;//右直流电动机sbitEN1=P1^2;//左直流电动机使能引脚sbitEN2=P1^3;//右直流电动机使能引脚//前进voidrun(void){EN1=1;//高电平1使能EN2=1;//高电平1使能

IN1=0;IN2=1;//左直流电动机正转IN3=0;IN4=1;//右直流电动机正转

}//后退voidbackrun(void){EN1=1;EN2=1;IN1=1;IN2=0;//左直流电动机反转IN3=1;IN4=0;//右直流电动机正转

}//停止voidstop(void){EN1=0;//左直流电动机停转EN2=0;//右直流电动机停转

}intmain(void){uart_Init();printf("controlMOTOR!\n");charCMD;intDuration;printf("Programming!\n");printf("Pleaseinputcommandandstateduration:\n");scanf("%c%d",&CMD,&Duration);//运动指令,持续时长while(1){if(CMD==f){run();//前进delay_nms(Duration);}elseif(CMD==b){backrun();//后退delay_nms(Duration);}elsestop();}}当在串口调试助手的发送区内写入“f200”并发送时,串口调试助手会将字符“f”转换为66H,将整数200转换为C8H,转换过程由串口调试助手程序完成,通过串口线发送到单片机的串口。scanf函数通过调用_getkey函数从单片机串口处接收字符“f”,接收SBUF满,将RI置1,进入中断服务程序,取出字符“f”;如此循环,直到全部数据接收完。最后,scanf函数将接收到的数据赋给对应的变量,即将f赋给字符变量CMD,将200赋给整型变量Duration。上机操作试一试吧!当然也可以更改波特率大小,如改为4800bit/s或19200bit/s进行调试,观察串口是否依然正常工作,或者尝试使用别的串口工作模式来进行串口通信。组队讨论试一试吧!感谢观看THANKSFORWATCHING

第六章

I/O口扩展及应用

目录CONTENTS01I/O口扩展基础51单片机I/O资源限制、并行扩展原理与常用扩展芯片介绍04

LCD1602液晶显示应用LCD1602接口电路、存储器与指令系统、时序分析与驱动编程03

矩阵键盘识别与处理矩阵键盘工作原理、行列扫描方法、按键消抖与键值解析02数码管动态显示技术数码管驱动原理、动态扫描实现、多位数码管显示编程05OLED显示模块拓展I2C/SPI接口OLED的工作原理、初始化配置与字符/图形显示实现06综合工程实践数码管显示、矩阵键盘、LCD/OLED的综合项目设计与代码封装学习目标01基础认知掌握理解51单片机I/O口扩展的必要性与并行扩展的基本原理,掌握常用扩展方式的特点与适用场景。02显示技术精通熟练掌握数码管动态显示的驱动原理与编程方法,理解动态扫描的时序逻辑与消抖技巧。03输入交互掌握掌握矩阵键盘的扫描识别原理与编程实现,学会按键消抖与键值处理的常用方法。04工程实践落地熟悉LCD1602/OLED等显示模块的接口时序、指令系统与驱动编程,能够独立完成显示与输入交互的综合项目开发。6.1并行I/O口扩展方式

并行I/O是一种不需要转发的操作,由各进程独立地发出I/O请求,以得到相应的数据区域。I/O性能是制约计算机系统性能提高的重要瓶颈,扩展并行I/O能力可以在一定程度上提高计算机系统的性能。并行I/O扩展有以下两种类型。(1)并行三总线扩展:采用数据总线DB、地址总线AB和控制总线CB的三总线方式,(2)并行I/O口直接扩展:数据与交互信息均由并行I/O口来完成。51单片机内部采用的是并行三总线扩展方式6.2数码管动态显示数码管是由多只LED封装在一起组成8字形器件,有红、绿、蓝、黄等多种颜色。广泛用于仪表、时钟、车站、家电等场合。按LED的连接方式不同,数码管可分为共阴极(CommonCathode,CC)数码管和共阳极(CommonAnode,CA)数码管。共阳极数码管是指将所有LED的阳极接到一起形成公共阳极的数码管,共阳极数码管在应用时应将公共阳极COM接到+5V电源,当某一字段LED的阴极为低电平时,相应字段点亮。共阴极数码管是指将所有LED的阴极接到一起形成公共阴极的数码管。在应用共阴极数码管时,应将公共阴极COM接到地线GND上,当某一字段LED的阳极为高电平时,相应字段点亮。

通常情况下,点亮数码管某一字段至少需要10mA电流,而单片机的I/O口无法提供如此大的电流,所以需要增加数码管驱动电路,可以采用上拉电阻,也可以使用专门的驱动芯片,如八位锁存器74HC573,其输出电流较大,足以点亮数码管。静态显示驱动是指每个数码管的每一个字段都需要用一个I/O口进行驱动,占用I/O口多。但这样做会大大增加电路复杂性,可以通过动态显示技术解决。

动态显示是指一位一位地轮流点亮数码管各位,对于数码管的每一位来说,每隔一段时间点亮一次,利用人眼的视觉暂留效应,采用循环扫描的方式,分时轮流选通数码管各位的公共端,使数码管各位轮流导通显示。人眼的视觉暂留时间为0.05~0.2s,即200ms以内。当扫描速度达到一定程度,即循环点亮间隔时间小于200ms时,人眼就分辨不出来了。尽管实际上数码管各位并非同时点亮,但只要扫描速度足够快,人眼看到的就是一组稳定的显示数据,即认为数码管各位是同时点亮的。当数码管的位数不大于8时,配合74HC573,只需1个P口(如P0口)来分时发送段码和位码数据即可。六位数码管的动态驱动接口电路如图。其中单片机的P2.6引脚和P2.7引脚接两个74HC573的使能端,分别作为位码和段码操作的控制引脚。六位数码管的动态驱动接口电路多位动态显示时,需要设置位码和段码两个数组。共阴极数码管0~F段码如下。tab[16]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}带小数点0~9段码如下。tab_dot[10]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef}六位数码管的位码如下。tab_wei[6]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf}以六位数码管的动态驱动接口电路图为例,编程实现六位数码管的动态显示,参考例程

的代码如下。#include<reg51.h>sbitdula=P2^6;//段选信号的控制引脚sbitwela=P2^7;//位选信号的控制引脚unsignedcharcodewei[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf};//数码管的位码表unsignedcharcodeduan[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d};//0~5的段码表voiddelay(unsignedinti){unsignedintm,n;for(m=i;m>0;m--)for(n=90;n>0;n--);}voidmain(){unsignedcharnum;while(1){for(num=0;num<6;num++){P0=wei[num];wela=1;wela=0;P0=duan[num];dula=1;dula=0;delay(2);}}}上机操作,可以看到数码管各位分别显示数字0~5,改变延时并观察,验证人眼的视觉暂留效应。6.3矩阵键盘识别键盘是由排列成矩阵形式的一系列按键开关组成的,用户通过键盘可以向CPU输入数据、地址和命令。键盘按其结构形式可分为编码式键盘和非编码式键盘两类。编码键盘通过硬件电路直接生成按键信息代码(如ASCII码),并将代码并行或串行传输至CPU,如计算机键盘;非编码键盘则通过软件控制程序处理键盘的行列坐标,具有按键功能可自定义的特性,如矩阵键盘。单片机系统中普遍使用非编码式键盘,使用这类键盘时需要解决以下几个问题。①

按键的识别。②

按键的消抖。③

按键的保护。一般情况下,将按键连接到电路时,内部的上拉电阻或外部的上拉电阻会将按键未被按下时的状态拉高为高电平。当按键被按下时,按键内部会导通,使连接到按键的引脚处于低电平状态,从而表示按键被按下。这种用低电平表示按键被按下的设计方式是较为常见的,因为在数字电路中,低电平通常被认为是逻辑“0”,而高电平被认为是逻辑“1”,使用低电平表示按键被按下,可以更容易地进行逻辑判断和控制。当然,也可以根据特定应用的需求,在设计中采用高电平表示按键被按下,只需根据相应的逻辑处理即可。矩阵键盘通过行和列的交叉连接构成一个矩阵,每个按键都位于一个行和一个列的交点处。矩阵键盘识别按键的方法有两种:一种是行列扫描法;另一种是线反转法。1.行列扫描法

行列扫描法是一种简单且直观的按键扫描方法。通过轮询扫描的方式,逐个检测按键的状态。当有按键被按下时,通过判断对应的行和列,可以确定被按下的是哪一个按键。行列扫描法简单易懂,实现成本较低,适用于规模较小的矩阵键盘。随着矩阵键盘规模的增大,由于需要逐一扫描每个按键,因此扫描效率会降低。行列扫描法的一般过程是先使矩阵键盘上某一行为低电平,而其余行接高电平,然后读取列值,如果读取到的列值中某位为低电平,则表明有按键被按下,进而判断被按下按键所在的位置,找到被按下的按键后,读入相应的键值,再转至相应的按键处理程序。否则扫描下一行,直到扫描完所有行。为了防止双键或多键同时被按下,往往从第一行一直扫描到最后一行,若只发现1个被按下的按键,则为按键有效,否则全部作废。以4×4矩阵键盘电路为例,扫描第一行的参考代码如下。unsignedcharkey_matrix_ranks_scan(void){unsignedcharkey_value=0;P3=0xfe;//给第一行赋值0if(P3!=0xfe)//判断第一行是否有按键被按下{delay_10us(1000);//消抖if(P3!=0xfe){switch(P3)//保存第一行按键被按下后的键值

{case0xee:key_value=1;break;case0xde:key_value=5;break;case0xbe:key_value=9;break;case0x7e:key_value=13;break;}}}

while(P3!=0xf7);//等待按键被松开P3=0xfd;//给第二行赋值0,其余行全为1......returnkey_value;}其余三行的扫描代码请自行推导补齐,想一想,试一试吧!注意:采用查询方式检测按键时,要加入延时(通常采用软件延时10~20ms)以消除抖动。P口为准双向口,P口的每一位都能独立地定义为输出口或输入口。当P口中的某位用作输入引脚时,必须先向P口锁存器相应位写入“l”。51单片机中所有P口锁存器的各位在复位时均置为“l”,如果后来在P口锁存器某位写入过“0”,则在需要时应重新向该位写入一个“1”,以使其再次成为一个输入引脚。2.线反转法线反转法也是识别按键是否被按下的一种常用方法,该法比行列扫描法速度快,但在硬件上要求行线与列线外接上拉电阻,适用于大规模的矩阵键盘。在线反转法中,先将行线作为输出线,列线写“1”作为输入线,行线输出全“0”信号,即向P3口锁存器写入0xf0;然后采集引脚信号,并将采集到的信号与0xf0进行与(&)运算,若此时有按键被按下,则必定会使某一列线值为0,即与运算的结果不等于0xf0,读入该列线的值;最后,将行线和列线的I/O关系互换,读取行线的输入值。这样当有按键被按下时,必定可读到一对唯一的行、列值。同样以4×4矩阵键盘电路为例,采用线反转法识别矩阵键盘按键的参考代码如下。while(1){P3=0xf0;//给所有行赋值0,给所有列赋值1if((P3&0xf0)!=0xf0)//判断是否有按键被按下{delay10ms();//软件消抖if((P3&0xf0)!=0xf0){P3=0xf0;//保存行为0,采集按键被按下后的列值switch(P3){case0xe0:i=0;break;//第一列case0xd0:i=1;break;//第二列case0xb0:i=2;break;//第三列case0x70:i=3;break;//第四列default:break;}}}delay10ms();//软件消抖P3=0x0f;//反转行线和列线的电平if((P3&0x0f)!=0x0f){delay10ms();//软件消抖if((P3&0x0f)!=0x0f){P3=0x0f;//保存列为0,采集按键被按下后的行值switch(P3){case0x0e:j=0;break;//第一行case0x0d:j=1;break;//第二行case0x0b:j=2;break;//第三行case0x07:j=3;break;//第四行default:break;}P3=num[j][i];//输出唯一对应的按键值}}}线反转法的原理可以简单概括为:4个行引脚推挽输出低电平(置0),4个列引脚上拉输入,如果有按键被按下,会连通行与列,导致某个列引脚电压被拉低,故输出寄存器不再是0x0f,而是0x0e、0x0d、0x0b或0x07。6.4LCD1602液晶(LiquidCrystal)是奥地利植物学家莱尼兹于1888年发现的一种特殊的混合物质,其组成物质是有机化合物,即以碳为中心构成的化合物。这种物质在常态下介于固态和液态之间,不仅如此,其还兼具固态物质和液态物质的双重特性。当时并没有对该物质的适当称呼,因此称之为液晶。1968年,在美国RCA公司的沙诺夫研发中心,工程师们发现液晶分子在电压的作用下会改变其分子的排列状态,并且可以让射入的光线产生偏转。利用这一原理,RCA公司发明了世界上第一台使用液晶显示的屏幕,即LCD。有TN结型LCD、STN型LCD、TFT型LCD等类型。LCD模块(LiquidCrystalDisplayModule)简称LCM或字符型LCD。字符型LCD是一种专门用于显示字母、数字、符号等的点阵式液晶显示模块。其显示的每个字符(字母、数字等)都是由5×7或5×11点阵组成的。点阵字符位之间有一空点距的间隔,起到字符间距和行距的作用。LCD1602是一款应用广泛的字符型LCD。LCD1602分为带背光和不带背光两种,其控制器大部分为HD44780,采用并行驱动方式。如图

所示。其中控制芯片U1为HD44780,U2为CGROM(CharacterGeneratorROM,字符生成只读存储器)。带背光的LCD1602(16个引脚)比不带背光的LCD1602(14个引脚)厚,背光电源A为15脚,地线K为16脚。LCD1602可显示两行,每行由16个点阵字符组成,能显示所有ASCII字符,每个字符由5×7点阵组成。LCD1602的尺寸如图6.9所示。LCD1602的主要技术参数如下:显示容量为16×2个字符,工作电压为4.5~5.5V,工作电流为2.0mA,字符尺寸(W×H)为2.95mm×4.35mm。6.4.1LCD1602接口LCD1602与单片机的连接有以下两种方式。一、直接控制方式LCD1602的8根数据线和3根控制线(E、RS、R/W)与单片机相连后即可正常工作如图

右图

所示。数据线用于传送数据和指令,接入P0口。由于一般应用中只需向LCD1602写入命令和数据,因此可将LCD1602的R/W引脚(读/写控制端)直接接地,这样可节省1根控制线。Vo引脚是液晶对比度调整端,通常连接一个10kΩ的电位器实现对比度的调整,也可采用将该引脚通过一个适当阻值的电阻接地的方法进行调整,电阻的阻值应通过调试决定。二、间接控制方式也称为四线制工作方式,其控制线连接与直接控制方式相同,不同之处在于其数据线连接利用了HD44780具有4位数据总线的特性,只采用引脚DB4~DB7与单片机进行通信,先传输数据或命令的高4位,再传输低4位。采用四线并口通信,可减少对微控制器I/O的需求。当产品设计过程中单片机的I/O资源紧张时,可以考虑使用此方法。二者的区别仅在于所用的数据线数量不同,其他都一样。在实际应用中,将LCD1602的16孔插座插入开发板上的排针时,不能插反,否则可能因接线错误而烧毁LCD1602或板上器件,只有接触良好才能保证电路完全畅通,6.4.2LCD1602存储器LCD1602是一种常用的字符型LCD,它内置了CGROM和CGRAM(CharacterGeneratorRAM,字符生成随机存储器)。LCD1602内部的三种核心存储器1.DDRAM2.CGROM3.CGRAM1.DDRAM

HD44780内部有一个地址计数器(AddressCounter,AC),保存的是DDRAM(DisplayDataRAM,显示数据随机存储器)或CGRAM的地址,用来定位DDRAM地址,我们可以通过指令控制具体访问的DDRAM地址。DDRAM通常简称显存,简单地说,你向DDRAM写入什么,屏幕上就会显示什么。DDRAM共80个字节,其地址与屏幕的对应关系如图。

每行16个显示单元,第一行实际显示地址为0x80~0x80+15,第二行实际显示地址为0xC0~0xC0+15。LCD1602屏幕上的内容与DDRAM的实际显示地址是一一对应的。要在LCD1602屏幕上显示字符,只需向相关DDRAM地址中写入该字符的ASCII码即可,写在范围外的字符不能被显示出来。这样,我们在程序中可以利用光标或显示移动指令使字符慢慢移动到可见的显示范围内,呈现字符的移动效果。注意:DDRAM上下两行的地址是不连续的,第一行地址为0x00~0x27、第二行地址为0x40~0x67。显示位置表示DDRAM地址对应的屏幕位置,HD44780包含80个DDRAM地址,也就意味最多可以显示80个字符。但是,我们使用的LCD1602只能显示2行,每行16个字符。默认情况下,它只使用32个DDRAM地址,即第一行0x00~0x0F、第二行0x40~0x4F,一个字符占用一个地址,其他地址也是有存储单元的,只不过不能显示出来。讨论:这里的实际显示地址为什么是0x80而不是0x00呢?可以通过LCD1602指令8找到答案。指令8用于对DDRAM地址,即字符的实际显示地址进行设置,其D7为1,D0~D6为地址。因此,需要在原来两行的首地址0x00和0x40的基础上加0x80,即0x80和0xC0。

2.CGROM

LCD1602中的HD44780芯片集成了CGROM,用于存储标准字符的字模数据。了解CGROM的结构和如何向LCD1602写入数据对于正确使用LCD1602至关重要。CGROM是预先固化好的字模库,包含标准ASCII字符集的显示数据,每个字符由5×8点阵组成,存储在CGROM中。CGROM中的字符编码与ASCII码表一一对应,方便直接使用ASCII码进行显示。我们向DDRAM写入的数据是字模地址(而不是字模本身),具体屏幕上显示什么取决于字模地址中对应的字模是什么。CGROM就是一个字库,内置了192个常用字符的字模,这些字符包括阿拉伯数字、大/小写英文字母、常用符号和日文假名等,按ASCII码排列。0x00~0x0F存储用户自定义的字符图形,0x20~0x7F存储标准的ASCII码,0xA0~0xFF存储日文字符和希腊文字符。

如果要在LCD1602上显示字符,还需要先设置DDRAM地址,再向该地址写入字符的CGROM代码。

例如,在屏幕左上角显示字符A的代码。LCD_Write_Com(0x80);//调用写指令函数,设置DDRAM地址为第一行第一个位置LCD_Write_Data('A');//调用写数据函数,写入字符A的CGROM代码或直接使用ASCII码,即//LCD_Write_Data(0x41);3.CGRAM

CGRAM中的字符是用户自定义的,共64字节,地址为0x40~0x7F,可存储8个5×8点阵图形,其中地址0x00~0x07存储字符代码为0x00的字符图形,0x08~0x0F存储字符代码为0x01的字符图形,以此类推。5×8点阵图形有8行5列,定义这样一个字符需要8个字节,每个字节的前3位没有被使用。

要想向DDRAM写入某个CGROM中不存在的字符,必须在CGRAM中先定义后使用。程序退出后CGRAM中定义的字符也不复存在,下次使用时,必须重新定义。

CGRAM地址从0x40开始,自定义字符实现步骤如下。使用5×8点阵设计字符形状,设计字符点阵;将点阵转换为8个字节的十六进制数据,生成字模数据;发送设置CGRAM地址指令(0x40+地址),将8个字节的字模数据依次写入CGRAM;向DDRAM写入自定义字符的代码(0x00~0x07),显示自定义字符。如定义一个摄氏温度符号(℃)并显示,参考代码如下。//自定义字符℃的字模数据ucharcodecelsius[]={0x10,0x06,0x09,0x08,0x08,0x09,0x06,0x00};//写入CGRAMLCD_write_com(0x40);//设置CGRAM地址for(i=0;i<8;i++){LCD_write_data(celsius[i]);//写入字模数据}//显示自定义字符LCD_write_com(0x80);//设置显示位置LCD_write_data(0x00);//显示第一个自定义字符CGRAM只能同时显示8个自定义字符,需注意合理规划使用。上述自定义字符点阵设计又称字符取模,即将图形转换为单片机可以识别的数据格式。取模方法有手动取模和软件取模两种。软件取模是指打开专门的取模软件,新建5×8点阵,在点阵中绘制所需字符,生成字模数据(8个十六进制数)。通过合理利用CGRAM和快速刷新,还可以在LCD1602上实现简单的动画或视频播放效果。基本思路是:将视频帧分割为多个5×8点阵,为每个点阵生成字模数据;使用双缓冲机制(一个缓冲区用于接收新数据帧,另一个缓冲区用于当前显示),通过串口高速传输数据帧,快速更新CGRAM,从而实现动画效果,即视频播放的高级应用。6.4.3LCD1602指令与时序1.LCD1602的操作时序在LCD1602的时序图中,在将E置高电平前,先设置好RS和R/W信号,在E下降沿到来之前,准备好写入的命令字或数据。只需在适当的地方加上延时,就可以满足要求。LCD1602的基本操作分为以下四种。①

读状态。输入:RS=0,R/W=1,E=1;输出:D0~D7=状态字。②

读数据。输入:RS=1,R/W=1,E=1;输出:D0~D7=数据。③

写指令。输入:RS=0,R/W=0,E=1,D0~D7=指令;输出:无。④

写数据。输入:RS=1,R/W=0,E=1,D0~D7=数据;输出:无。LCD1602的读操作控制时序如图时序参数时序参数符号极限值单位测试条件E信号周期400--ns引脚EE脉冲宽度150--nsE上升沿/下降沿时间、--25ns地址建立时间30--ns引脚E、RS、R/W地址保持时间10--ns数据建立时间(读操作)--100ns引脚DB0-DB7数据保持时间(读操作)20--ns数据建立时间(写操作)40--ns数据保持时间(写操作)10--ns2.LCD1602指令与编程1清显示00000000012光标返回000000001*3置输入模式00000001I/DS4显示开/关控制0000001DCB5光标或字符移位000001S/CR/L**6置功能00001DLNF**7置字符发生存贮器地址0001字符发生存贮器地址8置数据存贮器地址001显示数据存贮器地址9读忙标志或地址01BF计数器地址10写数到CGRAM或DDRAM)10要写的数据内容11从CGRAM或DDRAM读数11读出的数据内容(1)LCD1602指令。LCD1602内部的控制器共有11条控制指令,LCD1602指令集如表所示。各指令的功能或含义如下。指令1:清显示,指令码01H,将光标复位到地址00H。指令2:光标复位,使光标返回到地址00H。指令3:设置光标移动方向和显示模式。I/D表示光标移动方向,I/D为高电平时,光标右移;I/D为低电平时,光标左移。S表示屏幕上所有字符是否移动,高电平时移动,低电平时不移动。指令4:显示开关控制及光标设置。D用于控制整体显示的开与关,高电平表示开显示,低电平表示关显示。C用于控制光标的开与关,高电平表示有光标,低电平表示无光标。B用于控制光标是否闪烁,高电平表示闪烁,低电平表示不闪烁。指令5:光标或显示移位。S/C为高电平时移动显示的文字,S/C为低电平时移动光标;R/L为高电平时右移

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论