1.3 TIM 定时器中断 - 实验篇 2

在这个实验中, 我们使用按钮来控制灯的亮灭, 但是其中加入了消抖处理


什么是抖动?

如果你再之前的实验中仔细观察过了按下按钮的情况, 你就会发现, 有的时候按下按钮灯闪动了几下 – 跳变 这个就是我们说的按键抖动, 通常是因为人按下的时候的各种不精确导致按钮在极短时间内两端的电平多次变化

消除这种不确定的变化, 让我们的灯开关更加完美的方式就是消抖

消抖的通常手段

用来消抖的通常最好的方式就是等着, 也就是说, 你每次按下按钮, 按下和抬起两个过程是发生抖动的时候, 一般我们关注的是按下的过程

在你按下按钮的这个过程中抖动发生, 我们不想要这个抖动, 想要直接跳过这个抖动的过程去获得之后按钮被按下这个确定的状态

于是我们用上的手段是等待 , 等待这个抖动的过程过去, 这个抖动的时间通常很短, 凭借经验值我们使用 10ms 的等待让抖动消失


看到这里, 我默认你们前面的实验都好好自己去做了, 在这里我不会再费口舌去解释一大堆东西, 直接开始配置吧!

Step 1 - CubeMX 配置

Pasted image 20260623025810

Pasted image 20260623025950

Step 2 - 写代码

  • 编写回调函数

在这里我们有两个中断回调等待我们去写一个是定时器的, 一个是按钮的 GPIO 的中断Pasted image 20260623030555

我们的思路是, 当按钮被按下(按钮的中断被触发), 使能 TIM3 让它进行等待, 到达设定的 10ms, TIM3 的中断被触发, 这个时候等待完成抖动的过程已经过了, 我们再次检查按钮是否被按下, 如果还是处在被按下的状态就反转电平, 如果按钮没有被按下, 说明这是一次抖动我们之间忽略

也就是说: GPIO的EXIT: 这里写的是使能定时器 TIM3 TIM3的中断: 这里写的是反转电平

所以代码最终是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* USER CODE BEGIN 1 */  
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == Button_Pin)
{
HAL_TIM_Base_Start_IT(&htim3);
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim == &htim3)
{
HAL_TIM_Base_Stop_IT(&htim3); // 把定时器关闭 -- line1 if (HAL_GPIO_ReadPin(Button_GPIO_Port, Button_Pin) == GPIO_PIN_RESET) // 检查 10ms 之后按钮是否仍然在按下状态 -- line2 {
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
}
}
}
/* USER CODE END 1 */

现在我们来思考一下, 如果将 line2 注释掉, 效果会发生什么变化?

你先思考一下, 好了之后看下面的答案

这一句是消抖的二次确认:

按键在按下的瞬间,机械触点会抖动几毫秒,期间电平来回跳,会触发好多次 EXTI 中断。我们的思路是:中断来了先不动作,而是等一个消抖窗口(定时器那 10~20ms),等抖动平息后,再读一次按键的真实状态

  • 如果这时读到的还是按下,说明是真的有人按了,才执行翻灯
  • 如果读到的是松开,说明刚才那阵中断只是抖动/干扰造成的假触发,就忽略

所以这句话是用来消抖的关键判断

能不能去掉? 你去掉就不能消抖了, 你的消抖不能没有尾部判断啊


小练习

把回答写在评论区

练习1:

接下来, 你把 line1 和 line2 一起注释掉, 看看发生什么现象, 想想为什么,(看看改动 TIM3 的 ARR 会发生什么?), 如果你发现你改 ARR 没有效果了你再回头去看看原理篇有关 ARR 的说明

练习2:

只把 line1 注释掉, 看看会发生什么?(如果改动 ARR 呢?)