一:什么是开关抖动?
当我们按下按钮或拨动开关或微动开关时,两个金属部件会接触以短路电源。但它们不会立即连接,而是金属部件在实际稳定连接之前连接和断开几次。
释放按钮时也会发生同样的事情。这会导致误触发或多次触发,例如多次按下按钮。这就像一个弹跳的球从高处落下,它一直在表面弹跳,直到它静止。
换句话说,我们可以说开关弹跳是任何开关的非理想行为,它会生成单个输入的多个转换。当我们处理电源电路时,开关弹跳不是主要问题,但当我们处理逻辑或数字电路时,它会引起问题。因此,为了消除电路中的抖动,使用了开关去抖动电路。
二:电路及波形
首先,我们将演示没有开关去抖动的电路
通过示波器抓取信号的波形如下:
您还可以在按下按钮时在示波器中看到波形。它显示在按钮切换期间发生了多少弹跳。
三:硬件去抖动
防止电路开关弹跳的常用方法有3种。
硬件去抖
RC 去抖
开关去抖IC
01
硬件电路去抖
在硬件去抖动技术中,我们使用 S-R 触发器来防止电路发生开关抖动。这是所有方法中最好的去抖动方法。
该电路由两个与非门(74HC00 IC)组成,形成一个 SR 触发器。正如您在电路图中看到的,只要拨动开关切换到 A 侧,输出逻辑就会变为“高”。在这里,我们使用示波器来检测弹跳。而且,正如您在下面给出的波形中看到的那样,逻辑正在以轻微的曲线移动而不是弹跳。电路中使用的电阻是上拉电阻。 每当开关在触点之间移动以产生反弹时,触发器都会保持输出,因为“0”是从与非门的输出反馈的。
02
R-C 去抖
R-C 仅由其名称定义,该电路使用 RC 网络来防止开关弹跳。电路中的电容器滤除开关信号的瞬间变化。当开关处于打开状态时,电容器两端的电压保持为零。最初,当开关打开时,电容器通过 R1 和 R2 电阻器充电。
当开关闭合时,电容器开始放电至零,因此反相施密特触发器输入端的电压为零,因此输出变为高电平。
在弹跳情况下,电容器停止 Vin 处的电压,直到它达到 Vcc 或接地。
为了提高 RC 去抖动的速度,我们可以连接一个二极管,如下图所示。因此,它减少了电容器的充电时间。
03
开关去抖IC
市场上有用于开关去抖动的 IC。一些去抖 IC 是 MAX6816、MC14490 和 LS118。
下面是使用MAX6818进行开关去抖的电路图。
所以在这里,我们学习了按钮如何产生开关反弹效应,以及如何通过使用硬件的方式来防止按键抖动。
四:软件消抖
我们都知道,并且也是我们使用最多的场合是通过软件实现按键消抖。
最简单的方式是增加延迟以消除软件去抖。添加延迟会强制控制器在特定时间段内停止,但在程序中添加延迟并不是一个好的选择,因为它会暂停程序并增加处理时间。最好的方法是在代码中使用中断来进行软件弹跳。
01
软件延时
sbit KEY = P1^3; ///按键读取函数 uint8_t GetKey(void) { if(KEY == 1) { DelayMs(20); //延时消抖 if(KEY == 1) { return 1; } else { return 0; } } else { return 0; } }上面是最简单的软件延时方法,也可以通过多个按键组合增加相关软件滤波的方式进行按键判断,其实原理相似。
中断消抖
首先初始化管脚,打开管脚的外部中断:
/*Configure GPIO pins : KEY_1_Pin KEY_2_Pin */ GPIO_InitStruct.Pin = KEY_1_Pin|KEY_2_Pin; GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); /* EXTI interrupt init*/ HAL_NVIC_SetPriority(EXTI15_10_IRQn, 5, 0); HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);初始化TIM1,打开其update中断:
static void MX_TIM1_Init(void) { htim1.Instance = TIM1; htim1.Init.Prescaler = 7200 - 1; // 72000000 / 7200 = 10000 hz 0.01ms htim1.Init.CounterMode = TIM_COUNTERMODE_UP; htim1.Init.Period = 200 - 1; // 200 * 0.01 = 20ms htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim1.Init.RepetitionCounter = 0; htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_Base_Init(&htim1) != HAL_OK) { _Error_Handler(__FILE__, __LINE__); }
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base) { if(htim_base->Instance==TIM1) { /* Peripheral clock enable */ __HAL_RCC_TIM1_CLK_ENABLE(); /* USER CODE BEGIN TIM1_MspInit 1 */ HAL_NVIC_SetPriority(TIM1_UP_IRQn,1,3); HAL_NVIC_EnableIRQ(TIM1_UP_IRQn); } }在stm32f1xx_hal_it.c中去注册中断回调函数(关键的步骤,需要在按键中断处理函数中打开定时器,开始计时):
void EXTI15_10_IRQHandler(void) // 按键的中断处理函数 { HAL_TIM_Base_Start_IT(&htim1); // 开启定时器1,开始计时 printf("key down "); __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_11); __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_12); }定时器的中断处理函数:
void TIM1_UP_IRQHandler(void) { HAL_TIM_IRQHandler(&htim1); //这个是所有定时器处理回调的入口,在这个函数里对应定时器多种中断情况的中断回调,需要找到update的回调函数 printf("TIM IRQ "); } void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) // 定时器update中断处理回调函数 { /* USER CODE BEGIN Callback 0 */ /* USER CODE END Callback 0 */ if (htim->Instance == TIM2) { HAL_IncTick(); } if (htim->Instance == TIM1) { // 在这里选择tim1 printf("TIM1 updata "); HAL_TIM_Base_Stop_IT(&htim1); // 关闭tim1 及清除中断 if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_11) ) //再次判断管脚的电平 { printf("KEY1 be pressed!!! "); } if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_12) )//再次判断管脚的电平 { printf("KEY2 be pressed!!! "); } } /* USER CODE BEGIN Callback 1 */ /* USER CODE END Callback 1 */ }
总结一下,实现用定时器中断来完成按键延时去抖的关键步骤:
1. 初始化GPIO脚,初始化TIM ,算好时间,填入分频值。
2. 打开GPIO中断,在中断处理函数中打开定时器,让其计数。
3. 定时器溢出中断函数中,再次判断按键电平值。关闭定时器,清除pending。
全部0条评论
快来发表一下你的评论吧 !