你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32CUBEMX+MDK5实现按键以及LED灯

[复制链接]
STMCU小助手 发布时间:2022-5-6 10:47
1 概述
1.1 资源概述

开发板:正点原子STM32F103 Nano开发板
CUBEMX版本:1.3.0
MDK版本:5.23
主控芯片型号:STM32F103RBT6
ZS`RD5LNO2DBRH(2M@_V.png

1.2 实现功能
使用KEY0~KEY3分别控制LED0到LED3的亮灭,KEY_UP控制LED4的亮灭.按一次按键对应的灯亮,再按一次对应的灯灭。另外长按KEY0可以实现蜂鸣器的鸣响。

2 CUBEMX的配置
2.1 GPIO口配置
2.1.1 端口配置图

(部分引脚配置此次实验未用,但是不影响程序的编译,不会报错)

5HZ[J_8UQATS(}_(DF]0L.png

2.1.2 端口资源汇总表
查阅开发板的电路图确认实验用到的GPIO口配置资源如下。

6AJ(ZJA]8LQ2}TJ}79FYM.png

2.1.3 通用GPIO口的配置
KEY_UP是输出高有效,因此初始化时,配置输入下拉。其余LED端口或者按键均是低有效,所以配置为上拉。这里定义User Label(用户标签),方便后续程序的移植。

X2[OWJ']04K)LR00U{W22.png

2.2 时钟配置
2.2.1时钟RCC配置图
使用外部HSE,PLL选择9倍频,系统时钟选择PLL,HCLK配置为72MHz,APB1设置为2分频。

45$~GD1JD1A1KZC]LE_86IP.png

2.2.2晶振选择
选择外部晶振,高速晶振为8MHz,低速晶振为32.768KHz。

6QJ]9AV`DL$S@`NWU9YT12G.png

2.3 调试端口
采用SW方式进行调试烧录。

60RV@X1OQ[W63WY[OVR[DRM.png

2.4 CUBEMX工程管理配置
1、选择Advanced、MDK-ARM、V5。

BGZHEWX~8NUI8DA8`T1IUYI.png

2、代码生成配置
勾选生成外设C文件和H文件。否则这些端口的配置都会存在于main函数中。

RCD2K]7IVJ7RK3N]P8IJI.png

3、高级设置
全部勾选HAL函数。HAL函数的全称是Hardware abstract layer(硬件抽象层),这里还可以选择LL即Low layer(底层),函数和HAL完全不一样,优点是直接操作底层,程序占用空间小。咨询ST原厂FAE工程师,后续新开发芯片均不在支持标准库函数,仅支持HAL和LL函数。比如最新的H7系列芯片,就没有标准库函数的支持。

QTIALM`}$`@M{F26MQZ}K(6.png

2.3 选择生成代码
点击Generate code,生成软件代码,在之后的提示框中点击打开工程。

20200328173226350.png

3 main函数增加代码
3.1 端口初始化函数

在生成的main函数中增加相应的功能函数,端口初始化函数,给各个GPIO口赋值初值,防止上电时状态不确定。写入串口打印函数,提示信息。

  1. /* USER CODE BEGIN 2 */
  2. HAL_GPIO_WritePin(LED0_GPIO_Port,LED0_Pin,GPIO_PIN_SET);
  3. HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_SET);
  4. HAL_GPIO_WritePin(LED2_GPIO_Port,LED2_Pin,GPIO_PIN_SET);
  5. HAL_GPIO_WritePin(LED3_GPIO_Port,LED3_Pin,GPIO_PIN_SET);
  6. HAL_GPIO_WritePin(LED4_GPIO_Port,LED4_Pin,GPIO_PIN_SET);
  7. HAL_GPIO_WritePin(LED5_GPIO_Port,LED5_Pin,GPIO_PIN_SET);
  8. HAL_GPIO_WritePin(LED6_GPIO_Port,LED6_Pin,GPIO_PIN_SET);
  9. HAL_GPIO_WritePin(LED7_GPIO_Port,LED7_Pin,GPIO_PIN_SET);//初始化LED灯的状态,全部为灭
  10. HAL_GPIO_WritePin(BEEP_GPIO_Port,BEEP_Pin,GPIO_PIN_SET);//初始化BEEP,不响

  11. uint8_t temp[]="Press a key\r\n ";//向串口输入提示信息

  12.   /* USER CODE END 2 */
复制代码

3.2 功能实现函数
在while(1)函数中增加下述函数,实现不间断的按键检测

  1. while (1)
  2. {
  3.         /* USER CODE END WHILE */

  4.         /* USER CODE BEGIN 3 */

  5.         if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)==RESET)//判定KEY0按键是否有按下
  6.         {
  7.                 HAL_Delay(10);                                                                //延时,用于按键消抖
  8.                 if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)==RESET) //判定KEY0按键是否有按下
  9.                 {
  10.                         HAL_Delay(500);//判定KEY0按键是否长按
  11.                         if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)==RESET) //KEY0按键被长按
  12.                         {
  13.                                 HAL_GPIO_WritePin(BEEP_GPIO_Port,BEEP_Pin,GPIO_PIN_RESET);//开启蜂鸣器
  14.                                 HAL_Delay(1000);//开启蜂鸣器1000ms
  15.                                 HAL_UART_Transmit(&huart1,temp,sizeof(temp),50);//向串口输出提示信息
  16.                                 HAL_GPIO_WritePin(BEEP_GPIO_Port,BEEP_Pin,GPIO_PIN_SET);//关闭蜂鸣器
  17.                         }
  18.                         else         
  19.                           HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);//KEY0按键为短按,LED0灯状态翻转
  20.                         HAL_UART_Transmit(&huart1,temp,sizeof(temp),50);
  21.                 }
  22.         }
  23.         else  if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==RESET)
  24.         {
  25.                 HAL_Delay(10);
  26.                 if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==RESET)
  27.                 {
  28.                         HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);
  29.                         HAL_Delay(200);
  30.                         HAL_UART_Transmit(&huart1,temp,sizeof(temp),50);
  31.                 }
  32.         }
  33.         else   if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==RESET)
  34.         {
  35.                 HAL_Delay(10);
  36.                 if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==RESET)
  37.                 {
  38.                         HAL_GPIO_TogglePin(LED2_GPIO_Port,LED2_Pin);
  39.                         HAL_Delay(200);
  40.                         HAL_UART_Transmit(&huart1,temp,sizeof(temp),50);
  41.                 }
  42.         }
  43.         else   if(HAL_GPIO_ReadPin(KEYUP_GPIO_Port,KEYUP_Pin)==SET)
  44.         {
  45.                 HAL_Delay(10);
  46.                 if(HAL_GPIO_ReadPin(KEYUP_GPIO_Port,KEYUP_Pin)==SET)
  47.                 {
  48.                         HAL_GPIO_TogglePin(LED3_GPIO_Port,LED3_Pin);
  49.                         HAL_Delay(200);
  50.                         HAL_UART_Transmit(&huart1,temp,sizeof(temp),50);
  51.                 }
  52.         }
  53. }
  54. /* USER CODE END 3 */
复制代码

4 实验结果
4.1 串口打印

使用串口调试助手,按下一次按键后,串口会打印一个Press a key的字符

U)4HDWF~8FROB$G)2H`FX}2.png

4.2 硬件实现
将程序下载到开发板中,验证软件功能。全部功能实现完成。
通过按键将全部的灯点亮

1WXQ[AWLGY5`{~KCSH8MS5X.png

通过按键熄灭LED1和LED2

@KLY}71TJ%ERC@NVSD9SN(4.png

5 按键检测程序之魔鬼数字优化
参考正点原子的官方例程,按键这一段是这么写的

  1. u8 KEY_Scan(u8 mode)
  2. {         
  3.         static u8 key_up=1;//按键按松开标志
  4.         if(mode)key_up=1;  //支持连按                  
  5.         if(key_up&&(KEY0==0||KEY1==0||KEY2==0||WK_UP==1))
  6.         {
  7.                 delay_ms(10);//去抖动
  8.                 key_up=0;
  9.                 if(KEY0==0)return KEY0_PRES;
  10.                 else if(KEY1==0)return KEY1_PRES;
  11.                 else if(KEY2==0)return KEY2_PRES;
  12.                 else if(WK_UP==1)return WKUP_PRES;
  13.         }else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)key_up=1;              
  14.         return 0;// 无按键按下
  15. }
复制代码

初次看感觉非常绕,云里雾里,一会0一会1的,传递的全部是数值,非常不利于阅读,于是增加了一些宏定义,消除掉这些魔鬼数字。将key_up的变量名进行了更改。
增加的宏定义

  1. #define KEYOFF 1
  2. #define KEYON 0

  3. #define SINGLE_CLICK 0
  4. #define CONTINUOUS_CLICK 1
  5. #define NOKEY_PRESS 0
复制代码

依据宏定义改后的程序

  1. u8 KEY_Scan(u8 mode)
  2. {         
  3.         static u8 keyState=KEYOFF;//按键状态为OFF
  4.         if(mode==CONTINUOUS_CLICK)
  5.                 keyState=KEYOFF;  //按键初始态为OFF
  6.         if((keyState==KEYOFF)&&(KEY0==0||KEY1==0||KEY2==0||WK_UP==1))
  7.         {
  8.                 delay_ms(10);//去抖动
  9.                 keyState=KEYON;
  10.                 if(KEY0==0)
  11.                         return KEY0_PRES;
  12.                 else if(KEY1==0)
  13.                         return KEY1_PRES;
  14.                 else if(WK_UP==1)
  15.                         return WKUP_PRES;
  16.                 else if(KEY2==0)
  17.                         return KEY2_PRES;
  18.         }else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)
  19.          {
  20.                  keyState=KEYOFF;
  21.          }   
  22.         return NOKEY_PRESS;// 无按键按下
复制代码

主函数中引用的地方原来为

  1. key=KEY_Scan(0;                    //扫描按键
  2.         if((Key_Queue!=NULL)&&key)           //消息队列Key_Queue创建成功,并且按键被按下
复制代码

更改后为

  1. key=KEY_Scan(SINGLE_CLICK);                    //扫描按键
  2.         if((Key_Queue!=NULL)&&(key!=NOKEY_PRESS))           //消息队列Key_Queue创建成功,并且按键被按下
复制代码

这样就可以知道函数在主函数中调用方式为单次按有效,按键函数的逻辑也很清楚了,就是初次进入函数,静态变量keystate为keyoff,第二项条件式不满足,跳转到按键检测中,当有按键被按下时,keystate为keyon,并返回键值。如果按键没有松手,那么第二次进入这个函数时,if((keyStateKEYOFF)&&(KEY00||KEY10||KEY20||WK_UP==1))条件不满足,无法对按键进行检测。直到按键松手,keyState状态为Keyoff后,才可再次进去按键检测状态。同理可以理解连续按时的实现逻辑。

整个按键的功能的实现如下流程图

MRP$(UPVY_1EL03~ZWW4L_V.png


收藏 评论0 发布时间:2022-5-6 10:47

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版