范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

STM32F103C8T6标准库函数实现多按键检测状态机短按长按识别

  前言
  制作航模遥控器需要用到多按键检测,实现过程中主要参考了以下两篇文章,尤其是第一篇收获最大,作者的代码思想很好,但文中部分代码有误,实际运行时检测到的IO电平是错误的,花费了一天时间才调通,简单记录一下。 1.电路连接
  使用STM32F103C8T6蓝色板,按键采用共阴极连接。
  6个按键:
  CH1Left 接PB5
  CH1Right 接PB4
  CH2Up 接PA15
  CH2Down 接PB3
  CH4Left 接PA12
  CH4Right 接PA11
  串口USB-TTL接法:
  GND 电源地
  3V3 接3.3V
  TXD 接PB7
  RXD 接PB6
  ST-LINK V2接法:
  GND 电源地
  3V3 接3.3V
  SWCLK 接DCLK
  SWDIO 接DIO
  2.程序实现
  key.h - 主要定义结构体和函数预定义  #ifndef __KEY_H #define __KEY_H	  #include "stm32f10x.h" #include "stm32f10x_gpio.h"  typedef struct // 构造按键初始化类 { 	GPIOMode_TypeDef GPIO_Mode; // 初始化按键模式 	GPIO_TypeDef* GPIOx; // 初始化按键口 	uint16_t GPIO_Pin_x; // 初始化按键引脚好 	uint32_t RCC_APB2Periph_GPIOx; // 初始化时钟 }Key_Init;   typedef enum _KEY_STATUS_LIST // 按键状态 { 	KEY_NULL = 0x00, // 无动作 	KEY_SURE = 0x01, // 确认状态 	KEY_UP   = 0x02, // 按键抬起 	KEY_DOWN = 0x04, // 按键按下 	KEY_LONG = 0x08, // 长按 }KEY_STATUS_LIST;   typedef struct _KEY_COMPONENTS // 状态机类 {     FunctionalState KEY_SHIELD; //按键屏蔽,DISABLE(0):屏蔽,ENABLE(1):不屏蔽 	uint8_t KEY_COUNT;        	//按键长按计数     BitAction KEY_LEVEL;        //最终按键状态,按下Bit_SET(1),抬起Bit_RESET(0)     BitAction KEY_DOWN_LEVEL;   //按下时,按键IO实际的电平     KEY_STATUS_LIST KEY_STATUS;       //按键状态     KEY_STATUS_LIST KEY_EVENT;        //按键事件     BitAction (*READ_PIN)(Key_Init Key);//读IO电平函数 }KEY_COMPONENTS;     typedef struct // 按键类 { 	Key_Init Key; // 继承初始化父类 	KEY_COMPONENTS Status; // 继承状态机父类 }Key_Config;     typedef enum // 按键注册表 { 	CH1Left, 	CH1Right, 	CH2Up, 	CH2Down, 	CH4Left, 	CH4Right,// 用户添加的按钮名称 	KEY_NUM, // 必须要有的记录按钮数量,必须在最后 }KEY_LIST;     void KEY_Init(void);//IO初始化 void Creat_Key(Key_Init* Init); // 初始化按钮函数 void ReadKeyStatus(void); // 状态机函数 void TIM3_Init(u16 arr,u16 psc); #endif
  原文中Key用的是指针,结果导致读电平函数GPIO_ReadInputDataBit()寻址错误,才使得读出的电平有误。 typedef struct // 按键类 { 	Key_Init *Key; // 继承初始化父类 	KEY_COMPONENTS Status; // 继承状态机父类 }Key_Config;
  key.c - TIM3定时器初始化,定时检测按键状态;有限状态机实现  #include "stm32f10x.h" #include "key.h" #include "sys.h"  #include "delay.h" #include "usart.h"   //参考链接https://blog.csdn.net/qq_42679566/article/details/105892105,原文错误已修正   Key_Config Key_Buf[KEY_NUM];	// 创建按键数组 #define KEY_LONG_DOWN_DELAY 30 	// 设置30个TIM3定时器中断=600ms算长按	 #define DBGMCU_CR  (*((volatile u32 *)0xE0042004)) 	 /*通用定时器3中断初始化,使用TIM3控制按键定时检测   时钟选择为APB1的2倍,而APB1为36M * 参数:arr:自动重装值。 		psc:时钟预分频数 */ void TIM3_Init(u16 arr,u16 psc) { 	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; 	NVIC_InitTypeDef NVIC_InitStructure; 	 	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); 		//时钟使能   	TIM_TimeBaseInitStructure.TIM_Period = arr; //自动重装载寄存器周期的值 	TIM_TimeBaseInitStructure.TIM_Prescaler=psc;  //预分频值 	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; // 向上计数 	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //时钟分割为0,仍然使用72MHz 	TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);//允许更新中断 	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure); 	 	 	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; 	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;//抢占优先级0 	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3 	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能 	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化NVIC寄存器 	 	TIM_Cmd(TIM3,ENABLE); }   void TIM3_IRQHandler(void)   //TIM3中断 { 	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)  //检查TIM3更新中断发生与否 	{ 		// 中断处理代码 		ReadKeyStatus();  //调用状态机 		u8 i,status; 		for(i = 0;i < KEY_NUM;i++)     	{ 			status = Key_Buf[i].Status.KEY_EVENT; 			//if(status!=KEY_NULL) printf("%d,%d ",i,status);//事件处理 			if(status==KEY_DOWN) printf("%d短按 ",i); 			if(status==KEY_LONG) printf("%d长按 ",i); 		} 		TIM_ClearITPendingBit(TIM3, TIM_IT_Update);  //清除TIMx更新中断标志  	} }   //按键初始化函数 void KEY_Init(void) //IO初始化 {  	Key_Init KeyInit[KEY_NUM]= 	{  		{GPIO_Mode_IPU, GPIOB, GPIO_Pin_5, RCC_APB2Periph_GPIOB}, 	// 初始化按键CH1Left 		{GPIO_Mode_IPU, GPIOB, GPIO_Pin_4, RCC_APB2Periph_GPIOB}, 	// 初始化按键CH1Right 		{GPIO_Mode_IPU, GPIOA, GPIO_Pin_15, RCC_APB2Periph_GPIOA}, 	// 初始化按键CH2Up 		{GPIO_Mode_IPU, GPIOB, GPIO_Pin_3, RCC_APB2Periph_GPIOB}, 	// 初始化按键CH2Down 		{GPIO_Mode_IPU, GPIOA, GPIO_Pin_12, RCC_APB2Periph_GPIOA}, 	// 初始化按键CH4Left 		{GPIO_Mode_IPU, GPIOA, GPIO_Pin_11, RCC_APB2Periph_GPIOA}, 	// 初始化按键CH4Right 	}; 	Creat_Key(KeyInit); // 调用按键初始化函数 	 	//STM32没有彻底释放PB3作为普通IO口使用,切换到SW调试可释放PB3、PB4、PA15 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); 	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); 	DBGMCU_CR &=0xFFFFFFDF;  //如果没有这段代码,PB3就会一直是低电平 }   static BitAction KEY_ReadPin(Key_Init Key) //按键读取函数 {   return (BitAction)GPIO_ReadInputDataBit(Key.GPIOx,Key.GPIO_Pin_x); }   void Creat_Key(Key_Init* Init) { 	uint8_t i;  	GPIO_InitTypeDef  GPIO_InitStructure[KEY_NUM];   	for(i = 0;i < KEY_NUM;i++) 	{ 		Key_Buf[i].Key = Init[i]; // 按钮对象的初始化属性赋值 		RCC_APB2PeriphClockCmd(Key_Buf[i].Key.RCC_APB2Periph_GPIOx, ENABLE);//使能相应时钟 		GPIO_InitStructure[i].GPIO_Pin = Key_Buf[i].Key.GPIO_Pin_x;	//设定引脚			 		GPIO_InitStructure[i].GPIO_Mode = Key_Buf[i].Key.GPIO_Mode; 	//设定模式		 		GPIO_Init(Key_Buf[i].Key.GPIOx, &GPIO_InitStructure[i]);       //初始化引脚 		// 初始化按钮对象的状态机属性 		Key_Buf[i].Status.KEY_SHIELD = ENABLE; 		Key_Buf[i].Status.KEY_COUNT = 0; 		Key_Buf[i].Status.KEY_LEVEL = Bit_RESET; 		if(Key_Buf[i].Key.GPIO_Mode == GPIO_Mode_IPU) // 根据模式进行赋值 			Key_Buf[i].Status.KEY_DOWN_LEVEL = Bit_RESET; 		else 			Key_Buf[i].Status.KEY_DOWN_LEVEL = Bit_SET; 		Key_Buf[i].Status.KEY_STATUS = KEY_NULL; 		Key_Buf[i].Status.KEY_EVENT = KEY_NULL; 		Key_Buf[i].Status.READ_PIN = KEY_ReadPin;	//赋值按键读取函数 	} }   static void Get_Key_Level(void) // 根据实际按下按钮的电平去把它换算成虚拟的结果 {     uint8_t i;          for(i = 0;i < KEY_NUM;i++)     {         if(Key_Buf[i].Status.KEY_SHIELD == DISABLE)             continue;         if(Key_Buf[i].Status.READ_PIN(Key_Buf[i].Key) == Key_Buf[i].Status.KEY_DOWN_LEVEL)             Key_Buf[i].Status.KEY_LEVEL = Bit_SET;         else             Key_Buf[i].Status.KEY_LEVEL = Bit_RESET;     } }   void ReadKeyStatus(void) {     uint8_t i; 	     Get_Key_Level(); 	     for(i = 0;i < KEY_NUM;i++)     {         switch(Key_Buf[i].Status.KEY_STATUS)         {             //状态0:没有按键按下             case KEY_NULL:                 if(Key_Buf[i].Status.KEY_LEVEL == Bit_SET)//有按键按下                 {                     Key_Buf[i].Status.KEY_STATUS = KEY_SURE;//转入状态1 					Key_Buf[i].Status.KEY_EVENT = KEY_NULL;//空事件                 }                 else                 {                     Key_Buf[i].Status.KEY_EVENT = KEY_NULL;//空事件                 }                 break;             //状态1:按键按下确认             case KEY_SURE:                 if(Key_Buf[i].Status.KEY_LEVEL == Bit_SET)//确认和上次相同                 {                     Key_Buf[i].Status.KEY_STATUS = KEY_DOWN;//转入状态2 					Key_Buf[i].Status.KEY_EVENT = KEY_DOWN;//按下事件                     Key_Buf[i].Status.KEY_COUNT = 0;//计数器清零                 }                 else                 {                     Key_Buf[i].Status.KEY_STATUS = KEY_NULL;//转入状态0                     Key_Buf[i].Status.KEY_EVENT = KEY_NULL;//空事件                 }                 break;             //状态2:按键按下             case KEY_DOWN:                 if(Key_Buf[i].Status.KEY_LEVEL != Bit_SET)//按键释放,端口高电平                 {                     Key_Buf[i].Status.KEY_STATUS = KEY_NULL;//转入状态0                     Key_Buf[i].Status.KEY_EVENT = KEY_UP;//松开事件                 }                 else if((Key_Buf[i].Status.KEY_LEVEL == Bit_SET) 					&& (++Key_Buf[i].Status.KEY_COUNT >= KEY_LONG_DOWN_DELAY)) //超过KEY_LONG_DOWN_DELAY没有释放                 {                     Key_Buf[i].Status.KEY_STATUS = KEY_LONG;//转入状态3                     Key_Buf[i].Status.KEY_EVENT = KEY_LONG;//长按事件 					Key_Buf[i].Status.KEY_COUNT = 0;//计数器清零                 }                 else                 {                     Key_Buf[i].Status.KEY_EVENT = KEY_NULL;//空事件                 }                 break;             //状态3:按键连续按下             case KEY_LONG:                 if(Key_Buf[i].Status.KEY_LEVEL != Bit_SET)//按键释放,端口高电平                 {                     Key_Buf[i].Status.KEY_STATUS = KEY_NULL;//转入状态0                     Key_Buf[i].Status.KEY_EVENT = KEY_UP;//松开事件                 }                 else if((Key_Buf[i].Status.KEY_LEVEL == Bit_SET)                  && (++Key_Buf[i].Status.KEY_COUNT >= KEY_LONG_DOWN_DELAY)) //超过KEY_LONG_DOWN_DELAY没有释放                 {                     Key_Buf[i].Status.KEY_EVENT = KEY_LONG;//长按事件                     Key_Buf[i].Status.KEY_COUNT = 0;//计数器清零                 }                 else                 {                     Key_Buf[i].Status.KEY_EVENT = KEY_NULL;//空事件                 }                 break; 			default: 				break;         } 	} }
  main.c - 主函数调用TIM3初始化  #include "delay.h" #include "usart.h" #include "stm32f10x.h" #include "key.h" int main() {     delay_init();//初始化延时函数     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2,2位抢占优先级和2位子优先级     usart_init(115200);//初始化串口1,波特率为115200     TIM3_Init(19999,71);//1MHz,每20ms检测按键一次;     KEY_Init();		//KEY初始化     while(1){         delay_ms(1);     } }3.实现效果

腾讯的下一个增长点在哪里?中国的互联网巨头腾讯公布了一份具有亮点的财报,据一些媒体认为,财报的亮点是,目前腾讯似乎在努力告别粗放经营。腾讯公布2022财年第一季度财报,营收1354。71亿元,同比基本持平净做大合作蛋糕共创美好未来新华社北京5月19日电题做大合作蛋糕共创美好未来解读习近平主席在庆祝中国贸促会建会70周年大会暨全球贸易投资促进峰会上的致辞新华社记者谢希瑶潘洁侯雪静让我们携起手来,坚持和平发展合就在今天!新一轮乐购深圳消费券中午12时开抢今天中午12时,新一轮乐购深圳美团消费券开抢。第四轮发放时间5月19日12005月25日2359。消费券类型美食消费券类型堂食消费券满100元减50元满200元减80元满300减1买社保考驾照逛公园,港台明星融入内地生活,拿到身份证好激动成功申领到港澳居民居住证的鸠摩智李国麟,终于实现了自己有生之年的一个小目标。已定居福州一年多的他,日前在个人社交平台晒出新证件,开心宣称自己现在有了身份。作为被内地观众熟识的港星之19岁深圳男大学生爆红!纯情奶狗脸,垃圾堆里出大片继刘畊宏之后的,又一个男人屠榜某音热搜,成为我的新任5月男友。话题过亿,一个月涨粉526万。最最最重要的是,他用实力一下子就击中了我的,呃呃,心巴。这个男人,就是垫底辣孩。初次见面对公司新来的同事心动了,感觉机会就在眼前,该怎么把握这不是惊喜,就是这么训练故意的,去过好几个海洋馆了,全是这样套路不要当舔狗!你一定知道这是什么电影,讲戏中!马上520该送什么礼物?朋友你这是第一次开这么粗的水管阀门吗,你的衣服也登深圳小南山公园,近观壮阔的海上蛇口美景,远眺壮丽的海上夕阳(文章相片均为作者实地拍摄,创作不易,转载请标明出处)有想去看看深圳美丽又壮阔的海上美景吗?有想去看看深圳壮丽的夕阳西下余晖吗?如果有想法,那么千万不能错过这篇文章,在这里统统都能深圳一名警察因一句嘱托,坚持替死刑犯养育儿子十年,后来如何?前言你爸爸当时因为犯了大错,判了死刑,很早以前已经执行了。2014年,在经历十年的漫长时光之后,深圳民警叶剑雄见到了让他牵挂已久的陈雨奇。雨奇的父亲陈某因为犯下杀人重罪,2005年深圳31岁女子整容后智力为一岁婴儿水平,整容不再是你的救命良药为何素颜能成为人们心中一直的顶流?因为在这个物质横流的时代,有太多的虚伪了,肉眼根本辨别不出事情的真假。网络上各种层出不穷的网红,你会发现却都是一个模样,高高的鼻子,尖尖的下巴,以为何军队一律不得经商?这些国家的教训就摆在眼前任何一个正常国家都非常重视本国的军事发展,以建立强大的军事力量维护国防安全,为了支持这种发展,国家必须投入大量的经费给军队,以维持军队的运作,这种经费通常称为军费。由于各国经济实力蒋介石曾计划刺杀宋庆龄,宋美龄一句话,让他在最后一刻取消行动民国时期赫赫有名的宋氏三姐妹宋霭龄宋庆龄宋美龄,三姐妹感情深厚,却因为不同的理想和追求走上了不同的道路。其中,宋美龄嫁给了蒋介石,而宋庆龄则是孙中山先生的夫人。由于立场不同,蒋介石
甲流来袭!建议家里备好4样东西,关键时能救命大家好,我是真心爱厨房。今天又来给大家分享美食做法,每一道菜都是用心去研究,详细到每一个步骤,全部是由小编通过实践亲自摸索出来的。为了让大家更好的掌握美食技巧做法,保证您一看就会,技术分析方法向左看(我对三角形价格形态的认知)三角形价格形态通常属于持续形态。是对当前趋势的一种调整。之后还是以当前趋势保持一致。但是在市场底部或顶部往往充当重要的反转形态。多是以头肩底(顶)演变而成的。主要出现在日线级别以上高质量发展丨看经济大省如何领跑2023视频加载中经济,国之命脉经济大省,国家经济发展的顶梁柱。广东江苏浙江山东河南四川6个经济大省,经济总量市场主体数量吸纳就业都占全国4成以上。经济大省要勇挑大梁!这是中央要求,是大块普法强基超400万人次参与!今天你答题了吗?由中共云南省委宣传部中共云南省委政法委员会中共云南省委全面依法治省委员会办公室云南省司法厅云南省普及法律常识办公室主办云南发布云南网承办的普法强基法律常识在线答题活动于2月24日正老河口返乡创业续辉煌乡村振兴添力量湖北日报客户端讯(通讯员王雅丹李闻)近日,记者在老河口市孟楼镇轻工产业园的奥丰纺织有限公司生产车间看到,生产线上的梳棉机纺纱机转个不停,工人穿梭在机器间,确保一切运转正常。得益于近沙伊和解引发蝴蝶效应,24小时内又有5国找中国,美国该出局了!沙伊两国达成和解,不到72小时,又有5国找上中国,想要寻求中国的帮助。中东这盘大棋,看来美国已经出局了。这5国到底找中国做什么?美国到底又为何已经出局?近日,中国沙特和伊朗三国发表男性陪产假,是权利也是责任汪昌莲今年全国两会上,多位代表委员的建言聚焦男性育儿责任问题。全国政协委员丁磊建议推出夫妻共用产假制度,提高男性共同抚养责任。全国人大代表民建广东省委会副主委华南师范大学教授林勇则一岁后喝纯牛奶还是喝奶粉?别一味认可国外建议,还得看娃四特点文菁妈美国儿科学会建议孩子在一岁之后就可以喝全脂牛奶,如果孩子体重超标,还可以考虑喝脱脂牛奶!在国外很多孩子确实到了一岁之后就开始喝纯牛奶,不再喝奶粉!所以现在国内很多家长都说孩子鸡娃真的有效!孩子提升成绩和记忆必看,亲身实践有用的方法很多家长为自己的孩子记忆慢和学习差发愁,今天给大家推荐一本书的方法。第一次接触脑科学考试这本书是网上偶然看到,觉得很有意思,就不断把其中的方法在自己的孩子身上使用,适用一段时间后,养男孩,就像种树!父母给足这6种营养,他会越来越优秀2023育儿季养男孩就像种树,环境适宜了,养料足够了,他才能破土发芽,扎根深处,而后枝繁叶茂。作者枫子养育男孩一书说在我们生活的今天,女孩更能把握自己,她们积极向上,工作努力。但是tDCS对ADHD儿童有哪些帮助?家长最关注的4个问题及调查结果注意力缺陷多动障碍(ADHD)是一种神经发育障碍,其特征是注意力不集中多动和冲动会干扰个体功能。典型症状如下注意力障碍主要表现为患者不能集中注意力,比如走神注意力不集中做事马虎爱丢