STM32F103C8T6标准库函数实现多按键检测状态机短按
前言
制作航模遥控器需要用到多按键检测,实现过程中主要参考了以下两篇文章,尤其是第一篇收获最大,作者的代码思想很好,但文中部分代码有误,实际运行时检测到的IO电平是错误的,花费了一天时间才调通,简单记录一下。1。电路连接
使用STM32F103C8T6蓝色板,按键采用共阴极连接。
6个按键:
CH1Left接PB5
CH1Right接PB4
CH2Up接PA15
CH2Down接PB3
CH4Left接PA12
CH4Right接PA11
串口USBTTL接法:
GND电源地
3V3接3。3V
TXD接PB7
RXD接PB6
STLINKV2接法:
GND电源地
3V3接3。3V
SWCLK接DCLK
SWDIO接DIO
2。程序实现
key。h主要定义结构体和函数预定义ifndefKEYHdefineKEYHincludestm32f10x。hincludestm32f10xgpio。htypedefstruct构造按键初始化类{GPIOModeTypeDefGPIOMode;初始化按键模式GPIOTypeDefGPIOx;初始化按键口uint16tGPIOPinx;初始化按键引脚好uint32tRCCAPB2PeriphGPIOx;初始化时钟}KeyInit;typedefenumKEYSTATUSLIST按键状态{KEYNULL0x00,无动作KEYSURE0x01,确认状态KEYUP0x02,按键抬起KEYDOWN0x04,按键按下KEYLONG0x08,长按}KEYSTATUSLIST;typedefstructKEYCOMPONENTS状态机类{FunctionalStateKEYSHIELD;按键屏蔽,DISABLE(0):屏蔽,ENABLE(1):不屏蔽uint8tKEYCOUNT;按键长按计数BitActionKEYLEVEL;最终按键状态,按下BitSET(1),抬起BitRESET(0)BitActionKEYDOWNLEVEL;按下时,按键IO实际的电平KEYSTATUSLISTKEYSTATUS;按键状态KEYSTATUSLISTKEYEVENT;按键事件BitAction(READPIN)(KeyInitKey);读IO电平函数}KEYCOMPONENTS;typedefstruct按键类{KeyInitKey;继承初始化父类KEYCOMPONENTSStatus;继承状态机父类}KeyConfig;typedefenum按键注册表{CH1Left,CH1Right,CH2Up,CH2Down,CH4Left,CH4Right,用户添加的按钮名称KEYNUM,必须要有的记录按钮数量,必须在最后}KEYLIST;voidKEYInit(void);IO初始化voidCreatKey(KeyInitInit);初始化按钮函数voidReadKeyStatus(void);状态机函数voidTIM3Init(u16arr,u16psc);endif
原文中Key用的是指针,结果导致读电平函数GPIOReadInputDataBit()寻址错误,才使得读出的电平有误。typedefstruct按键类{KeyInitKey;继承初始化父类KEYCOMPONENTSStatus;继承状态机父类}KeyConfig;
key。cTIM3定时器初始化,定时检测按键状态;有限状态机实现includestm32f10x。hincludekey。hincludesys。hincludedelay。hincludeusart。h参考链接https:blog。csdn。netqq42679566articledetails105892105,原文错误已修正KeyConfigKeyBuf〔KEYNUM〕;创建按键数组defineKEYLONGDOWNDELAY30设置30个TIM3定时器中断600ms算长按defineDBGMCUCR(((volatileu32)0xE0042004))通用定时器3中断初始化,使用TIM3控制按键定时检测时钟选择为APB1的2倍,而APB1为36M参数:arr:自动重装值。psc:时钟预分频数voidTIM3Init(u16arr,u16psc){TIMTimeBaseInitTypeDefTIMTimeBaseInitStructure;NVICInitTypeDefNVICInitStructure;RCCAPB1PeriphClockCmd(RCCAPB1PeriphTIM3,ENABLE);时钟使能TIMTimeBaseInitStructure。TIMPeriodarr;自动重装载寄存器周期的值TIMTimeBaseInitStructure。TIMPrescalerpsc;预分频值TIMTimeBaseInitStructure。TIMCounterModeTIMCounterModeUp;向上计数TIMTimeBaseInitStructure。TIMClockDivisionTIMCKDDIV1;时钟分割为0,仍然使用72MHzTIMITConfig(TIM3,TIMITUpdate,ENABLE);允许更新中断TIMTimeBaseInit(TIM3,TIMTimeBaseInitStructure);NVICInitStructure。NVICIRQChannelTIM3IRQn;NVICInitStructure。NVICIRQChannelPreemptionPriority0;抢占优先级0NVICInitStructure。NVICIRQChannelSubPriority3;子优先级3NVICInitStructure。NVICIRQChannelCmdENABLE;IRQ通道使能NVICInit(NVICInitStructure);根据指定的参数初始化NVIC寄存器TIMCmd(TIM3,ENABLE);}voidTIM3IRQHandler(void)TIM3中断{if(TIMGetITStatus(TIM3,TIMITUpdate)!RESET)检查TIM3更新中断发生与否{中断处理代码ReadKeyStatus();调用状态机u8i,status;for(i0;iKEYNUM;i){statusKeyBuf〔i〕。Status。KEYEVENT;if(status!KEYNULL)printf(d,d,i,status);事件处理if(statusKEYDOWN)printf(d短按,i);if(statusKEYLONG)printf(d长按,i);}TIMClearITPendingBit(TIM3,TIMITUpdate);清除TIMx更新中断标志}}按键初始化函数voidKEYInit(void)IO初始化{KeyInitKeyInit〔KEYNUM〕{{GPIOModeIPU,GPIOB,GPIOPin5,RCCAPB2PeriphGPIOB},初始化按键CH1Left{GPIOModeIPU,GPIOB,GPIOPin4,RCCAPB2PeriphGPIOB},初始化按键CH1Right{GPIOModeIPU,GPIOA,GPIOPin15,RCCAPB2PeriphGPIOA},初始化按键CH2Up{GPIOModeIPU,GPIOB,GPIOPin3,RCCAPB2PeriphGPIOB},初始化按键CH2Down{GPIOModeIPU,GPIOA,GPIOPin12,RCCAPB2PeriphGPIOA},初始化按键CH4Left{GPIOModeIPU,GPIOA,GPIOPin11,RCCAPB2PeriphGPIOA},初始化按键CH4Right};CreatKey(KeyInit);调用按键初始化函数STM32没有彻底释放PB3作为普通IO口使用,切换到SW调试可释放PB3、PB4、PA15RCCAPB2PeriphClockCmd(RCCAPB2PeriphAFIO,ENABLE);GPIOPinRemapConfig(GPIORemapSWJJTAGDisable,ENABLE);DBGMCUCR0xFFFFFFDF;如果没有这段代码,PB3就会一直是低电平}staticBitActionKEYReadPin(KeyInitKey)按键读取函数{return(BitAction)GPIOReadInputDataBit(Key。GPIOx,Key。GPIOPinx);}voidCreatKey(KeyInitInit){uint8ti;GPIOInitTypeDefGPIOInitStructure〔KEYNUM〕;for(i0;iKEYNUM;i){KeyBuf〔i〕。KeyInit〔i〕;按钮对象的初始化属性赋值RCCAPB2PeriphClockCmd(KeyBuf〔i〕。Key。RCCAPB2PeriphGPIOx,ENABLE);使能相应时钟GPIOInitStructure〔i〕。GPIOPinKeyBuf〔i〕。Key。GPIOPinx;设定引脚GPIOInitStructure〔i〕。GPIOModeKeyBuf〔i〕。Key。GPIOMode;设定模式GPIOInit(KeyBuf〔i〕。Key。GPIOx,GPIOInitStructure〔i〕);初始化引脚初始化按钮对象的状态机属性KeyBuf〔i〕。Status。KEYSHIELDENABLE;KeyBuf〔i〕。Status。KEYCOUNT0;KeyBuf〔i〕。Status。KEYLEVELBitRESET;if(KeyBuf〔i〕。Key。GPIOModeGPIOModeIPU)根据模式进行赋值KeyBuf〔i〕。Status。KEYDOWNLEVELBitRESET;elseKeyBuf〔i〕。Status。KEYDOWNLEVELBitSET;KeyBuf〔i〕。Status。KEYSTATUSKEYNULL;KeyBuf〔i〕。Status。KEYEVENTKEYNULL;KeyBuf〔i〕。Status。READPINKEYReadPin;赋值按键读取函数}}staticvoidGetKeyLevel(void)根据实际按下按钮的电平去把它换算成虚拟的结果{uint8ti;for(i0;iKEYNUM;i){if(KeyBuf〔i〕。Status。KEYSHIELDDISABLE)continue;if(KeyBuf〔i〕。Status。READPIN(KeyBuf〔i〕。Key)KeyBuf〔i〕。Status。KEYDOWNLEVEL)KeyBuf〔i〕。Status。KEYLEVELBitSET;elseKeyBuf〔i〕。Status。KEYLEVELBitRESET;}}voidReadKeyStatus(void){uint8ti;GetKeyLevel();for(i0;iKEYNUM;i){switch(KeyBuf〔i〕。Status。KEYSTATUS){状态0:没有按键按下caseKEYNULL:if(KeyBuf〔i〕。Status。KEYLEVELBitSET)有按键按下{KeyBuf〔i〕。Status。KEYSTATUSKEYSURE;转入状态1KeyBuf〔i〕。Status。KEYEVENTKEYNULL;空事件}else{KeyBuf〔i〕。Status。KEYEVENTKEYNULL;空事件}break;状态1:按键按下确认caseKEYSURE:if(KeyBuf〔i〕。Status。KEYLEVELBitSET)确认和上次相同{KeyBuf〔i〕。Status。KEYSTATUSKEYDOWN;转入状态2KeyBuf〔i〕。Status。KEYEVENTKEYDOWN;按下事件KeyBuf〔i〕。Status。KEYCOUNT0;计数器清零}else{KeyBuf〔i〕。Status。KEYSTATUSKEYNULL;转入状态0KeyBuf〔i〕。Status。KEYEVENTKEYNULL;空事件}break;状态2:按键按下caseKEYDOWN:if(KeyBuf〔i〕。Status。KEYLEVEL!BitSET)按键释放,端口高电平{KeyBuf〔i〕。Status。KEYSTATUSKEYNULL;转入状态0KeyBuf〔i〕。Status。KEYEVENTKEYUP;松开事件}elseif((KeyBuf〔i〕。Status。KEYLEVELBitSET)(KeyBuf〔i〕。Status。KEYCOUNTKEYLONGDOWNDELAY))超过KEYLONGDOWNDELAY没有释放{KeyBuf〔i〕。Status。KEYSTATUSKEYLONG;转入状态3KeyBuf〔i〕。Status。KEYEVENTKEYLONG;长按事件KeyBuf〔i〕。Status。KEYCOUNT0;计数器清零}else{KeyBuf〔i〕。Status。KEYEVENTKEYNULL;空事件}break;状态3:按键连续按下caseKEYLONG:if(KeyBuf〔i〕。Status。KEYLEVEL!BitSET)按键释放,端口高电平{KeyBuf〔i〕。Status。KEYSTATUSKEYNULL;转入状态0KeyBuf〔i〕。Status。KEYEVENTKEYUP;松开事件}elseif((KeyBuf〔i〕。Status。KEYLEVELBitSET)(KeyBuf〔i〕。Status。KEYCOUNTKEYLONGDOWNDELAY))超过KEYLONGDOWNDELAY没有释放{KeyBuf〔i〕。Status。KEYEVENTKEYLONG;长按事件KeyBuf〔i〕。Status。KEYCOUNT0;计数器清零}else{KeyBuf〔i〕。Status。KEYEVENTKEYNULL;空事件}break;default:break;}}}
main。c主函数调用TIM3初始化includedelay。hincludeusart。hincludestm32f10x。hincludekey。hintmain(){delayinit();初始化延时函数NVICPriorityGroupConfig(NVICPriorityGroup2);设置NVIC中断分组2,2位抢占优先级和2位子优先级usartinit(115200);初始化串口1,波特率为115200TIM3Init(19999,71);1MHz,每20ms检测按键一次;KEYInit();KEY初始化while(1){delayms(1);}}3。实现效果
图紫吊兰的养殖方法和注意事项温度是否适宜十分关键紫吊兰又被叫做彩叶吊兰,它们的叶片柔软而又漂亮。枝条既刚且柔,形似展翅跳跃的仙鹤,故古有折鹤兰之称。总之,用紫吊兰装饰环境可起到别致的点缀效果。紫吊兰现在深受人们的喜爱。……
1999元,海信阅读手机A9黛青色版本开售作为手机品牌,海信手机一直致力于阅读手机品类的研发。此前,海信手机已经推出了彩墨屏阅读手机A5CA5Pro、海信5G阅读手机A7彩墨屏CC版等多款产品。今年5月,海信推出了海信……
图紫色满天星花还有这样神奇的花语满天星,属于石竹科,由于花型小、浅色、花姿蓬松且具立体感,气质淡雅、清秀,给人以朦胧感,是当今流行的鲜花之一,且多做陪衬作用。紫色满天星是其中的一种。鲜花是生活中最常见的……
图波斯猫饮食的注意事项给喵星人喂食要牢记三点波斯猫有一身飘逸华丽的毛发和漂亮的脸蛋,因此它们深受人们的喜爱,还有猫中王子的美称。这种猫咪性格甜美、温和,它们能很快融入新环境之中,让家庭和谐温暖。波斯猫的美貌是有目共……
图种荷花用什么盆这样选容器根长得快映日荷花别样红,出于对荷花的喜爱,很多朋友都喜欢自己在家栽种荷花。这样夏季来临,朋友们在家就能欣赏到美丽的荷花。不过盆栽荷花并不容易,需要注意的事项有很多。栽种荷花是一个……
我看不清他的脸(纪念平民英雄王立江)头条创作挑战赛我看不清他的脸文韵雨临风他是佳城不知疲倦的大白没有人知道他是谁当病毒猖獗、蔓延随时可能出现大面积感染危机时刻……
图紫蔷薇的花语分析几种含义让你充分了解它蔷薇花喜欢成团成簇的开放,在开花期给人一种非常震慑的视觉享受。那么,紫色的蔷薇花有什么样的特点呢?你对蔷薇花的了解又有多少?下面就一起看看吧。一、紫色。紫色的蔷薇花……
vivox80手机测评,说出你的真感觉今天买了一套x80,说一下一周感受,屏幕三星的感觉还是很不错的,用起来也很流畅,耗电还是挺快的,但是一天一充也是能够达到的,还有他的闪充真的是快的飞一样,半个小时差不多吧,……
图紫灵芝的种植方法几个因素让你搞定栽培技术紫灵芝是一种非常稀有的中药,因为罕见,所以在种植上也会有一定的难度。那么,我们常见的种植手法就是大棚种植了,怎么才能让够紫灵芝更好的生长呢?一、生长发育周期。紫灵芝……
图泰迪犬怎样训练更乖巧学会三个技巧让狗狗更温顺对泰迪犬进行科学而又有效的训练,可以不同程度地改善泰迪犬的神经类型和体型外貌,并且培养狗狗良好的行为特征。对于没有经验的主人,掌握合适的驯狗方法非常重要。很多爱宠人士都知……
图紫藤萝花期这些养护小知识一定不可错过顾名思义,紫藤萝的花朵颜色大多为紫色,清新淡雅的花色为它增色不少,俘获了不少人的喜爱。那么你知道紫藤萝花期是什么时候吗?下面就来一起了解一下吧!了解紫藤萝花期盛开时间,能……
图辨别萨摩耶幼犬必备的知识这样选才能买到纯种狗萨摩耶犬又被称为萨摩耶,原是西伯利亚的原住民萨摩耶族培育出的犬种,一岁前调皮、灵动。成年后的萨摩耶外表俊朗,性格乖巧,被众多爱宠人士称之为微笑天使。萨摩耶是外表非常好看的……