- 目的
实现类似于windows消息映射的方式编程思想,将离散的逻辑调用整合成消息映射的方式,基于消息触发调用
- 实现步骤
修改configPort.h中的内容
#ifndef __CONFIG_PORT__H
#define __CONFIG_PORT__H
#include "nrf_drv_gpiote.h"
#include "nrf_gpio.h"
#define __CONST
#define KEY_1_PIN_NO 7
#define KEY_2_PIN_NO 11
#define KEY_3_PIN_NO 14
#define KEY_4_PIN_NO 23
#define KEY_5_PIN_NO 24
#define KEY_6_PIN_NO 22
#define KEY_7_PIN_NO 13
#define KEY_8_PIN_NO 0xff
#define KEY_9_PIN_NO 0xff
#define KEY_10_PIN_NO 0xff
#define KEY_11_PIN_NO 0xff
#define KEY_12_PIN_NO 0xff
#define KEY_13_PIN_NO 0xff
#define KEY_14_PIN_NO 0xff
#define TOTAL_KEY_COUNT 7
#define BSP_GPIO_KEY {KEY_1_PIN_NO,KEY_2_PIN_NO,KEY_3_PIN_NO,KEY_4_PIN_NO,KEY_5_PIN_NO,KEY_6_PIN_NO,KEY_7_PIN_NO, \
KEY_8_PIN_NO,KEY_9_PIN_NO,KEY_10_PIN_NO,KEY_11_PIN_NO,KEY_12_PIN_NO,KEY_13_PIN_NO,KEY_14_PIN_NO}
#define BSP_key_init() \
do \
{ \
uint8_t list[] = BSP_GPIO_KEY; \
for(uint8_t i = 0; i < TOTAL_KEY_COUNT; i++) \
nrf_gpio_cfg_input(list[i], NRF_GPIO_PIN_PULLUP); \
}while(0)
#define BSP_key_DeInit() \
do \
{ \
uint8_t list[] = BSP_GPIO_KEY; \
for(uint8_t i = 0; i < TOTAL_KEY_COUNT; i++) \
{ \
nrf_gpio_pin_clear(list[i]); \
nrf_drv_gpiote_out_uninit(list[i]); \
} \
}while(0)
#define OSScanKey(temp) \
do \
{ \
uint8_t list[] = BSP_GPIO_KEY; \
uint16_t *p = &temp; \
*p = 0; \
for(uint8_t i = 0; i < TOTAL_KEY_COUNT; i++) \
{ \
if(nrf_gpio_pin_read(list[i])==0) \
*p |= 1msg = msg;
pMsg->extraData = extra;
//对尾指针后移
sqMsg.rear = (sqMsg.rear + 1) % QUEUESIZE;
//更新队列长度
sqMsg.count++;
return OK;
}
/*************************************************
Function: DeQueue
Description: 出队
Input: 队列指针 CircleQueue *queue
Output:
Return: 成功返回数据元素,失败程序退出
Others:
*************************************************/
QueueMsg *GetMessage(void)
{
QueueMsg *e=NULL;
//判断队列是否为空
if(sqMsg.count != 0)
{
//保存返回值
e = &sqMsg.data_elem[sqMsg.front];
//更新队头指针
sqMsg.front = (sqMsg.front + 1) % QUEUESIZE;
//更新队列长度
sqMsg.count--;
}
return e;
}
/*####################################################################*/
/***********************************************************************
设置按键声通用处理函数
************************************************************************/
void setKeyToneHandler(AFX_PMSG func)
{
keyToneFunc = func;
}
/*##################################################################*/
/*
void OnTime(uint8_t *count, AFX_VOID pfn)
{
if((*count > 0) && (--*count ==0))
{
pfn();
}
}*/
/***********************************************************************************
Routine Name: OnCmdMsg
Form: void OnCmdMsg(uint8_t event, void *lpEn)
Parameters: uint8_t event---- pass key msg value into it
void *lpEn----point to own its msg object
Return Value: void
Description: according to event value to search msg in lpEn object msg map,then call associated function
with it
*************************************************************************************/
void OnCmdMsg(uint16_t event,void *lpEn, void * extraData)
{
// you are check up lpEn value whether is null point before call this function operating
if(lpEn != NULL)
{
// the lpEn pointer be converted cmdTarget object pointer
const cmdTarget *pCmd =(const cmdTarget*)(*(void **)lpEn);
// get msg map entries address
const AFX_MSGMAP_ENTRY *ptr = pCmd->lpEn;
// declare and define union function pointer-set object
AFX_UNION_PFN AllFunc ;
uint16_t msg = event;
// if no msg map entries address,then no any operate and direct return it
if(pCmd == NULL )
return;
// below be named msg prefilter,if the return value of the virtual function of own object is FALSE ,will skip below msg
if((pCmd->preMsgHandler != NULL) && (pCmd->preMsgHandler(&msg)==FALSE))
{ //可以用过滤函数传递消息
return;
}
// search msg map in class object
while((ptr->message != 0)&&(msg != ptr->message))
{
ptr++;
}
// if msg handler function is null,then skip below msg
AllFunc.pfn = ptr->pfn;
if(AllFunc.pfn == NULL)
return;
// if extraData is NULL,then lpEn will be passed into function by caller
if(extraData == NULL)
{
extraData = lpEn;
}
// acording to nSign value, to call belong to its function protype
switch(ptr->nSign)
{
case AfxSig_vpv:
AllFunc.pvfn(extraData);
break;
case AfxSig_vpvuc:
AllFunc.pvmfn(extraData,msg);
break;
case AfxSig_uc:
AllFunc.pucfn(msg);
break;
case AfxSig_puc:
AllFunc.ppucfn(&msg);
break;
case AfxSig_pvv:
AllFunc.pvvfn((volatile uint8_t*)extraData);
break;
default:
AllFunc.pfn();
break;
}
/**************************************************************************/
// 按键处理函数,如果要重写按键函数,必须调用SetKeyToneHandler
if(keyToneFunc != NULL)
{
keyToneFunc(&msg);
}
}
}
/************************************************************************************************/
/* key value generator function */
/************************************************************************************************/
#define LONG_ON_DITHERING_COUNTER 60 //定义长按按下确认需要的时间,如果是每1/32S调用一次OSReadKey(),则64意味着这个时间为2S
static uint8_t KeyEventCnt;
static uint8_t KeySampleCnt;
uint16_t KeyBuffer;
/*############################################################################*/
/*# Prototype #*/
/*############################################################################*/
/*############################################################################*/
/*# API #*/
/*############################################################################*/
/**********************************************
Routine Name: OSReadKey
Form: uint8_t OSReadKey(void)
Parameters: void
Return Value: uint8_t
0 : invalid key msg
else return key msg
Description: get key msg
***********************************************/
static uint16_t OSReadKey(void)
{
uint16_t KeyTemp;
OSScanKey(KeyTemp);
switch( (uint16_t)KeyEventCnt )
{
case 0:
if(KeyTemp != 0)
{
KeySampleCnt=0;
KeyBuffer=KeyTemp;
KeyEventCnt=1;
}
break;
case 1:
if(KeyTemp == KeyBuffer )
{
KeyBuffer |= KEY_OFF;
return KeyTemp ;//sure that key on,return KeyBuffer
}
else
{
if(KeyTemp == (KeyBuffer & ~KEY_OFF))
{
if(++KeySampleCnt>LONG_ON_DITHERING_COUNTER)
{
KeySampleCnt=LONG_ON_DITHERING_COUNTER-1;
return KeyTemp | KEY_LONG_ON ;
}
}
else
{
if((KeySampleCnt < 4)&&(KeyBuffer & KEY_LONG_ON)==0)
{
KeyEventCnt = 0;
}
if(KeyTemp ==0)
{
KeyEventCnt = 0;
return KeyBuffer ; //sure that key on turn off,return KeyBuffer | 0x40
}
}
}
break;
}
return 0;
}
/***************************************************************************
Routine Name: DisLongKeyContinueResponse
Form: void DisLongKeyContinueResponse(void)
Parameters: void
Return Value: void
Description: if call this function in the key process function,so it keep
continue long key msg to generate
****************************************************************************/
void DisLongKeyContinueResponse(void)
{
KeyBuffer |= KEY_BLOCK_EVENT; //prevent long key to continue response
}
/*****************************************************************
Routine Name: keyProcess
Form: void keyProcess(void*self,uint8_t *flag)
Parameters: const void*self
point to own its msg object
Return Value: void
Description: key msg dispatch in 1/16s timer function
******************************************************************/
void keyProcess(void*self,uint8_t *flag)
{
uint16_t keyEvent= OSReadKey();
if(keyEvent != 0)
{
*flag = 1;
// OnCmdMsg(keyEvent,self,NULL);
PostMessage(keyEvent,self,NULL);
}
}
step 4:
keyScan.h中的内容如下:
#ifndef __KEYSCAN_H__
#define __KEYSCAN_H__
#include "baseCls.h"
#define BLUETOOTH_DATA 0X02
#define TICK_WAKEUP 0X01
#define UPDATE_DISP_ONE_TIME 1
#define KEY_1_LED 15
#define KEY_2_LED 18
#define KEY_3_LED 19
#define KEY_4_LED 20
#define KEY_5_LED 28
#define KEY_6_LED 30
#define KEY_7_LED 31
#define BACKLIGHT_LED 5
#define TOTAL_LED_COUNT 8
#define BSP_GPIO_LED {KEY_1_LED,KEY_2_LED,KEY_3_LED,KEY_4_LED,KEY_5_LED,KEY_6_LED,KEY_7_LED,BACKLIGHT_LED}
typedef struct tagBleData
{
uint8_t *rxBuf;
uint8_t *txBuf;
uint8_t *flag;
uint8_t rxLen;
uint8_t txLen;
}BLE_DATA;
extern uint8_t s_update_display;
extern uint16_t menu_item;
extern CONST MENU_KEY op_mode_table[];
extern CONST MENU_KEY tick_table;
//void init_key_message_Map(void);
void dispFunc(const MENU_KEY* self,uint8_t *flag);
void BSP_led_init(void);
void BSP_led_DeInit(void);
#endif
step 5:
keyScan.c中的内容如下:
#include "keyScan.h"
static const uint8_t list[] = BSP_GPIO_LED;
uint16_t menu_item;
uint16_t menu_sub_item; //设置项数量
//MENU_KEY Function_mode;
/*#############################################################################*/
/*********************************************************************************/
static void Key1Handler(void);
static void Key2Handler(void);
static void Key3Handler(void);
static void Key4Handler(void);
static void Key5Handler(void);
static void Key6Handler(void);
static void Key7Handler(void);
/*********************************************************************************/
static unsigned char preMessageHandler(uint16_t *msg);
/*#################################################*/
/* Key Message Map */
/*################################################*/
/*-----MENU mode Key Message Map ---*/
BEGIN_MESSAGE_MAP(Menu_Function_ModeFuncEn)
ON_NSM_MESSAGE(KEY_1_SHORT,Key1Handler)
ON_NSM_MESSAGE(KEY_2_SHORT,Key2Handler)
ON_NSM_MESSAGE(KEY_3_SHORT,Key3Handler)
ON_NSM_MESSAGE(KEY_4_SHORT,Key4Handler)
ON_NSM_MESSAGE(KEY_5_SHORT,Key5Handler)
ON_NSM_MESSAGE(KEY_6_SHORT,Key6Handler)
ON_NSM_MESSAGE(KEY_7_SHORT,Key7Handler)
END_MESSAGE_MAP(NULL) /* if END_MESSAGE_MAP param is NULL,then msg isn't match,no default handler */
//0- msg map entries name, 1-cmdTarget object name,2-pre-process msg func
// 如果参数3为空,则没有预处理消息函数
INIT_MSG_ENTRIES(Menu_Function_ModeFuncEn,Menu_Function_cmd_func,preMessageHandler)
/*************************************************************************************
正常模式下,短按键功能
***************************************************************************************/
static void Key1Handler(void)
{
nrf_gpio_pin_toggle(list[0]);
}
static void Key2Handler(void)
{
nrf_gpio_pin_toggle(list[1]);
}
static void Key3Handler(void)
{
nrf_gpio_pin_toggle(list[2]);
}
static void Key4Handler(void)
{
nrf_gpio_pin_toggle(list[3]);
}
static void Key5Handler(void)
{
nrf_gpio_pin_toggle(list[4]);
}
static void Key6Handler(void)
{
nrf_gpio_pin_toggle(list[5]);
}
static void Key7Handler(void)
{
nrf_gpio_pin_toggle(list[6]);
}
/**********************************************************************
消息预处理,如果返回非0,则消息继续传递,返回非0则消息被
过滤,不去映射表内查找消息函数,改变msg值将会改变其消息路径
**********************************************************************/
static unsigned char preMessageHandler(uint16_t *msg)
{
return TRUE; // 返回TRUE,不过滤按键消息
}
/*
显示处理
*/
void updateDisplay(uint16_t *p)
{
printf("updateDisplay\n");
}
/*#############################################################################*/
/*********************************************************************************/
static void TickWakeup(volatile uint8_t *flag);
static void BluetoothDataHandler(void *data);
/*********************************************************************************/
uint8_t s_update_display;
/*#################################################*/
/* Message Map */
/*################################################*/
/*-----MENU mode Key Message Map ---*/
BEGIN_MESSAGE_MAP(Tick_Function_ModeFuncEn)
ON_TICK_MESSAGE(TICK_WAKEUP,TickWakeup)
ON_SM_MESSAGE(BLUETOOTH_DATA,BluetoothDataHandler)
END_MESSAGE_MAP(NULL) /* if END_MESSAGE_MAP param is NULL,then msg isn't match,no default handler */
//0- msg map entries name, 1-cmdTarget object name,2-pre-process msg func
// 如果参数3为空,则没有预处理消息函数
INIT_MSG_ENTRIES(Tick_Function_ModeFuncEn,Tick_Function_cmd_func,NULL)
/*蓝牙数据处理*/
static void BluetoothDataHandler(void *data)
{
//BLE_DATA *p = (BLE_DATA*)data;
}
/*32HZ,2HZ,1HZ处理*/
static void TickWakeup(volatile uint8_t *flag)
{
if((*flag & WAKEUP_FOR_32HZ) == 0) // 32Hz event handler
return;
*flag &= CLEAR_WAKEUP_FOR_32HZ;
keyProcess((void*)&op_mode_table[menu_item],&s_update_display); //按键消息投递
if((*flag & TICK_FOR_2HZ)==0) // 2Hz handler
return;
*flag &= CLEAR_TICK_FOR_2HZ;
s_update_display = 1;
if((*flag & TICK_FOR_1HZ) == 0) // 1Hz handler
return;
*flag &= CLEAR_TICK_FOR_1HZ;
}
/**********************************************************************************/
/*################################################################################*/
CONST MENU_KEY tick_table = {&Tick_Function_cmd_func,NULL,NULL};
/*#################################################################*/
/*********************************************************************
功能映射表,每一项对应一个功能对象,每一项有三个值,分别表示为
按键映射变量,显示操作,附加变量
**********************************************************************/
CONST MENU_KEY op_mode_table[]={
{&Menu_Function_cmd_func,updateDisplay,&menu_sub_item}, //正常模式
};
/*********************************************************************************
内存不使用const空间时,则动态初时化消息映射表变量
**********************************************************************************/
/*
void init_key_message_Map(void)
{
// GET_MESSAGE_MAP(Function_mode, Menu_Function_cmd_func);
}
*/
/*#######################################################################*/
/*************************************************************************
功能模式多态显示
*****************************************************************************/
void dispFunc(const MENU_KEY* self,uint8_t *flag)
{
if(*flag)
{
const MENU_KEY *cp = self;
*flag = 0;
if((cp!= 0) && (cp->dispFunc != 0))
{
cp->dispFunc(cp->ptrFlag);
}
}
}
void BSP_led_init(void)
{
for(uint8_t i = 0; i < TOTAL_LED_COUNT; ++i)
{
nrf_gpio_cfg_output(list[i]);
nrf_gpio_pin_clear(list[i]);
}
nrf_gpio_pin_set(5);
}
/**------------------------------------------------------------------------------------------------
* @brief : This is output deinit function
* @param : None
* @retval : None
*----------------------------------------------------------------------------------------------*/
void BSP_led_DeInit(void)
{
for(uint8_t i = 0; i < TOTAL_LED_COUNT; ++i)
{
nrf_gpio_pin_clear(list[i]);
nrf_drv_gpiote_out_uninit(list[i]);
}
}
step 6:
在32HZ事件回调函数中投递定时器消息
/**@brief Function for handling the Battery measurement timer timeout. * * @details This function will be called each time the battery level measurement timer expires. * * @param[in] p_context Pointer used for passing some arbitrary information (context) from the * app_start_timer() call to the timeout handler. */ static void tick_32hz_timeout_handler(void * p_context) { UNUSED_PARAMETER(p_context); TICK_FOR_32HZ_INTERRUPT(tick_table); }
step 7:在蓝牙数据接收中投递蓝牙数据处理消息 static void MIL_BTRX_HanderCallback(ble_nus_evt_t * p_evt) { if(p_evt->params.rx_data.length < BT_DATA_MIN) return; if(p_evt->params.rx_data.p_data[0] != BT_DATA_STR) return; uint8_t len = p_evt->params.rx_data.length-1; uint8_t checksum = _Bluetooth_DataVerify(&p_evt->params.rx_data.p_data[0],len); if(checksum != p_evt->params.rx_data.p_data[len]) return; for(checksum = 0;checksum < len;checksum++) { ReceBuf[checksum] = p_evt->params.rx_data.p_data[checksum]; } s_ble_data.rxBuf = &ReceBuf[2]; //从协议数据开始 s_ble_data.txBuf = SendBuf; s_ble_data.rxLen = p_evt->params.rx_data.length-3; s_ble_data.flag = &s_update_display;
PostMessage(BLUETOOTH_DATA, (void*)&tick_table,&s_ble_data);
}
step 8:在main函数中取消息,翻译消息,执行消息 /**@brief Application main function. */ int main(void) {
// Initialize. uart_init(); timers_init(); power_management_init(); // Start execution. printf("BLE UART central example started.\r\n"); // NRF_LOG_INFO("BLE UART central example started."); ble_stack_init();
gap_params_init(); gatt_init(); conn_params_init(); services_init(); advertising_init(); advertising_start(); application_timers_start(); BSP_key_init(); BSP_led_init();
// Enter main loop. for (;;) { if((lpGetMsg = GetMessage()) != NULL) //取出消息 { OnCmdMsg(lpGetMsg->msg,lpGetMsg->lpMsgEn,lpGetMsg->extraData); //翻译执行消息 dispFunc(&op_mode_table[menu_item],&s_update_display); //功能显示处理 } else { nrf_pwr_mgmt_run(); // enter idle mode } } }
- baseCls.h文件分析
- 不同类型消息的函数指针
typedef void (*AFX_PVVMSG)(volatile uint8_t *flag); typedef void (*AFX_PMSG)(uint16_t * event); typedef void (*AFX_VOID)(void); typedef void (*AFX_PFN)( void *self); typedef void (*AFX_PVMSG)( void*self,uint16_t event); typedef void (*AFX_MSG)(uint16_t event); typedef uint8_t (*PreFilterMsg)(uint16_t *msg); 2.不同类型的标志,转换成对应函数指针来实现不同的函数类型调用
enum AfxSig { AfxSig_vv=0, // void (void) AfxSig_vpv, // void (void*) AfxSig_vpvuc, // void (void*,uint16_t) AfxSig_uc, //void (uint16_t) AfxSig_puc, // void (uint16_t *) AfxSig_pvv //带volatile uint8_t*参数 }; 3.共用体数据类型,根据不同的AfxSig标记转成对应的函数指针去调用函数 typedef union { AFX_VOID pfn; //不带参数 AFX_PFN pvfn; //带void*参数 AFX_PVMSG pvmfn; //带void*,unsigned short参数 AFX_MSG pucfn; //带unsigned short参数 AFX_PMSG ppucfn; // 带unsigned short*参数 AFX_PVVMSG pvvfn; //带volatile uint8_t*参数 }AFX_UNION_PFN;
4.消息入口数据类型 typedef struct tagMSGMAP_ENTRY { uint16_t message; uint8_t nSign; AFX_VOID pfn; }AFX_MSGMAP_ENTRY;
5.命令数据类型
typedef struct tagCmdTarget { const AFX_MSGMAP_ENTRY *lpEn; const PreFilterMsg preMsgHandler; }cmdTarget;
6.消息队列数据类型 typedef struct tagSqMsg { void *lpMsgEn; void *extraData; uint16_t msg; }QueueMsg;
7.循环队列存储结构 typedef struct _CircleQueue { QueueMsg data_elem[QUEUESIZE];//存储队列元素 uint8_t front;//队列头指针 uint8_t rear;//队列尾指针 int count;//队列元素个数 }CircleQueue;
8.初时化消息对象宏
#define initMsg(theClass)\ static CONST cmdTarget myTest = {&theClass[0]};
9.消息映射宏
#define ON_EVENT_MESSAGE(message,pfn) {message,AfxSig_uc,(AFX_VOID)(AFX_MSG)&pfn}, #define ON_NSM_MESSAGE(message,pfn) {message,AfxSig_vv,pfn}, #define ON_SM_MESSAGE(message,pfn) {message,AfxSig_vpv,(AFX_VOID)(AFX_PFN)&pfn}, #define ON_NOTIFY_MESSAGE(message,pfn) {message,AfxSig_vpvuc,(AFX_VOID)(AFX_PVMSG)&pfn}, #define ON_KEY_LONG_EVENT_MESSAGE(message,pfn) {message,AfxSig_puc,(AFX_VOID)(AFX_PMSG)&pfn}, #define ON_TICK_MESSAGE(message,pfn) {message,AfxSig_pvv,(AFX_VOID)(AFX_PVVMSG)&pfn},
10.声明命令对象宏 #define DECLARE_MESSAGE_MAP()\ const cmdTarget* lpEn;
11.声明消息对象映射宏,在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间添加要映射的消息映射宏 #define BEGIN_MESSAGE_MAP(theClass)\ static CONST AFX_MSGMAP_ENTRY theClass[]={\ #define END_MESSAGE_MAP(lpDef) \ {0,AfxSig_vpv,(AFX_VOID)lpDef } \ };\
12.初时化消息对象实例 #define INIT_MSG_ENTRIES(theClass,cmdTargetName,preMsgHandler)\ CONST cmdTarget cmdTargetName = {theClass,preMsgHandler};
13.获取命令对象实例
#define GET_MESSAGE_MAP(theClass, cmdTargetName) \ theClass.lpEn = &cmdTargetName; 14.声明命令对象 #define DECLARE_cmdTarget(theClass) \ extern CONST cmdTarget theClass;
15.接口函数及变量声明 extern QueueMsg *lpGetMsg; //void OnTime(uint8_t *count, AFX_VOID pfn); void OnCmdMsg(uint16_t event, void *lpEn, void * extraData); QueueMsg *GetMessage(void) ; State PostMessage(uint16_t msg, void *self, void *extra); void setKeyToneHandler(AFX_PMSG func);
void DisLongKeyContinueResponse(void); void keyProcess(void*self,uint8_t *flag);
- baseCls.c文件分析
1.消息队列变量定义 CircleQueue sqMsg={0};
QueueMsg *lpGetMsg; AFX_PMSG keyToneFunc = NULL;
2.往队列中添加消息对象
/************************************************* Function: EnQueue Description: 入队 Input: 队列指针 CircleQueue *queue 数据元素 ElemType e Output: Return: 成功返回OK,失败返回ERROR Others: *************************************************/ State PostMessage(uint16_t msg, void *self, void *extra) { QueueMsg *pMsg = &sqMsg.data_elem[sqMsg.rear]; //验证队列是否已满 if(sqMsg.count == QUEUESIZE) { return ERROR; } //入队 pMsg->lpMsgEn= self; pMsg->msg = msg; pMsg->extraData = extra; //对尾指针后移 sqMsg.rear = (sqMsg.rear + 1) % QUEUESIZE; //更新队列长度 sqMsg.count++; return OK; } 3.从队列里取出一个消息对象
/************************************************* Function: DeQueue Description: 出队 Input: 队列指针 CircleQueue *queue Output: Return: 成功返回数据元素,失败程序退出 Others: *************************************************/ QueueMsg *GetMessage(void) { QueueMsg *e=NULL; //判断队列是否为空 if(sqMsg.count != 0) { //保存返回值 e = &sqMsg.data_elem[sqMsg.front]; //更新队头指针 sqMsg.front = (sqMsg.front + 1) % QUEUESIZE; //更新队列长度 sqMsg.count--; } return e; } 4.设置按键音回调函数 /*####################################################################*/ /*********************************************************************** 设置按键声通用处理函数 ************************************************************************/ void setKeyToneHandler(AFX_PMSG func) { keyToneFunc = func; }
5.按键相关变量定义 /************************************************************************************************/ /* key value generator function */ /************************************************************************************************/
#define LONG_ON_DITHERING_COUNTER 60 //定义长按按下确认需要的时间,如果是每1/32S调用一次OSReadKey(),则64意味着这个时间为2S
static uint8_t KeyEventCnt; static uint8_t KeySampleCnt; uint16_t KeyBuffer;
6.得到一个键值 /********************************************** Routine Name: OSReadKey Form: uint8_t OSReadKey(void) Parameters: void Return Value: uint8_t 0 : invalid key msg else return key msg Description: get key msg ***********************************************/ static uint16_t OSReadKey(void) { uint16_t KeyTemp; OSScanKey(KeyTemp); switch( (uint16_t)KeyEventCnt ) { case 0: if(KeyTemp != 0) { KeySampleCnt=0; KeyBuffer=KeyTemp; KeyEventCnt=1; } break; case 1: if(KeyTemp == KeyBuffer ) { KeyBuffer |= KEY_OFF; return KeyTemp ;//sure that key on,return KeyBuffer } else { if(KeyTemp == (KeyBuffer & ~KEY_OFF)) { if(++KeySampleCnt>LONG_ON_DITHERING_COUNTER) { KeySampleCnt=LONG_ON_DITHERING_COUNTER-1; return KeyTemp | KEY_LONG_ON ; } } else { if((KeySampleCnt < 4)&&(KeyBuffer & KEY_LONG_ON)==0) { KeyEventCnt = 0; } if(KeyTemp ==0) { KeyEventCnt = 0; return KeyBuffer ; //sure that key on turn off,return KeyBuffer | 0x40 } } } break; } return 0; }
7.阻止长按键不停地触发函数 /*************************************************************************** Routine Name: DisLongKeyContinueResponse Form: void DisLongKeyContinueResponse(void) Parameters: void Return Value: void Description: if call this function in the key process function,so it keep continue long key msg to generate ****************************************************************************/
void DisLongKeyContinueResponse(void) { KeyBuffer |= KEY_BLOCK_EVENT; //prevent long key to continue response }
8.按键消息投入函数 /***************************************************************** Routine Name: keyProcess Form: void keyProcess(void*self,uint8_t *flag) Parameters: const void*self point to own its msg object Return Value: void Description: key msg dispatch in 1/16s timer function ******************************************************************/
void keyProcess(void*self,uint8_t *flag) { uint16_t keyEvent= OSReadKey(); if(keyEvent != 0) { *flag = 1; // OnCmdMsg(keyEvent,self,NULL); PostMessage(keyEvent,self,NULL); }
}
9.实现消息翻译的关键函数OnCmdMsg
/*********************************************************************************** Routine Name: OnCmdMsg Form: void OnCmdMsg(uint8_t event, void *lpEn) Parameters: uint8_t event---- pass key msg value into it void *lpEn----point to own its msg object Return Value: void Description: according to event value to search msg in lpEn object msg map,then call associated function with it *************************************************************************************/ void OnCmdMsg(uint16_t event,void *lpEn, void * extraData) { // you are check up lpEn value whether is null point before call this function operating if(lpEn != NULL) //对象表地址 { // the lpEn pointer be converted cmdTarget object pointer const cmdTarget *pCmd =(const cmdTarget*)(*(void **)lpEn); //得到命令对象指针 // get msg map entries address const AFX_MSGMAP_ENTRY *ptr = pCmd->lpEn; //得到消息入口地址 // declare and define union function pointer-set object AFX_UNION_PFN AllFunc ; //共用体变量 uint16_t msg = event; // 消息ID // if no msg map entries address,then no any operate and direct return it if(pCmd == NULL ) //命令对象为NULL,直接返回 return; // below be named msg prefilter,if the return value of the virtual function of own object is FALSE ,will skip below msg if((pCmd->preMsgHandler != NULL) && (pCmd->preMsgHandler(&msg)==FALSE)) { //可以用过滤函数传递消息,过滤函数返回false将执行return返回 return; } // search msg map in class object while((ptr->message != 0)&&(msg != ptr->message)) //根据消息ID,查找消息对象 { ptr++; } // if msg handler function is null,then skip below msg AllFunc.pfn = ptr->pfn; //共用体函数指针保存消息对象函数地址 if(AllFunc.pfn == NULL) //如果为NULL,没有消息处理函数 return; // if extraData is NULL,then lpEn will be passed into function by caller if(extraData == NULL) //如果extraData 为NULL, { extraData = lpEn; //将lpEn作附加数据参数 } // acording to nSign value, to call belong to its function protype switch(ptr->nSign) //根据nSign的值,共用体变量转换成对应的函数指针去调用函数 { case AfxSig_vpv: AllFunc.pvfn(extraData); break; case AfxSig_vpvuc: AllFunc.pvmfn(extraData,msg); break; case AfxSig_uc: AllFunc.pucfn(msg); break; case AfxSig_puc: AllFunc.ppucfn(&msg); break; case AfxSig_pvv: AllFunc.pvvfn((volatile uint8_t*)extraData); break; default: AllFunc.pfn(); break; } /**************************************************************************/ // 按键处理函数,如果要重写按键函数,必须调用SetKeyToneHandler if(keyToneFunc != NULL) { keyToneFunc(&msg); //按键音回调处理 } } }
- keyScan.h文件分析
1.事件宏ID定义
#define BLUETOOTH_DATA 0X02 #define TICK_WAKEUP 0X01 #define UPDATE_DISP_ONE_TIME 1
2.LED IO引脚定义 #define KEY_1_LED 15 #define KEY_2_LED 18 #define KEY_3_LED 19 #define KEY_4_LED 20 #define KEY_5_LED 28 #define KEY_6_LED 30 #define KEY_7_LED 31 #define BACKLIGHT_LED 5
#define TOTAL_LED_COUNT 8
#define BSP_GPIO_LED {KEY_1_LED,KEY_2_LED,KEY_3_LED,KEY_4_LED,KEY_5_LED,KEY_6_LED,KEY_7_LED,BACKLIGHT_LED}
3.蓝牙数据结构 typedef struct tagBleData { uint8_t *rxBuf; uint8_t *txBuf; uint8_t *flag; uint8_t rxLen; uint8_t txLen; }BLE_DATA;
4.接口函数及变量声明
extern uint8_t s_update_display; extern uint16_t menu_item;
extern CONST MENU_KEY op_mode_table[]; extern CONST MENU_KEY tick_table; //void init_key_message_Map(void);
void dispFunc(const MENU_KEY* self,uint8_t *flag);
void BSP_led_init(void); void BSP_led_DeInit(void);
- keyScan.c文件分析
1.变量定义
static const uint8_t list[] = BSP_GPIO_LED;
uint16_t menu_item; uint16_t menu_sub_item; //设置项数量
2.按键函数声明,消息映射时会用到
static void Key1Handler(void); static void Key2Handler(void); static void Key3Handler(void); static void Key4Handler(void); static void Key5Handler(void); static void Key6Handler(void); static void Key7Handler(void);
3.按键消息过滤函数声明,消息映射时会用到
static unsigned char preMessageHandler(uint16_t *msg);
4.使用BEGIN_MESSAGE_MAP和END_MESSAGE_MAP宏实现消息映射,此只映射了短按键键值,其它键值也可以映射
/*#################################################*/ /* Key Message Map */ /*################################################*/
/*-----MENU mode Key Message Map ---*/BEGIN_MESSAGE_MAP(Menu_Function_ModeFuncEn) ON_NSM_MESSAGE(KEY_1_SHORT,Key1Handler) ON_NSM_MESSAGE(KEY_2_SHORT,Key2Handler) ON_NSM_MESSAGE(KEY_3_SHORT,Key3Handler) ON_NSM_MESSAGE(KEY_4_SHORT,Key4Handler) ON_NSM_MESSAGE(KEY_5_SHORT,Key5Handler) ON_NSM_MESSAGE(KEY_6_SHORT,Key6Handler) ON_NSM_MESSAGE(KEY_7_SHORT,Key7Handler) END_MESSAGE_MAP(NULL) /* if END_MESSAGE_MAP param is NULL,then msg isn't match,no default handler */
5.使用INIT_MSG_ENTRIES宏初时化消息对象
//0- msg map entries name, 1-cmdTarget object name,2-pre-process msg func // 如果参数3为空,则没有预处理消息函数INIT_MSG_ENTRIES(Menu_Function_ModeFuncEn,Menu_Function_cmd_func,preMessageHandler)
6.短按键消息处理
static void Key1Handler(void) { nrf_gpio_pin_toggle(list[0]);
} static void Key2Handler(void) { nrf_gpio_pin_toggle(list[1]); } static void Key3Handler(void) { nrf_gpio_pin_toggle(list[2]); } static void Key4Handler(void) { nrf_gpio_pin_toggle(list[3]); } static void Key5Handler(void) { nrf_gpio_pin_toggle(list[4]); } static void Key6Handler(void) { nrf_gpio_pin_toggle(list[5]);
} static void Key7Handler(void) { nrf_gpio_pin_toggle(list[6]); }
7.消息预处理函数,如果返回false,将会屏蔽上面的按键处理函数的调用,否则可以调用 /********************************************************************** 消息预处理,如果返回非0,则消息继续传递,返回非0则消息被 过滤,不去映射表内查找消息函数,改变msg值将会改变其消息路径 **********************************************************************/ static unsigned char preMessageHandler(uint16_t *msg) { return TRUE; // 返回TRUE,不过滤按键消息 }
8.显示处理函数 /* 显示处理 */ void updateDisplay(uint16_t *p) { printf("updateDisplay\n"); }
9.按键功能表,此表只有一个模式,可以根据需要添加不同的模式,及显示函数,附加变量信息等
/*#################################################################*/ /********************************************************************* 功能映射表,每一项对应一个功能对象,每一项有三个值,分别表示为 按键映射变量,显示操作,附加变量 **********************************************************************/ CONST MENU_KEY op_mode_table[]={ {&Menu_Function_cmd_func,updateDisplay,&menu_sub_item}, //正常模式 0
};
10.变量定义
uint8_t s_update_display;
11.定时器函数和蓝牙数据处理函数声明,稍后消息映射会用到 static void TickWakeup(volatile uint8_t *flag); static void BluetoothDataHandler(void *data);
12.BEGIN_MESSAGE_MAP和END_MESSAGE_MAP实现消息映射 /*#################################################*/ /* Message Map */ /*################################################*/
/*-----MENU mode Key Message Map ---*/BEGIN_MESSAGE_MAP(Tick_Function_ModeFuncEn) ON_TICK_MESSAGE(TICK_WAKEUP,TickWakeup) ON_SM_MESSAGE(BLUETOOTH_DATA,BluetoothDataHandler)END_MESSAGE_MAP(NULL) /* if END_MESSAGE_MAP param is NULL,then msg isn't match,no default handler */
13.初时化消息对象 //0- msg map entries name, 1-cmdTarget object name,2-pre-process msg func // 如果参数3为空,则没有预处理消息函数INIT_MSG_ENTRIES(Tick_Function_ModeFuncEn,Tick_Function_cmd_func,NULL)
14.定时器及蓝牙数据功能表
CONST MENU_KEY tick_table = {&Tick_Function_cmd_func,NULL,NULL};
15.蓝牙数据处理函数
/*蓝牙数据处理*/ static void BluetoothDataHandler(void *data) { BLE_DATA *p = (BLE_DATA*)data; }
16.定时器函数处理
/*32HZ,2HZ,1HZ处理*/ static void TickWakeup(volatile uint8_t *flag) { if((*flag & WAKEUP_FOR_32HZ) == 0) // 32Hz event handler return; *flag &= CLEAR_WAKEUP_FOR_32HZ; keyProcess((void*)&op_mode_table[menu_item],&s_update_display); //按键消息投递
if((*flag & TICK_FOR_2HZ)==0) // 2Hz handler return; *flag &= CLEAR_TICK_FOR_2HZ; s_update_display = 1; if((*flag & TICK_FOR_1HZ) == 0) // 1Hz handler return; *flag &= CLEAR_TICK_FOR_1HZ; }
17.功能模式多态显示 /*#######################################################################*/
/************************************************************************* 功能模式多态显示 *****************************************************************************/ void dispFunc(const MENU_KEY* self,uint8_t *flag) { if(*flag) { const MENU_KEY *cp = self; *flag = 0; if((cp!= 0) && (cp->dispFunc != 0)) { cp->dispFunc(cp->ptrFlag); //回调到表中显示函数 } } }
18.LED IO初时化
void BSP_led_init(void) { for(uint8_t i = 0; i < TOTAL_LED_COUNT; ++i) { nrf_gpio_cfg_output(list[i]); nrf_gpio_pin_clear(list[i]); } nrf_gpio_pin_set(5); }
19. LED IO反初时化 void BSP_led_DeInit(void) { for(uint8_t i = 0; i < TOTAL_LED_COUNT; ++i) { nrf_gpio_pin_clear(list[i]); nrf_drv_gpiote_out_uninit(list[i]); } }
- maic.c文件部分代码分析
1.蓝牙数据接收后,使用PostMessage将BLUETOOTH_DATA消息ID,对象表tick_table,附加数据s_ble_data加到消息
队列中
PostMessage(BLUETOOTH_DATA, (void*)&tick_table,&s_ble_data);
2.在32HZ定时器回调函数中,调用TICK_FOR_32HZ_INTERRUPT宏,实现定时器消息回调
TICK_FOR_32HZ_INTERRUPT(tick_table);
宏体内容如下,最终使用PostMessage将TICK_WAKEUP消息ID,对象表tickTable,附加数据s_wakeup_flag添加到消
息队列中: #define TICK_FOR_32HZ_INTERRUPT(tickTable) \ do \ { \ static uint8_t cnt; \ static volatile uint8_t s_wakeup_flag; \ s_wakeup_flag |= WAKEUP_FOR_32HZ; \ if((++cnt & 0x07) == 0) s_wakeup_flag |= TICK_FOR_2HZ; \ if((cnt & 0x0f)==0) s_wakeup_flag |= TICK_FOR_1HZ; \ PostMessage(TICK_WAKEUP, (void*)&tickTable,(void*)&s_wakeup_flag); \ }while(0)
3.消息处理
if((lpGetMsg = GetMessage()) != NULL) //取出消息 { OnCmdMsg(lpGetMsg->msg,lpGetMsg->lpMsgEn,lpGetMsg->extraData); //翻译消息 dispFunc(&op_mode_table[menu_item],&s_update_display); //功能显示处理 } else { nrf_pwr_mgmt_run(); // enter idle mode }
如果lpGetMsg为NULL,则执行nrf_pwr_mgmt_run进入睡眠,如果不为NULL,执行OnCmdMsg函数将消息
翻译成对应的函数指针去回调消息对象中映射的函数,dispFunc多态调用表对象中函数
Demo下载地址:https://download.csdn.net/download/mygod2008ok/11180665