自带消抖
KEY
按键输入:通过按键向芯片输入信号,一般的按键都会接上拉电阻接芯片,另一端接地,按下将芯片的引脚拉低。因此在初始化函数中芯片引脚的工作模式为上拉输入,引脚的初始电平为高电平。
1.KEY初始化
CT117E开发板KEY1-4引脚,分别为PA0、PB0、PB1、PB2。
KEY_Init()函数注意的问题:
按键输入时GPIO的工作模式为上拉输入。
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin : PA0 */
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pins : PB0 PB1 PB2 */
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
2.按键抖动产生的原因
通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动。
由于单片机的运行速度非常快,按下一次按键,可能在A点检测到一次低电平,在B点检测到一次高电平,在C点又检测到一次低电平。同时抖动是随机,不可测的。那么按下一次按键,抖动可能对会让单片机误以为按下多次按键。
设置系统定时器,50ms进行一次按键扫描,就尽可能的避免按键抖动对按键检测的影响。
3.减数函数
利用STM32自带的系统的定时器达到一个延时扫描的效果:代码如下uwTick是32自带的全局变量而我们自己定义的变量uwTick_set_Point目的是为了控制这个函数的运行速度 达到一个延时的效果。
if((uwTick - uwTick_set_Point)<100) return; //减速函数
uwTick_set_Point = uwTick;
程序代码如下:
bsp_key.c文件
#include "key\bsp_key.h" void KEY_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /*Configure GPIO pin : PA0 */ GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /*Configure GPIO pins : PB0 PB1 PB2 */ GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } unsigned char KEY_Scan(void) { unsigned char unKey_Val = 0; if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==GPIO_PIN_RESET) unKey_Val=1; if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)==GPIO_PIN_RESET) unKey_Val=2; if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)==GPIO_PIN_RESET) unKey_Val=3; if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_RESET) unKey_Val=4; return unKey_Val; }
bsp_key.h文件
#include "main.h" void KEY_Init(void); unsigned char KEY_Scan(void);
主函数的编写
#include "main.h" #include "led\bsp_led.h" #include "key\bsp_key.h" //变量创建区 __IO uint32_t uwTick_set_Point = 0; //控制Key_Proc的执行速度 //*按键扫描专用变量 unsigned char ucKey_Val,unKey_Down,unKey_Up,unKey_Old; //子函数声明区 void SystemClock_Config(void); void Key_Proc(void); //主函数 int main(void) { //内核时钟的初始化 HAL_Init(); SystemClock_Config(); //外设函数的初始化 LED_Init(); KEY_Init(); while (1) { Key_Proc(); } } void Key_Proc(void) { if((uwTick - uwTick_set_Point)<100) return; //减速函数 uwTick_set_Point = uwTick; //分成几种情况 //情况1:100ms两次扫描,按键得到的结果从0(都没按下)到B4按下,产生了下降沿。 //ucKey_Val = 4(0000 0100) //unKey_Down = 0000 0100 & ( 0000 0000 ^ 0000 0100) = 0000 0100 & 0000 0100 = 0000 0100 (4) //ucKey_Up = 1111 1011 & 0000 0100 = 0000 0000 //ucKey_Old = 4 //情况2:B4产生了下降沿后,按键一直按着 //ucKey_Val = 4(0000 0100) //unKey_Down = 0000 0100 & ( 0000 0100 ^ 0000 0100) = 0000 0100 & 0000 0000 = 0000 0000 (0) //ucKey_Up = 1111 1011 & 0000 0000 = 0000 0000 //ucKey_Old = 4 //情况3:B4按键一直按着随后弹起 //ucKey_Val = 0(0000 0000) //unKey_Down = 0000 0000 & ( 0000 0100 ^ 0000 0000) = 0000 0000 & 0000 0100 = 0000 0000 (0) //ucKey_Up = 1111 1111 & 0000 0100 = 0000 0100 (4) //ucKey_Old = 0 // uKey_Val,unKey_Down,unKey_Up,unKey_Old; ucKey_Val = KEY_Scan(); //4(0000 0100)按下去了 //0000 0100 unKey_Down = ucKey_Val & (unKey_Old ^ ucKey_Val); //0000 0100 & (0000 0000 ^ 0000 0100) = 0000 0100 & 0000 0100 = 0000 0100(4) //0000 1000 0000 0100 0000 0000 0000 0100 unKey_Up = ~ucKey_Val & (unKey_Old ^ ucKey_Val); //0000 1000 1111 1011 0000 0000 0000 0100 unKey_Old = ucKey_Val; //0000 0100 0000 0100 if(unKey_Down == 4) { LED_Disp(0X88); } if(unKey_Down == 3) { LED_Disp(0X00); } } void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Configure the main internal regulator output voltage */ HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1); /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV3; RCC_OscInitStruct.PLL.PLLN = 20; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } } /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */