第5章GPIO的应用实例:键盘、LED与LCD本章导读:键盘、LED数码管和LCD液晶显示是嵌入式系统中常用的输入/输出设备,它们一般通过通用I/O口与MCU连接,本章把它们作为GPIO的应用实例来看待。键盘是嵌入式应用的输入设备,识别按键情况常用查询法、定时扫描法与中断法等。LED和LCD是嵌入式应用的输出设备。本章主要知识点有①键盘扫描基本原理与编程方法;②LED扫描基本原理与编程方法;③字符型LCD的编程举例。在本章讲述的鍵盘、LED和LCD构件设计的一般模型,可适用于不同型号的设备,但需注意硬件电路设计的差异。♦5.1键盘模块的驱动构件设计本节首先简要阐明有关键盘识别的一些基本问题,随后给出K60的键盘的工作原理、基于构件思想的键盘构件头文件以及源程序文件的设计思想,并给出了一个实际的4×4键盘驱动构件设计与测试实例。5.1.1键盘模型及接口1.键盘模型及接口键盘是由若干个按键组成的开关矩阵,它是最简单的MCU数字量输入设备。操作员通过键盘输入数据或命令,实现简单的人机通信。首先,我们应了解键盘模型及接口的基本概念和原理,以便为后续编程做准备。键盘的基本电路为接触开关,通、断两种状态分别表示0和1。键盘的电路模型及其实际按键动作见图5-1所示。MCU通过检测与键盘相连接的I/O口来确定键盘状态。图5-1键盘模型及按键抖动示意图其次,键盘接口按照不同的标准有不同的分类方法。按键盘排布的方式可分成独立方式和矩阵方式;按读入键值的方式可分为直读方式和扫描方式;按是否进行硬件编码可分成非编码方式和硬件编码方式;按微处理器响应方式可分为中断方式和查询方式。本章将使用中断方法编程。将以上各种方式组合可构成不同的键盘接口方式。以下介绍两种较为常用的键盘接口方式。1)独立方式独立方式是指将每个独立按键按一对一的方式直接接到I/O输入线上,如图5-2所示。读键值时直接读I/O口,每个键值通过读入对应I/O的状态来反映,所以也称这种方式为一维直读方式,习惯称为独立式。这种方式査键实现简单,但占用I/O资源较多,一般在按键数量较少的情况下采用。2)矩阵方式矩阵方式是用n条I/O线组成行输入口,m条I/O线组成列输入口,在行列线的每一个交点上设置一个按键,如图5-3所示。读键值方法一般采用扫描方式,即MCU输出口按位轮换输出低电平,再从输入口读入键信息,最后获得键码。这种方式占用I/O线较少,在实际应用系统中采用较多。在使用这种方法时需要考虑,硬件连接时需要选择键盘哪些引脚作为MCU的输入,哪些作为MCU的输出,作为MCU的输入的键盘引脚需要上拉。设计键盘时,通常小于4个按键的应用,可以使用独立式接口。如果多于4个按键,为了减少对微处理器的I/O资源的占用,可以使用矩阵式键盘。那么键盘编程应该注意哪些问题呢?下节我们就讨论这个问题,这对于实际应用非常重要。图5-2独立式键盘图5-1矩阵式键盘2.键盘编程的基本问题对于键盘编程我们至少应该解决下面几个问题。第一,如何识别键盘上的按键?第二,如何区分按键是否真正地被按下还是抖动?第三,如何处理重键问题?了解这些问题有助于键盘编程。1)键的识别如何知道键盘上哪个键被按下就是键的识别问题。若键盘上闭合键的识别由专用硬件实现,称为编码键盘;而靠软件实现的称为未编码键盘。在这里主要讨论未编码键盘的接口技术和键盘输入程序的设计。识别是否有键被按下,主要有查询法、定时扫描法与中断法等。而要识别键盘上哪个键被按下主要有行扫描法与行反转法。2)抖动问题当键被按下时,会出现所按的键在闭合位置和断开位置之间跳几下才稳定到闭合状态的情况,当释放一个按键时也会出现类似的情况,这就是抖动问题。抖动持续的时间因操作者而异,一般为5〜10ms,稳定闭合时间一般为十分之几秒到几秒,由操作者的按键动作所确定。在软件上,解决抖动的方法通常是延迟等待抖动的消失或多次识别判定。3)重键问题所谓重键问题就是有两个及两个以上按键同时处于闭合状态的处理问题。在软件上,处理重键问题通常有连锁法与巡回法。图5-44×4键盘的结构为了正确理解MCU键盘接口方法与编程技术,下面以4x4键盘为例说明按键识别的基本编程原理。4x4的键盘结构如图5-4所示,图中列线(n1〜n4)通过电阻接+5V,当键盘上没有键闭合时,所有的行线和列线断开,列线n1〜n4都呈高电平。当键盘上某一个键闭合时,则该键所对应的行线与列线短路。例如第2排第3个按键被按下闭合时,行线m2和列线n3短路,此时n3线上的电平由m2的电位所决定。那么如何确定键盘上哪个按键被按下呢?可以把列线n1〜n4接到MCU的输入口,行线m1〜m4接到MCU的输出口,则在微机的控制下,使行线m1为低电平(0),其余三根行线m2、m3、m4都为高电平,并读列线n1〜n4状态。如果n1〜n4都为高电平,则m1这一行上没有键闭合,如果读出列线n1〜n4的状态不全为高电平,那么为低电平的列线和m1相交的键处于闭合状态;如果m1这一行上没有键闭合,接着使行线m2为低电平,其余行线为高电平,用同样方法检查m2这一行上有无键闭合;以此类推,最后使行线m4为低电平,其余的行线为高电平,检查m4这一行上是否有键闭合。这种逐行逐列地检查键盘状态的过程称为对键盘的一次扫描。CPU对键盘扫描可以采取程序控制的随机方式,CPU空闲时扫描键盘;也可以采取定时控制,每隔一定时间,CPU对键盘扫描一次,CPU可随时响应键输入请求;还可以采用中断方式,当键盘上有键闭合时,向CPU请求中断,CPU响应键盘输入中断,对键盘扫描,以识别哪一键处于闭合状态,并对键盘输入信息做出相应处理。CPU对键盘上闭合键的键号确定,可以根据行线和列线的状态计算求得,也可以根据行线和列线状态查表求得。5.1.2键盘驱动构件设计本节给出一个实际的4×4键盘驱动构件设计方法,讲述如何扫描键盘取得键值和键盘编码等问题。键盘与MCU接线见图5-5,图5-6给出了键的定义符号“0”〜“9”、“A”〜“D”、“*”、“#”等。如何识别“1”键呢?这里将列线n1〜n4分别接PTIPO、PT1PKPTIP2、PTIP3,且编程时将这四个引脚定义为输入并进行上拉,行线m1〜m4分别接PORTAO〜PORTA3,且编程时将PORTA0〜PORTA3定义为输出,那么“1”键对应于按照PTIP3、PTIP2,PTIP1、PTIP0、PORTA3、PORTA2、PORTA1、PORTA0的顺序为11101110,即$EE:“2”键对应于11011110,即$DE;…:“D”键对应于01110111’即$77。前者“1”、“2”、“D”就是我们的定义值,后者$EE、$DE、$77就是“键值”,这种情况“键值”是一个字节。这样,按图5-5的接法可以得出键值表,见图5-6。键值可以通过扫描法获得,由键值通过査表法编程得到定义值。从软件角度来看,键盘模块具有初始化、单次扫描键值、多次扫描键值、键值转换等操作。按照构件的思想,可将它们封装成独立的功能函数。KB构件包括头文件kb.h和kb.C文件。KB构件头文件中主要包括相关宏定义、KB的功能函数原型说明等内容。KB构件程序文件的内容是给出KB各功能函数的实现过程。此外,由图5-5可看出,键盘与MCU的PTIP、PORTA部分引脚连接,在进行键盘按键扫描时,需设置PTIP、PORTA某些引脚的输出或读取某些引脚的状态,显然,在编写键盘KB构件时,需调用GPIO构件。图5-5键盘与MCU的接法图5-6键盘定义K1构件给出了表5-1所示的函数的声明和定义。表5-1KB构件函数序号函数名参数功能1KBInit无键盘初始化2KBScan1无扫描1次4x4键盘,返回扫描到的键值,若无按键,返回Oxff3KBScanN重复扫描键盘的次数(KB_count)多次扫描键盘,消除“抖动”4KBDef键值valve键值转为定义值5EnableKBlnter无开放键盘中断6DisableKBInter无禁止键盘中断7KBInterClear无键盘中断清空下面以K60的驱动构件设计为例,给出相应的程序代码。1.键盘构件头文件(kb.h)2.键盘构件源文件(kb.c)5.1.3键盘驱动构件测试实例本测试实例调用上节给出的KBI构件,使用UARTO口和PC通信,当按下键盘按键时,由串口向PC发送对应的键值。键的识别采用上文所讲述的中断法,故设计了与之相对应的中断处理函数。1.测试工程的主函数(main.c)2.中断函数(isr.c)测试结果:通过按键可在串口调试工具上显示相应按键的对应键值和定义值。♦5.2LED模块的驱动构件设计本节在介绍8段数码管(LightEmittingDiode,LED)显示编程原理的基础上,分析了LED驱动构件设计方法,并给出了扫描法实现四连排LED显示实例。5.2.1LED的基础知识对于LED编程我们需要了解下列几个问题:第一,所用LED是几段,共阴极还是共阳极?第二,所选LED的电气参数怎样?如额定功率、额定电流是多少?如果我们对上述两个问题有明确的了解,那么对LED编程和封装LED构件就变得容易多了。LED的选择需要根据实际应用需求来决定,若只需要显示数字“0”〜“9”,则只需7段LED就够了,若同时又要显示小数点,则需使用8段LED,8段LED由8个发光二极管组成。例如,图5-8的8段数码管分别由a、b、c、d、e、f、g位段和小数点位段h(或记为dp)组成。MCU是通过I/O引脚来控制LED某段发光二极管的亮灭从而达到显示某个数字的目的。那么怎样才能使LED发光二极管亮灭呢?首先应了解所选用的是共阴极数码管还是共阳极数码管。若为共阴极数码管,则公共端需要接地,若为共阳极则公共端接电源正极(见图5-7)。数码管外形见图5-8。图5-7数码管连接方式图5-8数码管外形共阴极8段数码管的信号端高电平有效,只要在各位段加上高电平信号即可使相应的位段发光,比如要使a段发光,则在a段加上高电平即可。共阳极的8段数码管则相反,在相应的位段加上低电平即可使该位段发光。因而一个8段数码管就必须有8位(即1个字节)数据来控制各个位段的亮灭。比如对共阳极8段数码管,PORTB0〜7分别接a〜g、dp,即PORTB=0b01111111时dp段亮;当PROTB=Ob10000000时除dp位段外,其他位段均亮。到此我们基本弄清对一个LED编程的原理了,下面我们需要注意的是在进行硬件连接时需要注意所选用的LED的电气参数,如能承受的最大电流、额定电压。根据其电气参数来选择使用限流电阻或电流放大电路。下面介绍如何进行多个LED编程,因为在实际应用中往往需要多个LED协同使用。那么是不是如上段述说一样,有几个8段数码管,就必须由几个字节的数据来控制各个数码管的亮灭?这样控制虽然简单,却不切实际,MCU也不可能提供这么多的端口用来控制数码管,为此往往是通过一个称为数据口的8位数据端口来控制位段。而8段数码管的公共端,原来接到固定的电平(对共阴极是GND,对共阳极是Vcc),现在接MCU的一个输出引脚,由MCU来控制,通常叫“位选信号”,而把这些由n个数码管合在一起的数码管组称为n连排数码管。这样MCU的两个8位端口就可以控制一个8连排的数码管。若是要控制更多的数码管,则可以考虑外加一个译码芯片。图5-9所示为一个四连排的共阴极数码管,它们的位段信号端(称为数据端)接在一起,可以由MCU的一个8位端口控制,同时还有4个位选信号(称为控制端),用于分别选中要显示数据的数码管,可用MCU另一个端口的4个引脚来控制。如图5-9所示,每个时刻只让一个数码管有效,由于人眼的“视觉暂留”(约100ms)效应,产生的看起来则是同时显示的效果。图5-9四连排共阴极8段数码管5.2.2LED驱动构件设计图5-10给出了K60的一个共阴极四连排8段数码管的硬件构件连接实例。利用MCU的PTC口控制8个位段(数据)。图中LED的数据线a〜h位段分别与MCU的PTC的10,12,8,5,11,7,6,4脚连接,LED的片选线CS0〜CS3分别与MCU的PTC的16,14,13