STM32CubeIDE C++类封装按键实现长、短、双击

STM32 C++类封装按键实现长按、短按、双击

一、背景

目的

  1. 为了提升按键功能的可移植性、减少对底层的硬件的依赖、使其功能独立
  2. 减少每次开发频繁的重复工作
  3. 个人练习

注意❗❗

  1. 设置的回调函数内部避免使用耗时过长的功能或延时,否则影响按键响应
  2. 注意STM32工程的C和C++混合编程的问题
  3. 类的keyFMS()函数应该周期被调用,并且周期时长在创建对象时输入,此周期同时用于消抖
  4. 如果回调函数中任务简单可直接设置回调函数,否则可通过keyFMS()返回值判断按键事件
  5. 如果一直长按会周期执行回调函数而不是只执行一次

二、软硬件版本

  • STM32CubeIDE Version: 1.9.0

  • 正点原子战舰开发板STM32F103ZET6

  • C++的项目

三、代码

  • 采用状态机的思想,状态+事件,动作引起状态的切换
    • FS1表示按键释放状态及单击的判断
    • FS2用于去抖动
    • FS3用于长按及短按第一次的记录及双击的判断
    • FS4用于等待长按结束

在这里插入图片描述

//头文件Key.h
namespace keys {

enum key_event {  //表示按键的事件
	KEY_RELEASE,
	KEY_LONGPRESSED,
	KEY_DOUBLEPRESSED,
	KEY_SINGLEPRESSED,
};

class Key {

	#define KEY_ISPRESSED 1
	#define KEY_NOTPRESSED 0
	public :
		enum fms {  //状态机
			FS1_RELEASE,
			FS2_DEBOUNCE,
			FS3_PRESSED,
			FS4_LONG,
		};

		Key(uint32_t frequency, uint16_t long_interval, uint16_t double_interval, int (*getIo)());
		virtual ~Key() = default;

		key_event keyFMS(); //状态机切换,内部执行回调函数,返回按键事件


		void setSinglePressedFunc(void (*func)());
		void setDoublePressedFunc(void (*func)());
		void setLongPressedFunc(void (*func)());

	private:
		uint32_t  key_scan_frequency_;   //按键检测的频率,即调用keyFMS()的间隔ms
		bool key_status_;                //按键状态,按下或是抬起
		fms fms_state_;                  //状态机的状态
		uint16_t long_press_interval_;   //长按的时长ms
		uint16_t double_press_interval_; //双击的最大间隔ms
		int (*getIoState_)();            //获取IO状态
		void (*onSinglePressed)();       //事件发生时的调用函数的指针
		void (*onDoublePressed)();
		void (*onLongPressed)();
};

} /* namespace keys */
namespace keys {

Key::Key(uint32_t frequency, uint16_t long_interval, uint16_t double_interval, int (*getIo)())
		: key_scan_frequency_(frequency), long_press_interval_(long_interval), double_press_interval_(double_interval),
		  getIoState_(getIo){
	fms_state_ = FS1_RELEASE;
	onDoublePressed = nullptr;
	onLongPressed = nullptr;
	onSinglePressed = nullptr;
}

key_event Key::keyFMS() {
	key_event key_ret = KEY_RELEASE;      //函数返回值
	static uint64_t press_interval = 0;   //递增的
	static uint64_t press_cnt = 0;        //长按次数cnt
	static uint64_t single_pressed = 0;   //单次按下
	static bool double_waiting = false;   //等待双击

	key_status_ = !!getIoState_();        //获取按键状态
	press_interval++;

	switch(fms_state_) {             
	case FS1_RELEASE:
		if (double_waiting && (press_interval-single_pressed) * key_scan_frequency_ >= double_press_interval_) { //双击发生
			double_waiting = false;
			if (onSinglePressed != nullptr) onSinglePressed();
			key_ret = KEY_SINGLEPRESSED;
		} else {
			//do nothing
		}

		if (key_status_ == KEY_ISPRESSED) {
			fms_state_ = FS2_DEBOUNCE;
		} else {
			//do nothing
		}
		break;
	case FS2_DEBOUNCE:
		if (key_status_ == KEY_ISPRESSED) {
			fms_state_ = FS3_PRESSED;
		} else {
			fms_state_ = FS1_RELEASE; //消抖
		}
		break;

	case FS3_PRESSED:
		if (key_status_ == KEY_ISPRESSED) {
			press_cnt++;
			if (press_cnt >= long_press_interval_ / key_scan_frequency_) { //长按时间到
				press_cnt = 0;
				key_ret = KEY_LONGPRESSED;
				if (onLongPressed != nullptr) onLongPressed(); //长按回调函数
				fms_state_ = FS4_LONG;
			}
		} else {  
			single_pressed = press_interval; //记录单击的时间点
			if (!double_waiting) { 
				double_waiting = true;
			} else {
				if (onDoublePressed != nullptr) onDoublePressed();
				double_waiting = false;
				key_ret = KEY_DOUBLEPRESSED;
			}
			fms_state_ = FS1_RELEASE;
		}
		break;
	case FS4_LONG:
		if (key_status_ == KEY_ISPRESSED) {
			press_cnt++;
			if (press_cnt >= long_press_interval_ / key_scan_frequency_) {
				press_cnt = 0;
				key_ret = KEY_LONGPRESSED;
				if (onLongPressed != nullptr) onLongPressed(); //长按持续
			}
		} else {  //长按结束
			fms_state_ = FS1_RELEASE;
		}
		break;
	}
	return key_ret;
}


void Key::setSinglePressedFunc(void (*func)()) {  //设置单击发生时候的函数
	onSinglePressed = func;
}
void Key::setDoublePressedFunc(void (*func)()){
	onDoublePressed = func;
}
void Key::setLongPressedFunc(void (*func)()){
	onLongPressed = func;
}

} /* namespace keys */

四、总结

花了时间重新试着写了写这个按键类,其过程中遇到了许多的问题,如FS4的增加由于FS3长按如果直接返回FS1会导致短按发生。

另外是STM32CubeIDE对于C++头文件的支持真的是蛋疼,不知道为什么工程目录中includes上面的一系列头文件路径必须要自己手动加入到头文件路径中,否则找不到C++的头文件。

STM32CubeMX生成的代码最后还需要将一些需要C++的更改为.cpp,每次生成之前还要改回.c,否则生成同名的.c

最后建议老老实实写C代码吧

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值