- 目的
实现类似于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);
	}
}
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
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]);
    }
	
}
在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

 
                 
    