zl程序教程

您现在的位置是:首页 >  系统

当前栏目

一种类似于windows消息映射的方式编程思想分析(NRF52832平台上运行)

Windows消息平台编程映射 分析 方式 运行
2023-09-14 09:06:41 时间
  • 目的

实现类似于windows消息映射的方式编程思想,将离散的逻辑调用整合成消息映射的方式,基于消息触发调用

  • 实现步骤 

   step 1:

     修改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 |= 1<<i;                 \
        }                                                       \
    }while(0)


#endif

step 2:

      baseCls.h中的内容如下:

#ifndef __BASECLASS_H
#define __BASECLASS_H
#include "configPort.h"

#ifndef __CONST

#define CONST code

#else
#define CONST const
#endif

#ifdef __TYPEDEF
    #ifndef  __TYPEDEF_ONCE
    #define __TYPEDEF_ONCE
    typedef unsigned char uint8_t;
    typedef char int8_t;
    typedef unsigned short  uint16_t;
    typedef short int16_t;
    typedef unsigned long uint32_t;
    typedef long int32_t;
        #ifndef NULL
            #define NULL (void*)0
        #endif
    #endif
#endif // __TYPEDEF end


#ifndef FALSE
    #define FALSE   (0)
#endif
            
#ifndef TRUE
    #define TRUE    (1)
#endif


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


/**********************************************************************/
typedef struct tagOnTime
{
	uint16_t elapseTime;
	uint8_t id;
	
}ONTIMER;


/************************************************************************/

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*参数
};
 

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;


typedef struct tagMSGMAP_ENTRY
{
	uint16_t message;
	uint8_t nSign;
	AFX_VOID pfn;
}AFX_MSGMAP_ENTRY;
 
typedef struct tagCmdTarget
{
	const AFX_MSGMAP_ENTRY *lpEn;
	const PreFilterMsg preMsgHandler;
}cmdTarget;

/*################################################*/
//定义函数结果状态码   
#define OK 1   
#define ERROR 0   
  
//定义循环队列空间大小   
#define QUEUESIZE 16   
  
//定义数据类型   
typedef  int ElemType;   
//定义程序返回状态类型   
typedef  int State;  

typedef struct tagSqMsg
{
	 void *lpMsgEn;
	 void *extraData;
	uint16_t msg;
}QueueMsg;

//循环队列存储结构   
typedef struct _CircleQueue  
{  
    QueueMsg data_elem[QUEUESIZE];//存储队列元素   
    uint8_t front;//队列头指针   
    uint8_t rear;//队列尾指针   
    int count;//队列元素个数   
}CircleQueue;  
  

/*##################################################*/

#define initMsg(theClass)\
static CONST cmdTarget myTest = {&theClass[0]};

#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},

#define DECLARE_MESSAGE_MAP()\
const cmdTarget* lpEn; 


#define BEGIN_MESSAGE_MAP(theClass)\
static CONST AFX_MSGMAP_ENTRY theClass[]={\
	
#define END_MESSAGE_MAP(lpDef) \
			{0,AfxSig_vpv,(AFX_VOID)lpDef } \
};\



#define INIT_MSG_ENTRIES(theClass,cmdTargetName,preMsgHandler)\
 CONST cmdTarget cmdTargetName = {theClass,preMsgHandler};

#define GET_MESSAGE_MAP(theClass, cmdTargetName) \
		theClass.lpEn = &cmdTargetName;


#define DECLARE_cmdTarget(theClass) \
extern CONST cmdTarget theClass;

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



/************************************************************************/

#define KEY_LONG_ON			0X8000
#define KEY_OFF				0X4000
#define KEY_BLOCK_EVENT		(KEY_LONG_ON|KEY_OFF)    //阻塞长按键


/*############################################################*/
/*    	短按按键事件ID																			*/
/*################################################################*/
#define KEY_1_SHORT           			0X01			
#define KEY_2_SHORT				        0X02			
#define KEY_3_SHORT			            0X04			
#define KEY_4_SHORT		                0X08
#define KEY_5_SHORT           			0X10			
#define KEY_6_SHORT				        0X20			
#define KEY_7_SHORT			            0X40			
#define KEY_8_SHORT		                0X80
#define KEY_9_SHORT           			0X100			
#define KEY_10_SHORT				    0X200			
#define KEY_11_SHORT			        0X400			
#define KEY_12_SHORT		            0X800
#define KEY_13_SHORT           			0X1000			
#define KEY_14_SHORT				    0X2000			


/*############################################################*/
/*    	长按按键事件ID																			*/
/*################################################################*/
#define KEY_1_LONG_ON		     (KEY_1_SHORT + KEY_LONG_ON)
#define KEY_2_LONG_ON		     (KEY_2_SHORT + KEY_LONG_ON)
#define KEY_3_LONG_ON		     (KEY_3_SHORT + KEY_LONG_ON)
#define KEY_4_LONG_ON		     (KEY_4_SHORT + KEY_LONG_ON)
#define KEY_5_LONG_ON		     (KEY_5_SHORT + KEY_LONG_ON)
#define KEY_6_LONG_ON		     (KEY_6_SHORT + KEY_LONG_ON)
#define KEY_7_LONG_ON		     (KEY_7_SHORT + KEY_LONG_ON)
#define KEY_8_LONG_ON		     (KEY_8_SHORT + KEY_LONG_ON)
#define KEY_9_LONG_ON		     (KEY_9_SHORT + KEY_LONG_ON)
#define KEY_10_LONG_ON		     (KEY_10_SHORT + KEY_LONG_ON)
#define KEY_11_LONG_ON		     (KEY_11_SHORT + KEY_LONG_ON)
#define KEY_12_LONG_ON		     (KEY_12_SHORT + KEY_LONG_ON)
#define KEY_13_LONG_ON		     (KEY_13_SHORT + KEY_LONG_ON)
#define KEY_14_LONG_ON		     (KEY_14_SHORT + KEY_LONG_ON)


/*############################################################*/
/*    	短按松开按键事件ID										*/
/*################################################################*/
#define KEY_1_OFF		    (KEY_1_SHORT + KEY_OFF)
#define KEY_2_OFF		    (KEY_2_SHORT + KEY_OFF)
#define KEY_3_OFF		    (KEY_3_SHORT + KEY_OFF)
#define KEY_4_OFF		    (KEY_4_SHORT + KEY_OFF)
#define KEY_5_OFF		    (KEY_5_SHORT + KEY_OFF)
#define KEY_6_OFF		    (KEY_6_SHORT + KEY_OFF)
#define KEY_7_OFF		    (KEY_7_SHORT + KEY_OFF)
#define KEY_8_OFF		    (KEY_8_SHORT + KEY_OFF)
#define KEY_9_OFF		    (KEY_9_SHORT + KEY_OFF)
#define KEY_10_OFF		    (KEY_10_SHORT + KEY_OFF)
#define KEY_11_OFF		    (KEY_11_SHORT + KEY_OFF)
#define KEY_12_OFF		    (KEY_12_SHORT + KEY_OFF)
#define KEY_13_OFF		    (KEY_13_SHORT + KEY_OFF)
#define KEY_14_OFF		    (KEY_14_SHORT + KEY_OFF)
                          

#define WAKEUP_FOR_32HZ                       0X01
#define TICK_FOR_2HZ                          0X08
#define TICK_FOR_1HZ                          0X10


#define CLEAR_WAKEUP_FOR_32HZ                         (~WAKEUP_FOR_32HZ)
#define CLEAR_TICK_FOR_2HZ                            (~TICK_FOR_2HZ)  
#define CLEAR_TICK_FOR_1HZ                            (~TICK_FOR_1HZ) 
                          
                          
 /*
                   32HZ中断调用
                   参数:tick_value---时间标识
*/                          
#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)


/****************************************************************
		功能菜单数据结构
*****************************************************************/
typedef struct tagMenuKey
{
	DECLARE_MESSAGE_MAP()	//must be put it in first position
	AFX_PMSG dispFunc;
	uint16_t *ptrFlag;
}MENU_KEY;

    
#endif

step 3:

  baseCls.c中的内容如下:

#include "baseCls.h"


/*#################################################################*/

CircleQueue sqMsg={0};

QueueMsg *lpGetMsg;
AFX_PMSG  keyToneFunc = NULL;
   
  //判断队列为空和满   
  //1、使用计数器count,队列为空和满时,front都等于rear	 
  //2、少用一个元素的空间,约定队列满时:(rear+1)%QUEUESIZE=front,为空时front=rear	 
  //rear指向队尾元素的下一个位置,始终为空;队列的长度为(rear-front+QUEUESIZE)%QUEUESIZE   
	
 	
  /************************************************* 
  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;  
	
  }  
	
  /************************************************* 
  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文件分析

  1. 不同类型消息的函数指针

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_MAPEND_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);

宏体内容如下,最终使用PostMessageTICK_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