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

■ 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);    
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

光芒Shine

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值