IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> STM32 C++类封装按键实现长、短、双击 -> 正文阅读

[嵌入式]STM32 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代码吧

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-03-30 18:41:35  更:2022-03-30 18:45:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 6:54:02-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码