外观
状态机判断
电路分析
简易万用表中仅有一个EC11旋转编码器作为功能按键,当EC11旋转编码器按下时,+5V电源输入连接到LDO得使能引脚上,使整个系统上电;
同时,按键按下时,+5V电源会使Q2-AO3400导通,此时PC2引脚被下拉到GND,通过检测PC2引脚低电平时间来判断按键按下的时间,当到达指定时间后,PC3引脚输出高电平,此时不管按键是否按下,单片机引脚都有输出高电平使LDO的使能引脚保持高电平,此时系统正常开机,从而实现长按开关机功能。
功能实现
创建工程
- 点击左上方文件、选择新建,选择新建MounRiver工程;
- 对工程进行修改,修改完成后点击“完成”按键,系统会自动生成好工程;
- 将之前的长按开关机代码包含进来修改;
- 对工程库路径进行包含;
代码实现
- 前言
在按键按下过程中,会经历以下几种变化,未按下-开始按下-抖动-稳定按下-松手-抖动-稳定松手;在之前的按键判断代码中,是通过延时函数来对按键进行消抖的,且按键按下后直接在while中进行判断,如果在一定时间内都是按下状态就退出,这种判断简单但是消耗的时间较多,在判断过程中是死等。
为此,这里使用按键状态机的形式来进行判断,将按键按下分为多个状态,开始状态、消抖状态、稳定状态、完成状态;且使用定时器来进行判断,如果时间没到则不进行判断,跳过执行其余代码;这样可以有效降低按键死等的时间。
- 代码编写
- 前面已经知道了按键有四种状态,分别是开始状态、消抖状态、稳定状态、完成状态,这里分别对这四种状态进行宏定义;
C
#define START_STATE 0x00 //开始状态
#define DITHERING_STATE 0x01 //消抖状态
#define STABILIZE_STATE 0x02 //稳定状态
#define CPLT_STATE 0x03 //完成状态
1
2
3
4
5
2
3
4
5
- 同时按键还有两种状态,长按与短按;
C
#define KEY_STORT_PRESS 0x04 //按键短按
#define KEY_LONG_PRESS 0x05 //按键长按
1
2
3
2
3
- 将之前的按键扫描函数全部删除掉,定义一个静态全局变量,用于保存此时按键的状态,接下来就是对这四种按键状态进行操作;
- 定义一个变量用于读取当前的按键值;默认按键未按下时是高电平;
- 在开始状态下,若检测到按键状态发生变化,使用一个变量记录变化,同时将状态设置为消抖状态;
- 机械按键从按下到稳定,肯定会有抖动,按照经验值,抖动时间一般为10~20ms,若我们直接在消抖状态中使用10ms延时死等过去,那与之前的代码就没有太大差异了,这里需要使用定时器中断,1ms中断一次,当按键进入除开始状态后,需进行定时器计数,如果10ms没到,则不会进行判断,转而执行其他操作;
- 若此时10ms时间到来,进入消抖判断,如果此时按键还是按下状态,证明按键真的按下,定义一个变量将按键记录为短按状态,此时按键进入稳定状态;
- 在稳定状态中,一直检测按键是否释放,如果超过指定时间还没有释放,则是长按,可以进行相关的开关机操作;
- 最后,按键进入完成状态,对各变量进行复位还原,若此时有返回值或输入值,将相关的记录赋值过去,将定时器关闭防止干扰正常运行,状态重新设置为开始状态,等待下一次判断;
- 后续就是在主函数中调用相关函数,并初始化定时器代码,注意,最好使用一个标志位来判断此时是否开机,若开机,则不去重复执行
C
#include "debug.h"
#include "powerswitch.h"
/* Global typedef */
/* Global define */
/* Global Variable */
/*********************************************************************
* @fn main
*
* @brief Main program.
*
* @return none
*/
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
SystemCoreClockUpdate();
Delay_Init();
Init_Power_Con_GPIO();
while(1)
{
key_task();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
C
/*
* 函数内容:按键检测任务
* 函数参数:无
* 返回值:无
*/
void key_task(void)
{
uint8_t keyvalue = KEY_NO_PRESS;
uint8_t ret = ERROR;
ret = Scanf_Power_Key(&keyvalue);
if(ret == SUCCESS){
if((keyvalue == KEY_STORT_PRESS) && (first_press_flag == 1)){
//短按且开机状态下进行处理
}
else if((keyvalue == KEY_LONG_PRESS) && (first_press_flag == 0)){
first_press_flag = 1; //长按松手后将开机标志位置1
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
TIP
长按开机,电源指示灯点亮。
源码&USB下载工具等软件,均在[gitee资料中](https://gitee.com/chen11232/simple-digital-multimeter)