■ 1. 按键
■ 按键队列发送后在读取队列处理
// key queue
#define KEY_QUEUE_MAX 5
typedef 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;
else
return 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;
else
return 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: release
key_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) //按位运算,异或,两个数据全相同时,返回0
for(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_EVENT
MsgAdd(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_EVENT
if(key.keys[i].count < KEY_LONG_COUNT)
msg.data[1] = KEY_DOWN;
else
msg.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;
else
key.value &= ~(1<<BOL_KEY);
if(M_KEY_Mute_DATA)
key.value |= 1<<MUTE_KEY;
else
key.value &= ~(1<<MUTE_KEY);
if(M_KEY_Start_DATA)
key.value |= 1<<START_KEY;
else
key.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_value
if(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);
}