本程序的主要功能是实现按键控制灯的亮灭。当灯为灭的状态时按键按下点亮灯,当灯为亮的状态时按键按下熄灭灯,即实现灯的电平翻转操作。
按键扫描是利用 GPIO 下降中断,来监测按键按下动作。并加以消抖操作,便可以获得可靠的按键操作。根据原理图,配置主控芯片与按钮相连引脚外部中断,并设置优先级信息。为了代码可读性和编写方便,我们利用结构体来管理按键信息。同时采用函数指针,进行按键结果回调。
在 GPIO 中断回调函数中,会根据 GPIO 引脚记录按键按下的标志和起始系统 Tick 时间。紧靠这些信息还是无法实现按键操作。还需要程序对按键事件进行消抖操作并回调最终结果。
硬件原理图如下所示:
GPIO.c文件的配置初始化操作。
1 /* USER CODE BEGIN 0 */ 2 //================================================================================= 3 //2018.5.16 中断开发与实例 4 //================================================================================= 5 #define KEY_DELAY_TICK 20//主要用于按键延迟消抖的操作 6 //定义一个按键的事件结构体 7 typedef struct 8 { 9 uint8_t key_event;//按键按下事件 10 int start_tick;//用于消抖的起始时间 Ticks 11 }key_press_t; 12 13 static key_press_t key_check_press = {0,0}; 14 15 static key_cb pFkey_cb; 16 //================================================================================= 17 //================================================================================= 18 /* USER CODE END 0 */ 19 20 /*----------------------------------------------------------------------------*/ 21 /* Configure GPIO */ 22 /*----------------------------------------------------------------------------*/ 23 /* USER CODE BEGIN 1 */ 24 25 /* USER CODE END 1 */ 26 27 /** Configure pins as 28 * Analog 29 * Input 30 * Output 31 * EVENT_OUT 32 * EXTI 33 */ 34 void MX_GPIO_Init(void) 35 { 36 37 GPIO_InitTypeDef GPIO_InitStruct; 38 39 /* GPIO Ports Clock Enable */ 40 __HAL_RCC_GPIOC_CLK_ENABLE(); 41 __HAL_RCC_GPIOH_CLK_ENABLE(); 42 __HAL_RCC_GPIOA_CLK_ENABLE(); 43 __HAL_RCC_GPIOB_CLK_ENABLE(); 44 45 /*Configure GPIO pin Output Level */ 46 HAL_GPIO_WritePin(LED_LD2_GPIO_Port, LED_LD2_Pin, GPIO_PIN_RESET); 47 48 /*Configure GPIO pin : PtPin */ 49 GPIO_InitStruct.Pin = KEY_INPUT_Pin; 50 GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; 51 GPIO_InitStruct.Pull = GPIO_PULLUP; 52 HAL_GPIO_Init(KEY_INPUT_GPIO_Port, &GPIO_InitStruct); 53 54 /*Configure GPIO pin : PtPin */ 55 GPIO_InitStruct.Pin = LED_LD2_Pin; 56 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; 57 GPIO_InitStruct.Pull = GPIO_NOPULL; 58 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; 59 HAL_GPIO_Init(LED_LD2_GPIO_Port, &GPIO_InitStruct); 60 61 } 62 63 /* USER CODE BEGIN 2 */ 64 //************************************** 65 //================================================================================= 66 //================================================================================= 67 // fn : GPIO_Led_Toggle 68 // 69 // brief : 翻转LED控制引脚电平 70 // 71 // param : none 72 // 73 // return : none 74 void GPIO_Led_Toggle(void) 75 { 76 HAL_GPIO_TogglePin(LED_LD2_GPIO_Port,LED_LD2_Pin);//用来控制 LED 引脚电平 77 } 78 //************************************** 79 //************************************** 80 // fn : KEY_RegisterCb 81 // 82 // brief : 注册按钮事件回调 83 // 84 // param : cb -> 处理按钮事件函数指针 85 // 86 // return : none 87 void KEY_RegisterCb(key_cb cb) 88 { 89 if(cb != 0) 90 { 91 pFkey_cb = cb; 92 } 93 } 94 95 //************************************** 96 //在 GPIO 中断回调函数中,会根据 GPIO 引脚记录按键按下的标志和起始系统 Tick 时间。 97 //紧靠这些信息还是无法实现按键操作。还需要程序对按键事件进行消抖操作并回调最终结果。 98 // fn : KEY_Poll 99 // 100 // brief : 轮询按钮事件 101 // 102 // param : none 103 // 104 // return : none 105 //在 KEY_Poll 函数中,如果发现 key_event 不为 0,即发现引脚有中断产生。则会进行延 106 //时操作,并最终读取引脚电平,确认按键是否真的按下。 如果真的有按键按下操作,且根据 107 //回调函数指针进行结果上报。 108 void KEY_Poll(void) 109 { 110 uint8_t key_event = 0 ; 111 if(key_check_press.key_event)//key_check_press.key_event = 0x01 112 { 113 if(HAL_GetTick() - key_check_press.start_tick >= KEY_DELAY_TICK )//delay 20ms 114 { 115 if(key_check_press.key_event & KEY_INPUT)//0x01 & 0x01 = true 116 { 117 if(HAL_GPIO_ReadPin(KEY_INPUT_GPIO_Port,KEY_INPUT_Pin) == GPIO_PIN_RESET)//读取引脚电平判断是否按下 118 { 119 key_event |= KEY_INPUT;//全部为false为false 结果为1 true 120 } 121 key_check_press.key_event ^= KEY_INPUT;//异或 0^0x01 = 1 1^0x01 = 0 赋值为0(相等为0,不等为1)等待下一次的按下触发操作 122 } 123 } 124 } 125 //======================================================================= 126 //如果真的有按钮按下,则执行回调函数 127 if(key_event && pFkey_cb)//key_event不等于0 true 1 && pFkey_cb(pFkey_cb 肯定不等于0)所以结果为true 128 { 129 pFkey_cb(key_event);//true 传入的为1 130 } 131 //======================================================================= 132 } 133 //************************************************************************ 134 // fn : HAL_GPIO_EXTI_Callback 135 // 136 // brief : 按键中断回调函数 137 // 138 // param : GPIO_Pin-> 引脚编号 139 // 140 // return : none 141 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) 142 { 143 //引脚有中断发生,要进行消抖,以确定是否为有效操作 144 if(GPIO_Pin == KEY_INPUT_Pin) 145 { 146 key_check_press.key_event = KEY_INPUT;//把按键当前的状态传给按键触发事件变量 0x01 147 key_check_press.start_tick = HAL_GetTick(); 148 } 149 } 150 //================================================================================= 151 //================================================================================= 152 /* USER CODE END 2 */
GPIO.H文件的基本操作:
1 /* Define to prevent recursive inclusion -------------------------------------*/ 2 #ifndef __gpio_H 3 #define __gpio_H 4 #ifdef __cplusplus 5 extern "C" { 6 #endif 7 8 /* Includes ------------------------------------------------------------------*/ 9 #include "stm32l4xx_hal.h" 10 #include "main.h" 11 12 /* USER CODE BEGIN Includes */ 13 14 /* USER CODE END Includes */ 15 16 /* USER CODE BEGIN Private defines */ 17 //定义按键标识 18 #define KEY_INPUT 0x01 19 //定义按键回调函数指针 20 typedef void (*key_cb)(uint8_t pin); 21 /* USER CODE END Private defines */ 22 23 void MX_GPIO_Init(void); 24 25 /* USER CODE BEGIN Prototypes */ 26 //************************************** 27 // fn : KEY_RegisterCb 28 // 29 // brief : 注册按钮事件回调 30 // 31 // param : cb -> 处理按钮事件函数指针 32 // 33 // return : none 34 void KEY_RegisterCb(key_cb cb); 35 36 //************************************** 37 // fn : KEY_Poll 38 // 39 // brief : 轮询按钮事件 40 // 41 // param : none 42 // 43 // return : none 44 void KEY_Poll(void); 45 /* USER CODE END Prototypes */ 46 47 #ifdef __cplusplus 48 } 49 #endif 50 #endif /*__ pinoutConfig_H */
main.c基本语法:
1 /* Includes ------------------------------------------------------------------*/ 2 #include "main.h" 3 #include "stm32l4xx_hal.h" 4 #include "gpio.h" 5 6 /* USER CODE BEGIN Includes */ 7 8 /* USER CODE END Includes */ 9 10 /* Private variables ---------------------------------------------------------*/ 11 12 /* USER CODE BEGIN PV */ 13 /* Private variables ---------------------------------------------------------*/ 14 15 /* USER CODE END PV */ 16 17 /* Private function prototypes -----------------------------------------------*/ 18 void SystemClock_Config(void); 19 static void MX_NVIC_Init(void); 20 21 /* USER CODE BEGIN PFP */ 22 /* Private function prototypes -----------------------------------------------*/ 23 //====================================================================================== 24 //====================================================================================== 25 //****************************************************************************** 26 // fn : AppKey_cb 27 // 28 // brief : 处理按键事件 29 // 30 // param : key -> 按钮按下标识 31 // 32 // return : none 33 void AppKey_cb(uint8_t key); 34 //====================================================================================== 35 //====================================================================================== 36 /* USER CODE END PFP */ 37 38 /* USER CODE BEGIN 0 */ 39 40 /* USER CODE END 0 */ 41 42 /** 43 * @brief The application entry point. 44 * 45 * @retval None 46 */ 47 int main(void) 48 { 49 /* USER CODE BEGIN 1 */ 50 //注册按钮回调函数 51 //KEY_RegisterCb(AppKey_cb); 52 /* USER CODE END 1 */ 53 54 /* MCU Configuration----------------------------------------------------------*/ 55 56 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ 57 HAL_Init(); 58 59 /* USER CODE BEGIN Init */ 60 61 /* USER CODE END Init */ 62 63 /* Configure the system clock */ 64 SystemClock_Config(); 65 66 /* USER CODE BEGIN SysInit */ 67 68 /* USER CODE END SysInit */ 69 70 /* Initialize all configured peripherals */ 71 MX_GPIO_Init(); 72 73 /* Initialize interrupts */ 74 MX_NVIC_Init(); 75 /* USER CODE BEGIN 2 */ 76 //=========================================================================== 77 //=========================================================================== 78 //注册按钮回调函数 79 KEY_RegisterCb(AppKey_cb);//注册按钮回调函数 80 //=========================================================================== 81 //=========================================================================== 82 /* USER CODE END 2 */ 83 84 /* Infinite loop */ 85 /* USER CODE BEGIN WHILE */ 86 while (1) 87 { 88 89 /* USER CODE END WHILE */ 90 91 /* USER CODE BEGIN 3 */ 92 //程序需要写在BEGIN 和 END之间否则下一次导入会覆盖之前的操作 93 {//这个只能实现点亮灯的操作 94 //HAL_GPIO_WritePin(LED_LD2_GPIO_Port, LED_LD2_Pin, GPIO_PIN_SET);//OPEN THE LED 95 //HAL_Delay(1000);//自带延迟函数//延时1000ms 96 //HAL_GPIO_WritePin(LED_LD2_GPIO_Port, LED_LD2_Pin, GPIO_PIN_RESET);//CLOSED THE LED 97 } 98 {//实现某个引脚电平的反转 99 //GPIO_Led_Toggle(); 100 //HAL_Delay(500);//延时500ms 101 } 102 KEY_Poll(); 103 } 104 /* USER CODE END 3 */ 105 106 } 107 108 /** 109 * @brief System Clock Configuration 110 * @retval None 111 */ 112 void SystemClock_Config(void) 113 { 114 115 RCC_OscInitTypeDef RCC_OscInitStruct; 116 RCC_ClkInitTypeDef RCC_ClkInitStruct; 117 118 /**Initializes the CPU, AHB and APB busses clocks 119 */ 120 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI; 121 RCC_OscInitStruct.MSIState = RCC_MSI_ON; 122 RCC_OscInitStruct.MSICalibrationValue = 0; 123 RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_6; 124 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; 125 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) 126 { 127 _Error_Handler(__FILE__, __LINE__); 128 } 129 130 /**Initializes the CPU, AHB and APB busses clocks 131 */ 132 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK 133 |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; 134 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI; 135 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; 136 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; 137 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; 138 139 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) 140 { 141 _Error_Handler(__FILE__, __LINE__); 142 } 143 144 /**Configure the main internal regulator output voltage 145 */ 146 if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK) 147 { 148 _Error_Handler(__FILE__, __LINE__); 149 } 150 151 /**Configure the Systick interrupt time 152 */ 153 HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000); 154 155 /**Configure the Systick 156 */ 157 HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); 158 159 /* SysTick_IRQn interrupt configuration */ 160 HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0); 161 } 162 163 /** 164 * @brief NVIC Configuration. 165 * @retval None 166 */ 167 static void MX_NVIC_Init(void) 168 { 169 /* EXTI15_10_IRQn interrupt configuration */ 170 HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0, 0); 171 HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); 172 } 173 174 /* USER CODE BEGIN 4 */ 175 //****************************************************************************** 176 // fn : AppKey_cb 177 // 178 // brief : 处理按键事件 灯亮的时候灭 灭的时候灯亮 179 // 180 // param : key -> 按钮按下标识 181 // 182 // return : none 183 void AppKey_cb(uint8_t key) 184 { 185 if(key & KEY_INPUT) 186 { 187 {//实现某个引脚电平的反转 188 GPIO_Led_Toggle(); 189 HAL_Delay(500);//延时500ms 190 } 191 } 192 } 193 /* USER CODE END 4 */ 194 195 /** 196 * @brief This function is executed in case of error occurrence. 197 * @param file: The file name as string. 198 * @param line: The line in file as a number. 199 * @retval None 200 */ 201 void _Error_Handler(char *file, int line) 202 { 203 /* USER CODE BEGIN Error_Handler_Debug */ 204 /* User can add his own implementation to report the HAL error return state */ 205 while(1) 206 { 207 } 208 /* USER CODE END Error_Handler_Debug */ 209 } 210 211 #ifdef USE_FULL_ASSERT 212 /** 213 * @brief Reports the name of the source file and the source line number 214 * where the assert_param error has occurred. 215 * @param file: pointer to the source file name 216 * @param line: assert_param error line source number 217 * @retval None 218 */ 219 void assert_failed(uint8_t* file, uint32_t line) 220 { 221 /* USER CODE BEGIN 6 */ 222 /* User can add his own implementation to report the file name and line number, 223 tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ 224 /* USER CODE END 6 */ 225 } 226 #endif /* USE_FULL_ASSERT */
main.h:
1 /* Define to prevent recursive inclusion -------------------------------------*/ 2 #ifndef __MAIN_H__ 3 #define __MAIN_H__ 4 5 /* Includes ------------------------------------------------------------------*/ 6 7 /* USER CODE BEGIN Includes */ 8 9 /* USER CODE END Includes */ 10 11 /* Private define ------------------------------------------------------------*/ 12 13 #define KEY_INPUT_Pin GPIO_PIN_13 14 #define KEY_INPUT_GPIO_Port GPIOC 15 #define KEY_INPUT_EXTI_IRQn EXTI15_10_IRQn 16 #define LED_LD2_Pin GPIO_PIN_5 17 #define LED_LD2_GPIO_Port GPIOA 18 19 /* ########################## Assert Selection ############################## */ 20 /** 21 * @brief Uncomment the line below to expanse the "assert_param" macro in the 22 * HAL drivers code 23 */ 24 /* #define USE_FULL_ASSERT 1U */ 25 26 /* USER CODE BEGIN Private defines */ 27 28 /* USER CODE END Private defines */ 29 30 #ifdef __cplusplus 31 extern "C" { 32 #endif 33 void _Error_Handler(char *, int); 34 35 #define Error_Handler() _Error_Handler(__FILE__, __LINE__) 36 #ifdef __cplusplus 37 } 38 #endif 39 40 #endif /* __MAIN_H__ */