【嵌入式开发-按键扫描】
嵌入式开发-按键扫描
- ■ 1. 按键
- ■ 按键队列发送后在读取队列处理
- ■ 定时器30ms扫描一次,并通过MsgAdd(msg); 发送出去。
- ■ 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; }}
}
■ 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);
}