任务4 信号灯的设计与实现
本任务重点学习微处理器的通用输入/输出接口(GPIO),以及GPIO的位操作,掌握GPIO的基本原理和功能,以及使用C语言驱动CC2530的GPIO的方法,从而实现对信号灯的控制。
4.1 开发场景:如何控制信号灯
当常见的自动化设备具备多种功能时,往往会用不同的信号指示灯(信号灯)来表示系统的功能和工作状态,图4.1所示的安防设备控制器上具有电源、报警、设置、电话4个系统指示灯,通过这4个LED指示灯,用户可以方便、直观地设置与管理设备。
图4.1 安防设备控制器上的系统指示灯
4.2 开发目标
(1)知识目标:了解GPIO基本概念、电路驱动和工作模式;熟悉CC2530微处理器GPIO基本构成;掌握利用位操作实现CC2530微处理器GPIO基本操作。
(2)技能目标:会设置CC2530微处理器GPIO的工作模式;掌握基于CC2530微处理器GPIO基本驱动方法;实现基于CC2530微处理器的信号灯驱动开发。
(3)任务目标:使用CC2530微处理器上的发光二极管(LED)模拟信号指示灯,先检测连接在CC2530微处理器GPIO引脚上按键电平状态并识别其开关状态,然后根据检测结果读取LED指示灯的状态并进行实时控制,LED状态(亮或者灭)可以表示设备的功能和工作状态。
4.3 原理学习:微处理器的GPIO功能与应用
4.3.1 微处理器GPIO
1.微处理器GPIO基本概念
GPIO(General Purpose Input Output, GPIO)是微处理器的通用输入/输出接口。微处理器可以通过向GPIO控制寄存器写入数据来控制GPIO的模式,实现对某些设备的控制或信号采集功能;也可以对GPIO进行组合配置,实现较为复杂的总线控制接口和串行通信接口。下面将通过GPIO的电气属性与基本工作模式来对GPIO进行讲解。
2.微处理器GPIO电路驱动
GPIO电路可分为很多种,GPIO电路不同,效果也不同。根据GPIO电路的区别,可将电路分为弱驱动GPIO、强驱动GPIO、高压GPIO、低压GPIO,同时电压与驱动能力可以相互组合。
(1)弱驱动GPIO。弱驱动电路指GPIO引脚输出的电流较小,无法对相关的控制设备提供足够的驱动电流,因此电路在设计时需要额外添加上拉电阻以提高GPIO的驱动能力。例如,CC2530微处理器中除了P1_0、P1_1为强驱动,其他引脚驱动能力均无法满足LED驱动的需要,因此需要通过添加额外的电路以达到控制外接设备的目的。
(2)强驱动GPIO。顾名思义,强驱动GPIO是指驱动能力较强的GPIO。通常情况下,当输入与芯片电源相同的电压时,强驱动GPIO可以驱动功率更大的外接设备。例如,STM32系列微处理器基本上都属于强驱动GPIO。
(3)高压GPIO与低压GPIO。目前微处理器GPIO输出电压有两种,一种为早期传统8051微处理器的5 V的GPIO,另一种为通用型的3.3 V的GPIO。5 V的GPIO接口是由于微处理器输入电压为5 V,因此微处理器引脚输出的电平同样为5 V;而低压GPIO的芯片的输入电源为3.3 V,因此微处理器引脚输出的电平为3.3 V。高压GPIO与低压GPIO相比,低压GPIO的工艺更加先进,引脚的开关效率也更高,可以满足高速总线的引脚电平跳变需求,因此高性能微处理器的GPIO引脚通常为低压GPIO。
3.微处理器GPIO工作模式
GPIO在工作时有三种工作模式,即输入、输出和高阻态,这三种状态的使用和功能都有所不同,在设置时需要根据实际的外接设备来对引脚进行配置。下面对GPIO的这三种状态进行简单的叙述。
(1)输入模式。输入模式是指GPIO被配置为接收外接电平信息的模式,通常读取的信息为电平信息,即高电平为1,低电平为0。这时读取的高低电平是根据微处理器的电源高低来划分的,相对于5 V电源的微处理器,判断为高电平时的检测电压为3.3~5 V;小于2 V时则微处理器判断为低电平。相对于3.3 V电源的微处理器,判断为高电平时的检测电压为2~3.3 V;小于0.8 V时则微处理器判断为低电平。
(2)输出模式。输出模式是指GPIO被配置为主动向外部输出电压的模式,通过向外输出电压可以实现对一般开关类设备的实时主动控制。当程序中向相应引脚写1时,GPIO会向外输出高电平,通常这个电平为微处理器的电源电压;当程序中向相应引脚写0时,GPIO会向外输出低电平,通常这个低电平为电源地的电压。
(3)高阻态模式。高阻态模式是指GPIO引脚内部电阻的阻值无限大,大到几乎占有外接输出的全部电压。这种模式通常在微处理器采集外部模拟电压时使用,通过将相应GPIO引脚配置为高阻态模式和输入模式,通过配合微处理器的ADC可以实现准确的模拟量电平读取。
4.3.2 CC2530与GPIO
1.CC2530的GPIO
微处理器的GPIO引脚可以组成3个8位端口,即端口0、端口1和端口2,分别表示为P0、P1和P2,其中P0和P1是8位端口,而P2只有5位可用,所有端口均可以通过SFR寄存器来进行P0、P1、P2位寻址和字节寻址。
寄存器PxSEL中的x表示端口标号0~2,用来设置端口的每个引脚为GPIO或者外部设备I/O信号,在默认情况下,当复位之后,所有数字输入/输出引脚都设置为通用输入引脚。
寄存器PxDIR用来改变一个端口引脚的方向,可设置为输入或输出,设置PxDIR的指定位为1时,对应的引脚口则为输出;设置为0时,对应的引脚口则为输入。
当读取寄存器P0、P1和P2的值时,不管引脚如何配置,输入引脚的逻辑值都被返回,但在执行读-修改-写期间不适用。当读取的是寄存器P0、P1和P2中一个独立位时,寄存器的值(而不是引脚上的值)可以被读取、修改并写回端口寄存器。CC2530引脚分布如图4.2所示。
图4.2 CC2530引脚分布
2.CC2530的GPIO寄存器
CC2530微处理器内核为增强型8051内核,同时芯片在内部总线上有较大的优化和改进,因此CC2530的GPIO配置寄存器众多,如表4.1所示。
表4.1 GPIO配置寄存器
GPIO的控制寄存器众多,但用于输入/输出配置的寄存器只有特定的几个,所以驱动GPIO时只需要配置P1DIR(端口1方向寄存器)和P1SEL(端口1功能选择寄存器)。P1DIR寄存器功能分配如表4.2所示,P1ESL寄存器功能分配如表4.3所示。
表4.2 P1DIR寄存器功能分配
表4.3 P1SEL寄存器功能分配
如表4.2所示,P1DIR寄存器用于配置GPIO的方向,即输入/输出方向,当某一位置1时表示对应的引脚为Output,即输出模式,反之则为Input,即输入模式。P1SEL用于设置GPIO引脚的功能,表示GPIO是GPIO模式还是外设模式,当某一位置1时表示对应的引脚配置为外设模式,反之则为GPIO模式,因此对GPIO的配置其实就是对控制寄存器的配置。
4.3.3 GPIO的位操作
GPIO一般是通过位操作完成寄存器设置的,常用的位操作运算符有按位与“&”、按位或“|”、按位取反“~”、按位异或“^”,以及左移运算符“<<”和右移运算符“>>”。
(1)按位或运算符“|”。参加运算的两个运算量的位至少有一个是1时,结果为1,否则为0,按位或运算常用来对一个数据的某些特定的位置1,例如,“P1DIR |=0X02”,0X02为十六进制数,转换成二进制数为00000010,若P1DIR原来的值为00110000,或运算后P1DIR的值为00110010。根据上面给出的取值表可知,按位或运算后P1_1的方向改为输出,其他I/O口方向保持不变。
(2)按位与运算符“&”。参加运算的两个运算量相应的位都是1时,则结果为1,否则为0,按位与运算常用于清除一个数中的某些特定位。
(3)按位异或运算符“^”。参加运算的两个运算量相应的位相同,即均为0或者均为1时,结果值中该位为0,否则为1,按位异或运算常用于将一个数中某些特定位翻转。
(4)按位取反“~”。用于对一个二进制数按位取反,即0变1,1变0。
(5)左移运算符“<<”。左移运算用于将一个数的各个二进制全部左移若干位,移到左端的高位被舍弃,右边的低位补0。
(6)右移运算符“>>”。用于对一个二进制数位全部右移若干位,移到右端的低位被舍弃。
例如,“P1DIR &=~0x02”, &表示按位与运算,~运算符表示取反,0x02为00000010,~0x02为11111101。若P1DIR原来的值为00110010,进行与运算后P1DIR的值为00110000。
4.4 任务实践:信号灯的软/硬件设计
4.4.1 开发设计
1.硬件设计
本任务的硬件架构设计如图4.3所示。
图4.3 硬件架构设计图
要通过CC2530微处理器实现对按键动作的检测和信号灯的控制,第一要了解信号灯的控制原理,第二要掌握按键动作的捕获原理,将捕获按键动作和信号灯控制结合起来就可以实现两者的联动控制,从而达到项目设计效果。
1)连接到GPIO的LED控制
将信号灯的控制转化成对GPIO的主动控制:高电平输出和低电平输出,信号灯LED接口电路如图4.4所示,图中D1与D2一端接电阻,另一端接在CC2530微处理器上,电阻的另一端连接在3.3 V的电源上,D1与D2采用的是正向连接导通的方式,当P1_0和P1_1为高电平(3.3 V)时,D1与D2两电压相同,无法形成压降,因此D1与D2不导通,D1与D2熄灭;反之当P1_0和P1_1为低电平时,D1与D2两端形成压降,则D1与D2点亮。
图4.4 LED接口电路图
按键的状态检测方式主要是使用CC2530微处理器GPIO的引脚电平读取功能,相关引脚为高电平时引脚读取的值为1,反之则为0。而按键是否按下,以及按下前后的电平状态则需要按照实际的按键原理图来确认,按键接口电路如图4.5所示。图中,按键K1的引脚2接GND,引脚1接电阻和CC2530微处理器的引脚P1_2,电阻的另一端连接3.3 V电源,当按键没有按下时K1的引脚1和引脚2断开,由于CC2530微处理器引脚在输入模式时为高阻态,所以引脚P1_2采集的电平为高电平;当K1按键按下后K1的引脚1和引脚2导通,此时引脚P1_2导通接地,所以此时引脚检测电平为低电平。
图4.5 按键接口电路图
2)按键控制
通常按键所用的开关都是机械弹性开关,当机械触点断开、闭合时,由于弹性作用,一个按键开关在闭合时不会马上就稳定地接通,在断开时也不会一下子就彻底断开,而是在闭合和断开的瞬间伴随着一连串的抖动,按键抖动电信号波形如图4.6所示。
图4.6 按键抖动电信号波形
按键稳定闭合时间长短是由操作人员决定的,通常都会在100 ms以上,刻意快速按的话能达到40~50 ms,很难再低了。抖动时间是由按键的机械特性决定的,一般都会在10 ms以内,为了确保程序对按键的一次闭合或者一次断开只响应一次,必须进行按键的消抖处理。当检测到按键状态变化时,不是立即去响应动作,而是先等待闭合或断开稳定后再进行处理。按键消抖可分为硬件消抖和软件消抖。
本任务使用软件消抖,当检测到按键状态变化后,先等待一段时间,让抖动消失后再进行按键状态检测,如果与刚才检测的状态相同,就可以确认按键已经稳定了。
2.软件设计
掌握硬件设计之后,再来分析软件设计。首先需要将CC2530微处理器的GPIO配置为输入模式和输出模式,配置输入模式和输出模式时涉及两个寄存器,分别为PxSEL(模式选择寄存器)和PxDIR(输入/输出方向控制寄存器)。其次,在按键输入检测时需要使用延时消抖和松手检测方法,通过延时消抖可以屏蔽开关动作时的电平抖动,防止误操作;使用松手检测作为对LED控制的触发条件。程序设计流程如下。
(1)配置LED和按键对应的GPIO,初始化LED和按键外设。
(2)初始化完成后程序进入主循环,主循环中不断检测按键的状态。
(3)当检测到按键按下时,延时10 ms,待电平稳定后如果按键依旧处于按下状态则确定按键被按下,等待按键抬起。
(4)检测到按键抬起后执行LED的反转控制操作,完成对LED的控制。
程序设计流程如图4.7所示。
图4.7 软件设计流程图
注意:本任务中所讲到的按键和信号灯在接下来的任务开发中都会用到,相关内容在此处进行详细解释后,其他任务将不再对按键和信号灯等相关内容进行介绍。
4.4.2 功能实现
通过原理学习可知,要实现D1、D2的亮灭,只需配置P1_0、P1_1引脚即可,然后输出高/低电平,即可实现D1、D1的闪烁控制,这两个引脚只需配置为输入模式读取电平即可。下面是源代码实现的解析过程。
1.主函数模块
首先实现LED端口和按键端口的初始化,然后进入循环以检测按键状态,当按键按下时,进行LED状态控制。
/**************************************************************************************** * 名称:main() * 功能:LED驱动逻辑代码 ****************************************************************************************/ void main(void) { led_io_init(); //LED端口初始化 key_io_init(); //按键端口初始化 LED2=ON; //打开LED0 while(1){ if(KEY1==ON){ //按键按下,改变两个LED的状态 delay_ms(10); //按键防抖10 ms if(KEY1==ON){ //按键按下,改变两个LED的状态 while(KEY1==ON); //松手检测 LED2=~LED2; //LED翻转闪烁 LED1=~LED1; //LED翻转闪烁 } } } }
2.LED初始化模块
LED初始化需要先配置P1SEL寄存器为GPIO模式,然后配置P1DIR寄存器为输出模式,并先关闭两个LED,源代码如下。
/**************************************************************************************** * 名称:led_init() * 功能:LED控制引脚初始化 ****************************************************************************************/ void led_io_init(void) { P1SEL&=~0x03; //配置控制引脚(P1_0和P1_1)为GPIO模式 P1DIR|=0x03; //配置控制引脚(P1_0和P1_1)为输出模式 LED1=OFF; //初始状态为关闭 LED2=OFF; //初始状态为关闭 }
3.按键初始化模块
按键初始化时先配置P1SEL寄存器为GPIO模式,然后配置P1DIR寄存器为输入模式,源代码如下。
/**************************************************************************************** * 名称:key_init() * 功能:按键初始化 ****************************************************************************************/ void key_init(void) { P1SEL&=~0x0C; //配置按键检测引脚(P1_2和P1_3)为GPIO模式 P1DIR&=~0x0C; //配置按键检测引脚(P1_2和P1_3)为通输出模式 }
4.延时模块
/**************************************************************************************** * 名称:delay_ms() * 功能:硬件延时,大于250 ms * 参数:times—延时时间 ****************************************************************************************/ void delay_ms(u16 times) { u16 i, j; //定义临时参数 i=times/250; //获取要延时时长的250 ms倍数部分 j=times % 250; //获取要延时时长的250 ms余数部分 while(i--)hal_wait(250); //延时250 ms hal_wait(j); //延时剩余部分 } /**************************************************************************************** * 名称:hal_wait(u8 wait) * 功能:硬件延时函数 * 参数:wait—延时时间(wait<255) ****************************************************************************************/ void hal_wait(u8 wait) { unsigned long largeWait; //定义硬件计数的临时参数 if(wait==0)return; //如果延时参数为0,则跳出 largeWait=((u16)(wait<<7)); //将数据扩大64倍 largeWait+=114*wait; //将延时数据扩大114倍并求和 largeWait=(largeWait>>CLKSPD); //根据系统时钟频率对延时进行缩放 while(largeWait--); //等待延时自减完成 }
4.5 任务验证
使用IAR开发环境打开任务设计工程,程序通过编译后,由SmartRF下载到CC2530微处理器中,执行程序后,开发平台上D2点亮,D1熄灭。按下K1按键后D2熄灭,D1点亮。再次按下K1按键后D2点亮,D1熄灭,如此循环往复。
4.6 任务小结
GPIO是微处理器最常用的基本接口,本任务先学习了GPIO的概念、工作模式,然后进一步学习了GPIO的基本功能和控制,并介绍了GPIO的位操作,最后完成该任务的硬件设计和软件设计,实现了通过CC2530微处理器的GPIO接口控制相关仪表的信息和状态。
4.7 思考与拓展
(1)GPIO的三种状态有什么功能?可以应用在哪些方面?
(2)常见的GPIO的位操作有哪些?
(3)CC2530的GPIO方向寄存器和功能选择寄存器有什么功能?如何配置?
(4)如何驱动CC2530微处理器的GPIO?
(5)手机接收到短消息时,信号灯就会像人的呼吸一样闪烁,信号灯逐渐变亮,达到最亮后又逐渐熄灭,通过这样一种有反差的闪烁效果,既能体现科技时尚感,又能达到很好的来电消息提醒效果。以手机信号灯为项目目标,如何基于CC2530实现LED闪烁的“呼吸”效果?