您当前的位置: 首页 >  windows

仙剑情缘

暂无认证

  • 3浏览

    0关注

    333博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

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

仙剑情缘 发布时间:2019-05-16 09:16:39 ,浏览量:3

  • 目的

实现类似于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 |= 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文件分析
  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_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

 

 

关注
打赏
1658017818
查看更多评论
立即登录/注册

微信扫码登录

0.0755s