当前位置: 首页 > news >正文

【嵌入式开发-按键扫描】

嵌入式开发-按键扫描

  • ■ 1. 按键
    • ■ 按键队列发送后在读取队列处理
    • ■ 定时器30ms扫描一次,并通过MsgAdd(msg); 发送出去。
    • ■ 按键msg (已经验证ok)
  • ■ 2. 触摸屏处理

■ 1. 按键

■ 按键队列发送后在读取队列处理

// key queue
#define KEY_QUEUE_MAX 5typedef enum
{KEY_TYPE_IR = 0,KEY_TYPE_ADC,KEY_TYPE_GPIO,
} KEY_TYPE_e;typedef struct
{KEY_MSG_s key_msg[KEY_QUEUE_MAX];int front;int rear;  //队尾指针int size;  //大小
} KEY_QUEUE_s;static KEY_QUEUE_s m_key_queue;static KEY_QUEUE_s m_key_queue;// 初始化队列
void key_queue_init(KEY_QUEUE_s *q)
{q->front = q->rear = 0;q->size = 0;		//队列当前长度为0
}// 判断队列是否为空
bool key_queue_empty(KEY_QUEUE_s *q)
{if (q->size==0)	//队空条件return true;elsereturn false;
}// 入队
bool key_queue_write(KEY_QUEUE_s *q, KEY_MSG_s *x)
{if (q->size == KEY_QUEUE_MAX)return false;		//队列满则报错memcpy(&(q->key_msg[q->rear]), x, sizeof(KEY_MSG_s));	//将x插入(拷贝)队尾q->rear = (q->rear + 1) % KEY_QUEUE_MAX;    //队尾指针后移q->size++;return true;
}// 出队
bool key_queue_read(KEY_QUEUE_s *q, KEY_MSG_s **x)
{if (q->size==0)return false;	//队空则报错*x = &(q->key_msg[q->front]);q->front = (q->front + 1) % KEY_QUEUE_MAX; //队头指针后移q->size--;return true;
}// 获取队头元素
bool key_queue_head_get(KEY_QUEUE_s *q, KEY_MSG_s **x)
{if (q->size==0)return false;	//队空则报错*x = &(q->key_msg[q->front]);return true;
}// 队列中元素的个数
int key_queue_num(KEY_QUEUE_s *q)
{return q->size;
}KEY_MSG_s *api_key_msg_get(void)
{KEY_MSG_s *key_msg = NULL;if (key_queue_read(&m_key_queue, &key_msg))return key_msg;elsereturn NULL;
}void api_key_queue_send(uint32_t act_key, int32_t press)
{KEY_MSG_s key_msg;memset(&key_msg, 0, sizeof(KEY_MSG_s));key_msg.key_type = KEY_TYPE_IR;key_msg.key_event.type = EV_KEY;key_msg.key_event.code = act_key;key_msg.key_event.value = press;  //1: pressed; 0: releasekey_queue_write(&m_key_queue,&key_msg);
}

■ 定时器30ms扫描一次,并通过MsgAdd(msg); 发送出去。

按键h文件定义

#define KEY_DETE_TIME       30
#define KEY_LONG_SENSE      1000
#define KEY_LONG_COUNT      (KEY_LONG_SENSE/KEY_DETE_TIME)typedef enum
{KEY_UP,KEY_DOWN,KEY_LONG_DOWN,
}KeyStatus;typedef enum
{BOL_KEY,  MUTE_KEY, START_KEY,POWER_OFF_KEY,KEY_END=POWER_OFF_KEY,
}KeyType;typedef struct
{uint16_t count;KeyStatus state;
}KeyArgus;typedef struct
{uint32_t value;uint32_t FilterValue;KeyArgus keys[KEY_END];
}KeyInfors;

按键长安短按处理

static KeyInfors key;
static void KeyOperate(void) 
{int8_t i;KeyStatus tmp_state;SysMessage msg = {CONTROL_MODULE, MODEL_MODULE};	//按键的信息传递给model模块,因为没有立即显示需要,不用给view模块if((key.FilterValue ^ key.value) == 0)	//按位运算,异或,两个数据全相同时,返回0for(i=0; i<KEY_END; i++){if(key.FilterValue & (1<<i))	//为1说明有按键按下{tmp_state = KEY_DOWN;if(key.keys[i].count++ >= KEY_LONG_COUNT)	//按下1s钟才算长按{tmp_state = KEY_LONG_DOWN;key.keys[i].count = KEY_LONG_COUNT;}msg.data[0] = i;//按键的值msg.type = (EventType)(KEY_UP_EVENT+tmp_state);//KEY_LONG_DOWN_EVENT或者KEY_DOWN_EVENTMsgAdd(msg);}else{tmp_state = KEY_UP;if(key.keys[i].state != tmp_state)	//按键刚刚释放{msg.data[0] = i;//按键的值msg.type = (EventType)(KEY_UP_EVENT+tmp_state);//KEY_UP_EVENTif(key.keys[i].count < KEY_LONG_COUNT)msg.data[1] = KEY_DOWN;elsemsg.data[1] = KEY_LONG_DOWN;//按键曾经的状态MsgAdd(msg);	//添加按键消息}key.keys[i].count = 0;}key.keys[i].state = tmp_state;}
}//每30ms扫描一次按键
static void KeyScan(int arg)
{key.value = 0;if(M_KEY_Bol_DATA)key.value |= 1<<BOL_KEY;elsekey.value &= ~(1<<BOL_KEY);if(M_KEY_Mute_DATA)key.value |= 1<<MUTE_KEY;elsekey.value &= ~(1<<MUTE_KEY);if(M_KEY_Start_DATA)key.value |= 1<<START_KEY;elsekey.value &= ~(1<<START_KEY);//说明:POWER_OFF_KEY不在这里扫描,其由M0发送的信息来判断KeyOperate();key.FilterValue = key.value;
}//按键初始化
void KeyInit(void)
{key.FilterValue = 0;TimerOnMsRepeatDelay(KeyTimer, KEY_DETE_TIME, KeyScan, 10);
}

获取队列按键处理

void GeneralKeyDeal(SysMessage msg)
{if(msg.type == KEY_UP_EVENT) //松开{switch(msg.data[0]){case MUTE_KEY:if(msg.data[1] == KEY_DOWN){	;}else if(msg.data[1] == KEY_LONG_DOWN){;}break;case START_KEY:break;			case BOL_KEY:break;			}		}else if(msg.type == KEY_LONG_DOWN_EVENT){switch(msg.data[0]){case MUTE_KEY:break;case START_KEY:break;			case BOL_KEY:break;			}}
}

■ 按键msg (已经验证ok)

typedef enum
{
KEY_UP, //
KEY_SHORT_DOWN, //短按下
KEY_SHORT_UP, //短按下弹起
KEY_LONG_DOWN, //长按下
KEY_LONG_UP, //长按下弹起
}KeyStatus;

短按下和短按弹起 各收到信息一次。
长按下和长按弹起 各收到信息一次。 可以定制长按下一直发送,长按弹发送一次数据。
通过扫描方式没有采用中断方式

==================短按=================================
[2025-05-15 14:16:18.085]# RECV ASCII>
START_KEY====1    //短按下
[2025-05-15 14:16:18.568]# RECV ASCII>
START_KEY====0   //短按下弹起==================长按=================================
[2025-05-15 14:16:19.965]# RECV ASCII>
START_KEY====1    //短按下
[2025-05-15 14:16:22.130]# RECV ASCII>
START_KEY++++1   //长按下
[2025-05-15 14:16:22.605]# RECV ASCII>
START_KEY++++0   //长按下弹起

main.c 处理函数

		KeyMessage keyMsg;if(NONE_ERR==MsgGet(&keyMsg)){	switch(keyMsg.data[0]){case START_KEY:if(keyMsg.data[1] == KEY_SHORT_DOWN){	printf("START_KEY====1\r\n");}else if(keyMsg.data[1] == KEY_SHORT_UP){printf("START_KEY====0\r\n");}else if(keyMsg.data[1] == KEY_LONG_DOWN){printf("START_KEY++++1\r\n");}else if(keyMsg.data[1] == KEY_LONG_UP){printf("START_KEY++++0\r\n");}break;		}	}

== keboard.c==

#include "keyboard.h"
#include "delay.h"
#include "systick.h"
#include "SystemMessage.h"static KeyInfors key;
static void KeyOperate(void) 
{KeyMessage msg = {0};for(int8_t i=0; i<KEY_END; i++){if(key.value & (1<<i))	//有按键按下{	key.keys[i].stateS = KEY_SHORT_DOWN;if(key.keys[i].count++ >= KEY_LONG_COUNT) //按下1s钟才算长按{key.keys[i].stateS = KEY_LONG_DOWN;key.keys[i].count = KEY_LONG_COUNT;}if(key.keys[i].stateS!=key.keys[i].stateE){   //防止重复发送。msg.data[0] = i;//按键的值msg.data[1] = key.keys[i].stateS;MsgAdd(msg);key.keys[i].stateE = key.keys[i].stateS;}}else{if(key.keys[i].stateS == KEY_SHORT_DOWN)	//短按键刚刚释放{msg.data[0] = i;	//按键的值msg.data[1] = KEY_SHORT_UP;MsgAdd(msg);	key.keys[i].stateS = KEY_UP;}if(key.keys[i].stateS == KEY_LONG_DOWN)	//长按键刚刚释放{msg.data[0] = i;	//按键的值msg.data[1] = KEY_LONG_UP;MsgAdd(msg);	key.keys[i].stateS = KEY_UP;}key.keys[i].count = 0;}key.keys[i].stateE = key.keys[i].stateS;}
}//每30ms扫描一次按键
void KeyScan(int arg)
{key.value = 0;if(!PC14_GETVALUE()){key.value |= 1<<START_KEY;       //1}else{key.value &= ~(1<<START_KEY);    //0}KeyOperate();
}//按键初始化
void KeyInit(void)
{TimerOnMsRepeatDelay(KeyTimer, KEY_DETE_TIME, KeyScan, 100);
}

== keboard.h==

#ifndef __KEYBOARD_H
#define __KEYBOARD_H		#include "cw32l083_gpio.h"
#include "typedef.h"#define KEY_DETE_TIME       30      //定时器间隔
#define KEY_LONG_SENSE      1000     //长按下时间
#define KEY_LONG_COUNT      (KEY_LONG_SENSE/KEY_DETE_TIME) //长按下次数typedef enum
{KEY_UP,      	  //KEY_SHORT_DOWN,   //短按下KEY_SHORT_UP,     //短按下弹起KEY_LONG_DOWN,    //长按下KEY_LONG_UP,      //长按下弹起
}KeyStatus;typedef enum
{
//	KEY_ST,START_KEY,KEY_END
}KeyType;typedef struct
{uint16_t  count;KeyStatus stateS; //这次数据KeyStatus stateE; //上次数据
}KeyArgus;typedef struct
{uint32_t value;   		//这次数据
//	uint32_t FilterValue;   //上次数据KeyArgus keys[KEY_END];
}KeyInfors;void KeyScan(int arg);
void KeyInit(void);#endif

KeyMessage.c

#ifndef __SYSTEM_MESSAGE_H__
#define __SYSTEM_MESSAGE_H__#include "cw32l083_gpio.h"
#include "typedef.h"#define MESSAGE_DATA_MAX_LEN    2
#define MESSAGE_QUEUE_LEN       80//data[0] = 按键的值
//data[1] = 按键的状态
typedef struct
{uint8_t data[MESSAGE_DATA_MAX_LEN];
}KeyMessage;//typedef void (*MsgCall)(KeyMessage msg);typedef struct
{int16_t head;			//待取出int16_t tail;			//待添加KeyMessage msg[MESSAGE_QUEUE_LEN];
}MessageQueue;void MsgQuInit(void);
RETURN_VALUE MsgGet(KeyMessage *msg);
RETURN_VALUE MsgAdd(KeyMessage msg);#endif

KeyMessage.h

#include "SystemMessage.h"static MessageQueue MsgQueue =
{0, 0,{0},
};void MsgQuInit(void)
{
//    MsgQueue.head = 0;
//    MsgQueue.tail = 0;
//    memset(MsgQueue.msg, 0, sizeof(MsgQueue.msg));
}RETURN_VALUE MsgGet(KeyMessage *msg)
{if(MsgQueue.head == MsgQueue.tail)return EMPTY_ERR;*msg = MsgQueue.msg[MsgQueue.head];MsgQueue.head = (MsgQueue.head + 1)%MESSAGE_QUEUE_LEN;return NONE_ERR;
}RETURN_VALUE MsgAdd(KeyMessage msg)
{if((MsgQueue.tail+1)%MESSAGE_QUEUE_LEN == MsgQueue.head)return FULL_ERR;MsgQueue.msg[MsgQueue.tail] = msg;MsgQueue.tail = (MsgQueue.tail + 1)%MESSAGE_QUEUE_LEN;return NONE_ERR;
}

■ 2. 触摸屏处理

触摸中断

void PORTA_IRQHandler(void)  
{if(TsArgu.port->ISFR & (1<<TsArgu.PinNum)){TimerOnMsOnceCallBack(TsTimer, 100, TsWork, 0);//延时去抖动NVIC_DisableIRQ(PORTA_IRQn);}TsArgu.port->ISFR |= PORT_ISFR_ISF_MASK;//每次中断仅可执行一次
}//有触摸屏按下时,每隔5ms执行一次
//#ifdef TSC2007
static void TsWork(int argu)
{int8_t pin_status;if(M_TSC_INT_DATA) pin_status = GPIO_HIGH_LEVEL;else pin_status = GPIO_LOW_LEVEL;if(pin_status== GPIO_LOW_LEVEL)//touch down{TsRead_XYvalues();	//获取坐标值和Z值TsDisADC_EnPENIRQ();//为下一次读取数据做准备TsFilter();			//做滤波处理,其平均值再赋给x_value和y_valueif(TsArgu.point.data.x_value && TsArgu.point.data.y_value){		if(++TsArgu.LongPressCount >= TS_LONG_PRESS_TIMES_COUNT+TS_LONG_PRESS_INTERVAL_COUNT)	//长按为至少1700ms{TsSendMsg(4, TsArgu.point.datas, TOUCH_LONG_DOWN_EVENT);//发送长按消息和坐标TsArgu.LongPressCount = TS_LONG_PRESS_TIMES_COUNT; }else if(TsArgu.LongPressCount < TS_LONG_PRESS_TIMES_COUNT){TsSendMsg(4, TsArgu.point.datas, TOUCH_DOWN_EVENT);		//发送按下消息和坐标}}TimerOnMsOnceCallBack(TsTimer, TOUCH_SCREEN_CHECK_TIME, TsWork, 0);//继续执行TsWork函数}else	//touch up{	if(TsArgu.point.data.x_value && TsArgu.point.data.y_value){		if(TsArgu.LongPressCount >= TS_LONG_PRESS_TIMES_COUNT){TsSendMsg(4, TsArgu.point.datas, TOUCH_LONG_DOWN_UP_EVENT);	//发送从长按释放的消息}else{TsSendMsg(4, TsArgu.point.datas, TOUCH_UP_EVENT);	//发送释放触摸屏的消息}}//清空x和y的坐标记录,清空滤波器TsArgu.point.data.x_value = 0;TsArgu.point.data.y_value = 0;memset(TsArgu.filter, 0 ,sizeof(TsArgu.filter));//打开中断,等待下一次正确处理TsArgu.LongPressCount = 0;NVIC_EnableIRQ(PORTA_IRQn);}
}//把触摸屏的触发事件添加到消息队列中去
static void TsSendMsg(int8_t len, uint8_t *data, EventType type)
{SysMessage msg = {CONTROL_MODULE, VIEW_MODULE};	//由控制模块传递到显示模块的消息msg.type = type;  memcpy((char *)msg.data, (char *)data, 4);MsgAdd(msg);	//添加到消息队列
}//数组内数据左移,刚到来的数据放在最后一个位置
static void filter(int8_t type, uint16_t *value)
{int8_t i;int32_t min, max, average, sum;sum = 0;max = min = *value;for(i=0; i<TOUCH_SCREEN_FILTER_NUM; i++){if(i < TOUCH_SCREEN_FILTER_NUM-1){if(TsArgu.filter[i+1].value[type] == 0)TsArgu.filter[i].value[type] = *value;	//若是数组为空,就填充刚进来的值else				//如果数组已有值,就让数据左移TsArgu.filter[i].value[type] = TsArgu.filter[i+1].value[type];}else {TsArgu.filter[i].value[type] = *value;}sum += TsArgu.filter[i].value[type];if(TsArgu.filter[i].value[type] > max)max = TsArgu.filter[i].value[type];if(TsArgu.filter[i].value[type] < min)min = TsArgu.filter[i].value[type];    }sum -= (max + min);	//去掉最大值和最小值average = sum / (TOUCH_SCREEN_FILTER_NUM-2);*value = average;	//求取平均值
}static void TsFilter(void)
{filter(0, &TsArgu.point.data.x_value);filter(1, &TsArgu.point.data.y_value);    
}

相关文章:

  • 某智能家电龙头,社招 校招全面应用 AI 面试的创新实践
  • 【Lua】java 调用redis执行 lua脚本
  • 【证书与信任机制​】自签名证书的风险与适用场景​​
  • 【Python 异常处理】
  • 梯度优化提示词:模型生成精准回答的秘密
  • 青蛙跳杯子--bfs最短路
  • 普通IT的股票交易成长史--20250514复盘
  • 基于GPUGEEK 平台进行深度学习
  • 高德地图在Vue3中的使用方法
  • vue3实现JSON格式化和JSONPath提取功能
  • 最大熵逆强化学习
  • Seata源码—2.seata-samples项目介绍
  • OrangePi Zero 3学习笔记(Android篇)9 - I2C和从设备
  • C++类和对象--高阶
  • 【C++】类与对象【下】
  • “智”造升级:金众诚如何赋能重型机械企业高效项目管理?
  • 【Deepseek 学cuda】CUTLASS: Fast Linear Algebra in CUDA C++
  • 【Python】普通方法、类方法和静态方法的区分
  • Vue百日学习计划Day1-3天详细计划-Gemini版
  • Socket API 核心函数详解
  • 河南:响鼓重锤对违规吃喝问题露头就打、反复敲打、人人喊打
  • 微软宣布将裁员3%
  • 智能手表眼镜等存泄密隐患,国安部提醒:严禁在涉密场所使用
  • 波兰关闭俄罗斯驻克拉科夫领事馆
  • 应急部:正在积极推动各地逐步科学建设改造应急避难场所
  • 上海建筑领域绿色发展2025年工作要点发布