基于STM32CubeMX和Keil5-MDK的STM32F103C8点灯程序(纯C语言,无中断)
一、任务要求:
1.用按键实现按一次(按下松开)灯闪烁一次;
2.连续按两次(每次按下松开)按键之后灯的状态切换为隔两秒闪一下;
3.长按按键(不松开)灯的闪烁速度逐渐变快,按键松开后灯长亮;
二、最后实现效果:
能有效识别按键状态,并且在三种命令之间任意切换(由每两秒闪烁切换到单击闪 烁识别率不高 = = )
三、主要思路:
检测按键状态—》改变click的值—》分别实现对“长按、单击、双击”这三种状态的辨别。(个人认为的难点)
接下来定义一下单击、双击、长按
单击:在2秒内只点击一次按键(按下,松开)
双击:在2秒内点击两次按键
首先因为程序逐句执行,所以我们的按键检测都在循环中,让程序循环执行,检测按键是否被按下
click:最重要的一个参数,通过click的值识别此时应该执行什么部分,click初始值为0,按键每按下一次click就自增一次
在理想状态下(理想状态指只进行单击,双击,长按这三种操作)click只有三种状态分别为(0,1,2)
但是考虑误操作(比如在很短时间内点击三次,等等)为了提高程序稳定性,故将范围扩大为(0,奇数,非0偶数)
“本次实验代码采用第二种,但是为了方便解释,文本中将启用第一种进行描述”click的三值分别对应程序的三种状态
(检测状态此时灯常亮,执行单击内容闪烁一次,执行双击内容灯切换为每隔2秒闪一次)
四、引脚设置
五、代码部分
while(1){
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == 0)//HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0)此函数可以读取引脚PA0的状态若为低电平返回0,高电平返回1。
{
click++;
time_interval = 500;
while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == 0)
{
HAL_Delay(time_interval); //bug1(可能导致检测不到第二次点击)(但是此处延时不可或缺)
//如果缺少此延时函数,而可能我要实现双击,但是第一次点击之后由于没有延时我的函数会马上执行If,然而此时按键在短时间内肯定时没有抬起,所以if条件为真,执行if内部内容,识别失败。
//但是当我把延时提前后:此时按键按下进入while延时500毫秒,此时由于延时500毫秒,按键必定已经抬起,所以if为假if内部内容不执行,第一次点击识别成功
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == 0)
{
click--; //若程序执行到此处,说明此次按键为长按,由于此次按键导致的click自增应为无效 (不为单击或者双击中的任何一种)
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
HAL_Delay(time_interval);
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
//(此处本应为延时所处位置)但是提前更好
i -= 20;
if (i <= 0)
{
click=0; //重置click,防止无法退出两秒延时闪烁状态
break;
}
}
}
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, 0);
HAL_Delay(20);
while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == 0)
;
}
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == 0 || click % 2)//说明一下click%2的意义:
//为什么要加入click%2这个条件呢?因为当我按下按键的时候,程序跑到的位置是随机的可能在第一部分
//(即上方检测部分)检测到按键按下:如果不添加click%2,此时如果我想双击按键,并且第一次按键在
//上方被检测到,那么此时click==1,if条件为假,if不执行,click=1,此次点击被识别为单击,识别失败
//同样当我添加click%2条件时,同上方的情况下,此时click==1,if条件为真,if执行,由于程序运行速度快,
//所以会立即进入while进行的二次按键检测,如果此刻按下按键,click自增,此时click==2,识别成功
{
HAL_Delay(5);
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == 0)
{
while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == 0); //bug2(可能导致长按无法识别)
click++;
HAL_Delay(5);
j = 300000;
while (j--)
{
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == 0)
{
HAL_Delay(5);
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == 0)
{
while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == 0);
click++;
}
}
}
}
}
if (click % 2)
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
HAL_Delay(500);
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
HAL_Delay(500);
click = 0;//执行完相应程序之后click重置,为了跳出循环
}
if (!(click % 2) && click != 0)
//此函数通过死循环实现
//如果click为偶数执行if,进入死循环,并且此if是与外部检测按键状态的if并行的,
//意思就是我每次此if执行之后会回到开头检测按键状态,也就是说即使进入死循环,
//我还可以通过按键状态改变click的值,实现跳出死循环执行其他的命令,回到初始状态
{
HAL_Delay(2000); //bug3(延时时间可能导致程序无法检测按键状态)
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
}
}
Click增长的情况详解:即命令识别详解
将代码分为三个部分
1. while检测
2. if双击检测
3. 实现部分
(1).单击:
可能在if被检测到按键按下
可能在while检测到
-
双击:
while检测、检测到到第一次点击,此时 click=1,if为真,进行二次检测检测到第二次点击
If检测、检测到第一次点击,if为真,进行二次检测检测到二次点击
-
长按: 进入while检测被识别 Bug情况:在if检测被识别进入死循环语句 while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == 0);