当前位置:首页 > 单片机 > 单片机
[导读] 从事单片机工作有几年了,一直想做一个系列总结,正好赶上今天下雨,俗话说:下雨天,宅家天。吃饱喝足,闲来无事,正好写篇博客,算是这个系列的开头第一篇,以后有时间就写点,这个“系列死了”也不奇怪。有不对

从事单片机工作有几年了,一直想做一个系列总结,正好赶上今天下雨,俗话说:下雨天,宅家天。吃饱喝足,闲来无事,正好写篇博客,算是这个系列的开头第一篇,以后有时间就写点,这个“系列死了”也不奇怪。有不对的地方或者您有什么好的建议请留言,思想是碰撞的火花,请大家畅所欲言。

按键是单片机系统中最常用的一个东东,简单人机交互界面基本都有按键存在。犹记得刚开始接触单片机时候,读取一个按键IO值,就当做键值来处理,发现一次按键经常识别为好几次,查阅资料才发现要“消抖”,于是加个delay_ms(5),连续读取几次,确定单次有效值。这就是后来不怎么跑多任务,不注重实时性时经常‘玩’的方法,如果你还在用,那么下面的文字对你有用,如果你已经知道了,那就请飞过吧,反正我也是随便写写,你也就随性看看。


下面就是一个很典型的例子:


boolkey_get_value(void)

{

boolbNewIoValue=0;

staticbools_bOldIoValue=0;

uint8_ti=0;

for(i=0;i<4;i++){

bNewIoValue=io_read();

if(s_bOldIoValue!=bNewIoValue){

i=0;

s_bOldIoValue=bNewIoValue;

continue;

}

delay_ms(5);

}

returns_bOldIoValue;

}


我们来看看代码“阻塞在什么地方”?有代码分析可知,主要在delay_ms(5)这里,在这里通过“软件延时”来达到消除抖动的目的。那么我们换一种思路,假如我们的task执行到delay_ms(5)这里,便退出(让出运行权),同时启动一个“计数时钟”,当到5MS时候,就再起运行起来,这样就变为了“非阻塞”。为了达到这个目的,不管如何实现都需要一个“硬时基”,用于异步计数。

说下思路:就是tick时钟到,调用一次io_read(),并做消抖,然后获取一次有效按键值。好,到这里我们消除了delay_ms(5),那么我们继续思考,对于按键的值的“获得”我们可以说有“实时性要求”,例如:我按下按键,你要能够及时获得这个动作。但是按键值获得之后,按键的状态判断(down、short_press、long_press、repeat_press、up)相对来说可以暂缓,还有按键状态获得之后,其对应的处理函数又可分为紧急和非紧急两类(紧急类的先不讨论),非紧急类的又可以通过key_message_queue暂存按键消息,然后慢慢去执行消化。数据流程图如图所示:





这么处理的原因,是把有实时要求的任务和没有要求的任务分开,运用“生产者-消费者”模型。



思路介绍完了,下面说说具体模块:


注意:

1、最多255个按键;

2、每个按键都有按下、短按、长按、双击、抬起五个状态;

下载地址:http://download.csdn.net/download/wuhenyouyuyouyu/9952815

(下载分数限制,为什么不能设置为0分了?)


一、配置


public.h
宏KEY_MODE_SCHEDULER_IS_USER_CALL


1:按键的调度函数由用户调度;
0:按键的调度函数由系统自动调度;


宏KEY_NUM 配置按键个数

app_cfg.h


模块配置
#define KEY_ELIMINATE_JITTER_NUM 5 //按键消抖次数


#define KEY_MODE_QUEUE_LONG 10 //key消息队列大小


#define KEY_NORMAL_VALUE 1 //定义按键常态值


#define KEY_SCAN_CYCLE 10 //键值扫描周期:单位ms


#define KEY_SHORT_PRESSED_TIMER 1000 //短按判定时间:单位ms


#define KEY_LONG_PRESSED_TIMER 3000 //长按判定时间:单位ms


#define KEY_REPEAT_TIMER 500 //连按判定周期:单位ms


#define KEY_GET_VALUE_IS_INLINE_FUNCTION 0 //为inline函数还是callback函数


按键消息配置:为0,则不发送;为1,则发送
#define KEY_MODE_IS_ENABLE_MESSAGE_DOWN 1
#define KEY_MODE_IS_ENABLE_MESSAGE_SHORT 1
#define KEY_MODE_IS_ENABLE_MESSAGE_LONG 1
#define KEY_MODE_IS_ENABLE_MESSAGE_REPEAT 1
#define KEY_MODE_IS_ENABLE_MESSAGE_UP 1


加锁控制


......



二、用户实现函数
用户需提供按键扫描函数,原型bool key_mode_get_key_value(uint8_t chKeyID)


bool 按键状态
chKeyID 按键的ID


宏KEY_GET_VALUE_IS_INLINE_FUNCTION控制函数为inline类型,还是callback。


三、使用


int main(void)
{
key_message_t tKeyMessage;
//初始化
//关中断

......

USER_KEY_MODE_INIT(NULL);//key_mode_get_key_value()为inline函数

......

//开中断

while(1){
#if(KEY_MODE_SCHEDULER_IS_USER_CALL)
USER_KEY_MODE_SCHEDULER();
#endif


if(key_mode_get_message(&tKeyMessage)){
key_function[tKeyMessage.chKeyID](tKeyMessage.chKeyMessage);//按键处理
}
}
}


//心跳定时器
void systick(void)
{
......
USER_KEY_MODE_SCAN();
......
}


四、补充


#define __C99__


#ifndef __C99__
#define SAFE_ATOM_CODE(__CODE) {
istate_t tState = GET_GLOBAL_INTERRUPT_STATE();
DISABLE_GLOBAL_INTERRUPT();
{
__CODE;
}
SET_GLOBAL_INTERRUPT_STATE(tState);
}
#else
#define SAFE_ATOM_CODE(...) {
istate_t tState = GET_GLOBAL_INTERRUPT_STATE();
DISABLE_GLOBAL_INTERRUPT();
{
__VA_ARGS__;
}
SET_GLOBAL_INTERRUPT_STATE(tState);
}


#endif


五、技术交流群号


如果有BUG请留言或者进入技术群:344659218


六、修改记录

2017.08.29

1、修复BUG,用下面的函数替换掉原来的


/*****************************************************************************

*Function:key_mode_scan_sub_state

*PreCondition:None

*Input:void

*Output:void

*SideEffects:None

*Overview:KEY模块扫描子函数

*Note:被key_mode_scheduler调用

*****************************************************************************/

staticfsm_rt_tkey_mode_judge_sub_state(key_mode_temp_t*ptKeyStruct)

{

key_mode_key_queue_t*ptThis=NULL;

#defineRESET_FMS_JUDGE_SBU(__ID){s_tKeyArray[__ID].tState=KEY_MODE_JUDGE_FSM_START;}

#defineKEY_MODE_POST_MESSAGE(__MESSAGE)do{

ptThis = malloc_key_queue();

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭