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

STM32G4系列MCU学习笔记:按键模块

[复制链接]
STMCU-管管 发布时间:2021-6-18 16:44
STM32G4系列MCU学习笔记:按键模块

前言
我所学过的按键模块有独立按键和矩阵键盘两种,其实两者原理类似,本文主要介绍按键模块中实现长按和短按功能。
注意了:本文是基于HAL库来进行介绍的,如果你想用标准库实现也比较简单,类比移植即可。
由于作者第一次写博客,且自身水平有限,如文中有误,还请大家指正,谢谢~


先放一张开发板的实物图,它长这样~
15.png
一、硬件操作
在应用层中我利用不同的点灯程序来区分短按和长按所进行的操作。所以先来看看该模块所设计到的硬件原理图。


1. 原理图
14.png
注意了:点灯的这个模块,我们这里忽略这个锁存器芯片,不用它的锁存功能,着重于分析按键模块。
13.png
我们先把所使用到的所有GPIO口列出来:(方便待会儿看程序更加清楚)
1.点灯相关的IO口:PC8,PC9,PC10,PC11,PC12,PC13,PC14,PC15;
2.按键相关的IO口:PB0,PB1,PB2,PA0。


2. 硬件分析
从原理图里可以发现,我们如果将PB1设置为输入模式,去采集该GPIO上的电平状态,按键松开时,PB1上的电平应该为高电平,当我们按下按键后,例如:B2按键,PB1上的电平就会被拉低,其余按键类似,所以根据这样一个简单原理,即可实现按键的输入检测。


3. 初始化代码
  1. /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  2.   HAL_Init();//使用默认配置即可
  3.   /* Configure the system clock */
  4.   SystemClock_Config();
  5.   /* Initialize all configured peripherals */
  6.   MX_GPIO_Init();
复制代码
(1)时钟设置
这里我使用的内部时钟源HSI = 16MHz;配置HCLK = PCLK1 = PCLK2 = 80MHz。
  1. void SystemClock_Config(void)
  2. {
  3.   RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  4.   RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  5.   /** Configure the main internal regulator output voltage
  6.   */
  7.   HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
  8.   /** Initializes the CPU, AHB and APB busses clocks
  9.   */
  10.   RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  11.   RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  12.   RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  13.   RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  14.   RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  15.   RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV2;
  16.   RCC_OscInitStruct.PLL.PLLN = 20;
  17.   RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  18.   RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
  19.   RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
  20.   if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  21.   {
  22.     Error_Handler();
  23.   }
  24.   /** Initializes the CPU, AHB and APB busses clocks
  25.   */
  26.   RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
  27.                               |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  28.   RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  29.   RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  30.   RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  31.   RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  32.   if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK)
  33.   {
  34.     Error_Handler();
  35.   }
  36. }
复制代码
(2)GPIO初始化
  1. static void MX_GPIO_Init(void)
  2. {
  3.   GPIO_InitTypeDef GPIO_InitStruct = {0};

  4.   /* GPIO Ports Clock Enable */
  5.   __HAL_RCC_GPIOC_CLK_ENABLE();
  6.   __HAL_RCC_GPIOF_CLK_ENABLE();
  7.   __HAL_RCC_GPIOA_CLK_ENABLE();
  8.   __HAL_RCC_GPIOB_CLK_ENABLE();
  9.   __HAL_RCC_GPIOD_CLK_ENABLE();

  10.   /*Configure GPIO pin Output Level */
  11.   HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_8
  12.                           |GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12, GPIO_PIN_RESET);

  13.   /*Configure GPIO pin Output Level */
  14.   HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);

  15.   /*Configure GPIO pins : PC13 PC14 PC15 PC8
  16.                            PC9 PC10 PC11 PC12 */
  17.   GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_8
  18.                           |GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12;
  19.   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  20.   GPIO_InitStruct.Pull = GPIO_NOPULL;
  21.   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  22.   HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  23.   /*Configure GPIO pin : PA0 */
  24.   GPIO_InitStruct.Pin = GPIO_PIN_0;
  25.   GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  26.   GPIO_InitStruct.Pull = GPIO_NOPULL;
  27.   HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  28.   /*Configure GPIO pins : PB0 PB1 PB2 */
  29.   GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2;
  30.   GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  31.   GPIO_InitStruct.Pull = GPIO_NOPULL;
  32.   HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  33.   /*Configure GPIO pin : PD2 */
  34.   GPIO_InitStruct.Pin = GPIO_PIN_2;
  35.   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  36.   GPIO_InitStruct.Pull = GPIO_NOPULL;
  37.   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  38.   HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

  39. }
复制代码
二、按键模块的驱动层实现
1. 硬件框图
在这里插入图片描述
11.png
避坑点:
短按:按键释放时判断时间
长按:直接判断时间


2. 按键驱动层代码实现
下面直接上C代码:


(1)key.c
  1. extern unsigned int keyCount;
  2. extern uint8_t keyValue;
  3. uint8_t keyflag = 0;
  4. void Key_Scan(void)
  5. {
  6. //keyState  按键状态;
  7. //keyPass   记录哪一个按键被按下
  8. //keyCount  记录按键按下时间
  9. //keyflag   开始计数标志位,方便在Systick中断中进行计时
  10. //keyValue  返回给应用层的键值,用于判断处理相应的事件
  11. //ShoutKeyx和LongKeyx是两个宏,为了区分短按和长按
  12. //ShoutKeyx 短按x键值,
  13. //LongKeyx        长按x键值
  14.     static uint8_t keyState = 0,keyPass = 0;
  15.     switch(keyState)
  16.     {
  17.         case 0:if(KEYB_1==0||KEYB_2==0||KEYB_3==0||KEYB_4==0)
  18.                 {
  19.                     HAL_Delay(100);//消抖
  20.                     keyState = 1;
  21.                     keyPass = 0xff; //初始化按键
  22.                 }   
  23.                 break;
  24.                     
  25.         case 1: if(KEYB_1==0||KEYB_2==0||KEYB_3==0||KEYB_4==0)
  26.                 {
  27.                     keyState = 2;
  28.                     keyflag = 1;//开始计数
  29.                     if(KEYB_1==0){
  30.                         keyPass = 1;//返回对应键值
  31.                     }   
  32.                     else if(KEYB_2==0)keyPass = 2;
  33.                     else if(KEYB_3==0)keyPass = 3;
  34.                     else if(KEYB_4==0)keyPass = 4;
  35.                 }   
  36.                 else keyState = 0;//返回检测按键      
  37.                
  38.                 break;
  39.         case 2:
  40.                 switch(keyPass)
  41.                 {              
  42.                     case 1:if(KEYB_1!=0) //判断是否释放按键
  43.                             {
  44.                                 if((10 < keyCount)&&(keyCount < 800))//短按
  45.                                 {
  46.                                     keyCount = 0;
  47.                                     keyflag = 0;
  48.                                     keyValue = ShoutKey1;
  49.                                     keyState = 0;
  50.                                 }   
  51.                             }      
  52.                             break;
  53.                     case 2:if(KEYB_2!=0)
  54.                             {
  55.                                 if((10 < keyCount)&&(keyCount < 800))//短按
  56.                                 {
  57.                                     keyCount = 0;
  58.                                     keyflag = 0;
  59.                                     keyValue = ShoutKey2;
  60.                                     keyState = 0;
  61.                                 }   
  62.                             }      
  63.                             break;
  64.                     case 3:if(KEYB_3!=0)
  65.                             {
  66.                                 if((10 < keyCount) && (keyCount < 800))//短按
  67.                                 {
  68.                                     keyCount = 0;
  69.                                     keyflag = 0;
  70.                                     keyValue = ShoutKey3;
  71.                                     keyState = 0;
  72.                                 }   
  73.                             }      
  74.                             break;
  75.                     case 4:if(KEYB_4!=0)
  76.                             {
  77.                                 if((10 < keyCount)&&(keyCount < 800))//短按
  78.                                 {
  79.                                     keyCount = 0;
  80.                                     keyflag = 0;
  81.                                     keyValue = ShoutKey4;
  82.                                     keyState = 0;
  83.                                 }   
  84.                             }
  85.                             break;
  86.                 }
  87.                 if(keyCount > 800)//长按
  88.                 {   
  89.                     if(KEYB_1==0)keyValue = LongKey1;
  90.                     else if(KEYB_2==0)keyValue = LongKey2;
  91.                     else if(KEYB_3==0)keyValue = LongKey3;
  92.                     else if(KEYB_4==0)keyValue = LongKey4;

  93.                     keyState = 3;
  94.                 }
  95.                 break;

  96.         case 3: if(KEYB_1==1&&KEYB_2==1&&KEYB_3==1&&KEYB_4==1)
  97.                 {
  98.                     keyCount = 0;
  99.                     keyflag = 0;
  100.                     keyState = 0;
  101.                     keyValue = 0xff;//长按之后释放按键代表不执行操作,就没有键值传出  
  102.                 }
  103.                
  104.                 break;
  105.     }
  106. }
复制代码
(2)key.h
  1. #ifndef __KEY_H
  2. #define __KEY_H
  3. #include "stm32g4xx_hal.h"
  4. #define KEYB_1                 HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0) //PB0
  5. #define KEYB_2                 HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1) //PB1
  6. #define KEYB_3                 HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2)//PB2
  7. #define KEYB_4                 HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0)//PA0
  8. #define ShoutKey1             1
  9. #define ShoutKey2                2
  10. #define ShoutKey3                3
  11. #define ShoutKey4       4
  12. #define LongKey1             5
  13. #define LongKey2                6
  14. #define LongKey3                7
  15. #define LongKey4        8
  16. void Key_Scan(void);
  17. #endif
复制代码
3. 计时操作
我使用Systick写了一个简单的定时中断函数,(触发Systick的定时中断时间是1ms)去处理计时操作,这是为了给按键驱动提供短按和长按的时间,便于在驱动层进行相应的判断,所以我不去深挖Systick模块的细节,本文会用即可,后面再单独来记录学习Systick模块。
下面直接来看看Systick的中断服务函数叭,很简单,就这么几句~
  1. extern uint8_t keyflag;
  2. unsigned int keyCount = 0;
  3. void SysTick_Handler(void)
  4. {
  5.   HAL_IncTick(); //这个api是用于HAL库的延时函数HAL_Delay(uint32_t Delay)
  6.         if(keyflag == 1) //若有按键按下就开始计时
  7.                 keyCount++;       
  8. }
复制代码
三、应用层简单逻辑实现
应用层要做的事情在文章开篇其实已经说了,因为逻辑很简单,就是根据长按和短按实现不同的点灯程序,所以直接上代码叭~
  1. HAL_GPIO_WritePin(GPIOC, GPIO_PIN_All, GPIO_PIN_SET); //关闭所有灯
  2. HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);        //关闭锁存器
  3. while (1)
  4. {
  5.                 Key_Scan();
  6.                 if(keyValue) //有按键按下
  7.                 {
  8.                         if(keyValue < 5) //短按
  9.                         {
  10.                                 switch(keyValue)
  11.                                 {
  12.                                         case 1:
  13.                                                         keyValue = 0;
  14.                                                         HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_8);
  15.                                                         HAL_Delay(100);
  16.                                         break;
  17.                                         case 2:
  18.                                                         keyValue = 0;
  19.                                                         HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_9);
  20.                                                         HAL_Delay(100);
  21.                                         break;
  22.                                         case 3:
  23.                                                         keyValue = 0;
  24.                                                         HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_10);
  25.                                                         HAL_Delay(100);
  26.                                         break;
  27.                                         case 4:
  28.                                                         keyValue = 0;
  29.                                                         HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_11);
  30.                                                         HAL_Delay(100);
  31.                                         break;
  32.                                 }
  33.                         }
  34.                         else
  35.                         {
  36.                                 switch(keyValue)
  37.                                 {
  38.                                         case 5:
  39.                                                         keyValue = 0;
  40.                                                         HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_15);
  41.                                                         HAL_Delay(100);
  42.                                         break;
  43.                                         case 6:
  44.                                                         keyValue = 0;
  45.                                                         HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_14);
  46.                                                         HAL_Delay(100);
  47.                                         break;
  48.                                         case 7:
  49.                                                         keyValue = 0;
  50.                                                         HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
  51.                                                         HAL_Delay(100);
  52.                                         break;
  53.                                         case 8:
  54.                                                         keyValue = 0;
  55.                                                         HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_12);
  56.                                                         HAL_Delay(100);
  57.                                         break;
  58.                                 }
  59.                         }
  60.                 }
  61. }
复制代码
总结
以上就是今天要讲的内容,本文仅仅简单介绍了MCU里按键的短按和长按功能,希望能对你有所帮助噢~。









收藏 评论1 发布时间:2021-6-18 16:44

举报

1个回答
landeng 回答时间:2021-6-19 11:09:17
学习了~~

所属标签

相似分享

官网相关资源

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