对于按键驱动修改不错的博文:https://wenku.baidu.com/view/12e61922aef8941ea66e05b9.html
https://blog.csdn.net/stone8761/article/details/79563594
此外,使用的TI BLE协议栈版本是1.3.2。
KEY服务API
KEY服务一共有7个API。分别是:
- HalKeyInit()
- HalKeyConfig()
- HalKeyRead()
- HalKeyEnterSleep()
- HalKeyExitSleep()
- HalKeyPoll()
- HalKeyPressed()
其中,HalKeyInit()、HalKeyConfig()、HalKeyRead()、HalKeyPoll()是我们需要关注的。
1.五向按键的硬件原理
五向按键的五个方向:上、下、左、右、垂直向下。原理是只要五向按键有按压动作,在JOY_MOVE引脚会产生中断,此时再去采集JOY_LEVEL的电压,根据电压范围来判断当前的五向按键的方向。原理还是很简单的,电路倒是有点复杂。
此外,开发板还有一个单独的按键S1。
2.KEY服务API源码分析
因为我使用的是开发板的例程,五向按键部分的电路和官方是不一样的,因此使用开发板提供的修改文件hal_key.c。
2.1 HalKeyInit()
-
/**************************************************************************************************
-
* @fn HalKeyInit
-
*
-
* @brief Initilize Key Service
-
*
-
* @param none
-
*
-
* @return None
-
**************************************************************************************************/
-
void HalKeyInit( void )
-
{
-
#if (HAL_KEY == TRUE)
-
/* Initialize previous key to 0 */
-
halKeySavedKeys = HAL_KEY_CODE_NOKEY;
-
/* The advanced remote doesn‘t have the same 8X8 row/column matrix as in other
-
* products. Instead, a 3X16 row/column matrix is used, with the rows continuing
-
* to be utilized by GPIOs, but the columns are generated via a 16 bit
-
* shift register. Controls for the shift register are, however, utilized with
-
* GPIOs.
-
*
-
* Another difference is that the GPIOs utilized for the rows are split between
-
* P0 and P1.
-
*/
-
//ghostyu from EM
-
HAL_KEY_SW_6_SEL &= ~(HAL_KEY_SW_6_BIT); /* Set pin function to GPIO */
-
HAL_KEY_SW_6_DIR &= ~(HAL_KEY_SW_6_BIT); /* Set pin direction to Input */
-
HAL_KEY_JOY_MOVE_SEL &= ~(HAL_KEY_JOY_MOVE_BIT); /* Set pin function to GPIO */
-
HAL_KEY_JOY_MOVE_DIR &= ~(HAL_KEY_JOY_MOVE_BIT); /* Set pin direction to Input */
-
/* Initialize callback function */
-
<span style="font-size:10px;">pHalKeyProcessFunction = NULL;</span>
-
/* Start with key is not configured */
-
HalKeyConfigured = FALSE;
-
<span style="font-size:10px;">halKeyTimerRunning = FALSE;</span>
-
#endif /* HAL_KEY */
-
}
这里主要是对两个引脚(一个用于触发中断,一个用于AD采样)进行设置,并设置相关文件域的全局变量,包括pHalKeyProcessFunction、HalKeyConfigured、halKeyTimerRunning。在使用KEY服务前必须要调用该函数,在TI的例程中只要开启相关宏定义即可,这时会在HalDriverInit()中进行初始化:
2.2 HalKeyConfig()
-
/**************************************************************************************************
-
* @fn HalKeyConfig
-
*
-
* @brief Configure the Key serivce
-
*
-
* @param interruptEnable - TRUE/FALSE, enable/disable interrupt
-
* cback - pointer to the CallBack function
-
*
-
* @return None
-
**************************************************************************************************/
-
void HalKeyConfig (bool interruptEnable, halKeyCBack_t cback)
-
{
-
#if (HAL_KEY == TRUE)
-
/* Enable/Disable Interrupt */
-
Hal_KeyIntEnable = interruptEnable;
-
/* Register the callback fucntion */
-
pHalKeyProcessFunction = cback;
-
/* Determine if interrupt is enabled or not */
-
if (Hal_KeyIntEnable)
-
{
-
//ghostyu from EM
-
/* Rising/Falling edge configuratinn */
-
PICTL &= ~(HAL_KEY_SW_6_EDGEBIT); /* Clear the edge bit */
-
/* For rising edge, the bit must be set. */
-
PICTL &= ~(HAL_KEY_SW_6_EDGEBIT);
-
/* Interrupt configuration:
-
* - Enable interrupt generation at the port
-
* - Enable CPU interrupt
-
* - Clear any pending interrupt
-
*/
-
HAL_KEY_SW_6_ICTL |= HAL_KEY_SW_6_ICTLBIT;
-
HAL_KEY_SW_6_IEN |= HAL_KEY_SW_6_IENBIT;
-
HAL_KEY_SW_6_PXIFG = ~(HAL_KEY_SW_6_BIT);
-
/* Rising/Falling edge configuratinn */
-
HAL_KEY_JOY_MOVE_ICTL &= ~(HAL_KEY_JOY_MOVE_EDGEBIT); /* Clear the edge bit */
-
/* For falling edge, the bit must be set. */
-
HAL_KEY_JOY_MOVE_ICTL |= HAL_KEY_JOY_MOVE_EDGEBIT;
-
/* Interrupt configuration:
-
* - Enable interrupt generation at the port
-
* - Enable CPU interrupt
-
* - Clear any pending interrupt
-
*/
-
HAL_KEY_JOY_MOVE_ICTL |= HAL_KEY_JOY_MOVE_ICTLBIT;
-
HAL_KEY_JOY_MOVE_IEN |= HAL_KEY_JOY_MOVE_IENBIT;
-
HAL_KEY_JOY_MOVE_PXIFG = ~(HAL_KEY_JOY_MOVE_BIT);
-
/* Do this only after the hal_key is configured - to work with sleep stuff */
-
if (HalKeyConfigured == TRUE)
-
{
-
osal_stop_timerEx( Hal_TaskID, HAL_KEY_EVENT); /* Cancel polling if active */
-
}
-
}
-
else /* Interrupts NOT enabled */
-
{
-
HAL_KEY_SW_6_ICTL &= ~(HAL_KEY_SW_6_ICTLBIT); /* don‘t generate interrupt */
-
HAL_KEY_SW_6_IEN &= ~(HAL_KEY_SW_6_IENBIT); /* Clear interrupt enable bit */
-
osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_POLLING_VALUE); /* Kick off polling */
-
}
-
/* Key now is configured */
-
HalKeyConfigured = TRUE;
-
#endif /* HAL_KEY */
-
}
该函数对KEY服务进行配置,形参有2个:interruptEnable, cback。
interruptEnable用来选择按键的扫描方式,KEY服务的按键扫描方式有两种,一种是循环扫描,另外一种是中断扫描。循环扫描和中断扫描的具体过程后面会介绍。cback就是应用层传给HAL KEY服务的回调函数,用于通知应用层相关按键的触发动作。回调函数的原型是:
typedef void (*halKeyCBack_t) (uint8 keys, uint8 state);
keys表示哪个按键被按下,state表示按键是怎么被按下的,TI的HAL进行如下定义:
state中HAL_KEY_STATE_SHIFT好像不怎么用得到,用HAL_KEY_STATE_NORMAL比较多。
2.3 HalKeyConfig()
-
/**************************************************************************************************
-
* @fn HalKeyRead
-
*
-
* @brief Read the current value of a key
-
*
-
* @param None
-
*
-
* @return keys - current keys status
-
**************************************************************************************************/
-
uint8 HalKeyRead ( void )
-
{
-
uint8 keys = 0;
-
//注意这里的SW6对应SmartRF开发板上的S1,是高电平有效
-
if ((HAL_KEY_SW_6_PORT & HAL_KEY_SW_6_BIT)) /* Key is active HIGH */
-
{
-
keys |= HAL_KEY_SW_6;
-
}
-
if ((HAL_KEY_JOY_MOVE_PORT & HAL_KEY_JOY_MOVE_BIT)) /* Key is active low */
-
{
-
keys |= halGetJoyKeyInput();
-
}
-
return keys;
-
}
该函数不需要应用层调用,是有HAL层调用的,在开发板的例程中,因为硬件上只有一个按键和一个五向按键,所以该函数的前面部分是通过IO口电平判断按键现在的状态,后面部分则是具体检测五向按键的状态。首先要确定五向按键被操作了,之后再执行函数halGetJoyKeyInput()。
-
/**************************************************************************************************
-
* @fn halGetJoyKeyInput
-
*
-
* @brief Map the ADC value to its corresponding key.
-
*
-
* @param None
-
*
-
* @return keys - current joy key status
-
**************************************************************************************************/
-
uint8 halGetJoyKeyInput(void)
-
{
-
/* The joystick control is encoded as an analog voltage.
-
* Read the JOY_LEVEL analog value and map it to joy movement.
-
*/
-
uint8 adc;
-
uint8 ksave0 = 0;
-
uint8 ksave1;
-
/* Keep on reading the ADC until two consecutive key decisions are the same. */
-
do
-
{
-
ksave1 = ksave0; /* save previouse key reading */
-
adc = HalAdcRead (HAL_KEY_JOY_CHN, HAL_ADC_RESOLUTION_8);
-
if ((adc >= 2) && (adc <= 38))
-
{
-
ksave0 |= HAL_KEY_UP;
-
}
-
else if ((adc >= 74) && (adc <= 88))
-
{
-
ksave0 |= HAL_KEY_RIGHT;
-
}
-
else if ((adc >= 60) && (adc <= 73))
-
{
-
ksave0 |= HAL_KEY_LEFT;
-
}
-
else if ((adc >= 39) && (adc <= 59))
-
{
-
ksave0 |= HAL_KEY_DOWN;
-
}
-
else if ((adc >= 89) && (adc <= 100))
-
{
-
ksave0 |= HAL_KEY_CENTER;
-
}else{
-
ksave0=0;
-
}
-
} while (ksave0 != ksave1);
-
return ksave0;
-
}
2.4 HalKeyPoll()
-
/**************************************************************************************************
-
* @fn HalKeyPoll
-
*
-
* @brief Called by hal_driver to poll the keys
-
*
-
* @param None
-
*
-
* @return None
-
**************************************************************************************************/
-
#define GHOSTYU_CODE_NOKEY 0x00
-
void HalKeyPoll (void)
-
{
-
#if (HAL_KEY == TRUE)
-
uint8 keys = 0;
-
/*
-
* If interrupts are enabled, get the status of the interrupt-driven keys from ‘halSaveIntKey‘
-
* which is updated by the key ISR. If Polling, read these keys directly.
-
*/
-
keys = HalKeyRead();
-
/* Exit if polling and no keys have changed */
-
if (!Hal_KeyIntEnable)
-
{
-
if (keys == halKeySavedKeys)
-
{
-
return;
-
}
-
halKeySavedKeys = keys; /* Store the current keys for comparation next time */
-
}
-
if(keys==0){
-
(pHalKeyProcessFunction) (0, HAL_KEY_STATE_NORMAL);
-
}
-
/* Invoke Callback if new keys were depressed */
-
if ((keys != 0 /*HAL_KEY_CODE_NOKEY*/) && Hal_KeyIntEnable &&
-
(pHalKeyProcessFunction))
-
{
-
// When interrupt is enabled, send HAL_KEY_CODE_NOKEY as well so that
-
// application would know the previous key is no longer depressed.
-
(pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
-
}
-
//如果使能了中断触发,在中断触发后,poll按键,先是去抖动,去抖动结束后,如果该按键仍存在,
-
//则调用按键回调函数,向用户传递按键数据
-
//然后执行下面的代码,再延时50ms,看时候该按键是否仍然没有释放。如果没有释放,则继续调用按键的回调函数。
-
if (Hal_KeyIntEnable)
-
{
-
if (keys != 0/*HAL_KEY_CODE_NOKEY*/)
-
{
-
// In order to trigger callback again as far as the key is depressed,
-
// timer is called here.
-
osal_start_timerEx(Hal_TaskID, HAL_KEY_EVENT, 50);
-
}
-
else
-
{
-
halKeyTimerRunning = FALSE;
-
}
-
}
-
#endif /* HAL_KEY */
-
}
该函数是对应开发板硬件修改的,它也是HAL层来执行,不需要应用层进行判断,主要功能是对按键的状态进行采集分析,并作回调。不过我看这个函数有些怪怪的,可以再优化下,增加可读性。另外,该函数和TI官方代码最大不同是,它增加了回调函数的一个功能,就是所有按键都释放时会有回调,此时state=0。下面贴一下官方的代码:
-
/**************************************************************************************************
-
* @fn HalKeyPoll
-
*
-
* @brief Called by hal_driver to poll the keys
-
*
-
* @param None
-
*
-
* @return None
-
**************************************************************************************************/
-
void HalKeyPoll (void)
-
{
-
uint8 keys = 0;
-
uint8 notify = 0;
-
#if defined (CC2540_MINIDK)
-
if (!(HAL_KEY_SW_1_PORT & HAL_KEY_SW_1_BIT)) /* Key is active low */
-
{
-
keys |= HAL_KEY_SW_1;
-
}
-
if (!(HAL_KEY_SW_2_PORT & HAL_KEY_SW_2_BIT)) /* Key is active low */
-
{
-
keys |= HAL_KEY_SW_2;
-
}
-
#else
-
if (!(HAL_KEY_SW_6_PORT & HAL_KEY_SW_6_BIT)) /* Key is active low */
-
{
-
keys |= HAL_KEY_SW_6;
-
}
-
if ((HAL_KEY_JOY_MOVE_PORT & HAL_KEY_JOY_MOVE_BIT)) /* Key is active HIGH */
-
{
-
keys = halGetJoyKeyInput();
-
}
-
#endif
-
/* If interrupts are not enabled, previous key status and current key status
-
* are compared to find out if a key has changed status.
-
*/
-
if (!Hal_KeyIntEnable)
-
{
-
if (keys == halKeySavedKeys)
-
{
-
/* Exit - since no keys have changed */
-
return;
-
}
-
else
-
{
-
notify = 1;
-
}
-
}
-
else
-
{
-
/* Key interrupt handled here */
-
if (keys)
-
{
-
notify = 1;
-
}
-
}
-
/* Store the current keys for comparation next time */
-
halKeySavedKeys = keys;
-
/* Invoke Callback if new keys were depressed */
-
if (notify && (pHalKeyProcessFunction))
-
{
-
(pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
-
}
-
}
不过,TI的代码好像也有问题:
-
#else
-
if (!(HAL_KEY_SW_6_PORT & HAL_KEY_SW_6_BIT)) /* Key is active low */
-
{
-
keys |= HAL_KEY_SW_6;
-
}
-
if ((HAL_KEY_JOY_MOVE_PORT & HAL_KEY_JOY_MOVE_BIT)) /* Key is active HIGH */
-
{
-
keys = halGetJoyKeyInput();
-
}
-
#endif
keys = halGetJoyKeyInput();
应该改为:
keys |= halGetJoyKeyInput();
剩下的3个API:HalKeyEnterSleep()和HalKeyExitSleep()是没有内容的,HalKeyPressed()只找到声明没找到定义,就不管了。
3.KEY服务的工作流程
前面已经大概介绍了几个API的主要功能,现在来说说他们是怎么被HAL层使用的。
首先,当按键服务宏定义被打开后,HalKeyInit()函数会被调用(前面有介绍),之后,一般在设备启动任务中对KEY服务进行配置。
这里就有两个分支了,一个是当KEY_INT_ENABLED时,使用的是中断扫描,否则使用循环扫描方式,他们再HAL层工作流程就不太一样。
3.1 循环扫描
当使用循环扫描时,HalKeyConfig()在最后会触发HAL_KEY_EVENT事件:
-
/* Do this only after the hal_key is configured - to work with sleep stuff */
-
if (HalKeyConfigured == TRUE)
-
{
-
osal_stop_timerEx(Hal_TaskID, HAL_KEY_EVENT); /* Cancel polling if active */
-
}
-
}
-
else /* Interrupts NOT enabled */
-
{
-
#if defined ( CC2540_MINIDK )
-
HAL_KEY_SW_1_ICTL &= ~(HAL_KEY_SW_1_ICTLBIT); /* don‘t generate interrupt */
-
HAL_KEY_SW_1_IEN &= ~(HAL_KEY_SW_1_IENBIT); /* Clear interrupt enable bit */
-
HAL_KEY_SW_2_ICTL &= ~(HAL_KEY_SW_2_ICTLBIT); /* don‘t generate interrupt */
-
HAL_KEY_SW_2_IEN &= ~(HAL_KEY_SW_2_IENBIT); /* Clear interrupt enable bit */
-
#else
-
HAL_KEY_SW_6_ICTL &= ~(HAL_KEY_SW_6_ICTLBIT); /* don‘t generate interrupt */
-
HAL_KEY_SW_6_IEN &= ~(HAL_KEY_SW_6_IENBIT); /* Clear interrupt enable bit */
-
#endif // !CC2540_MINIDK
-
osal_set_event(Hal_TaskID, HAL_KEY_EVENT);
-
}
而在HAL任务中,会进行如下工作:
-
if (events & HAL_KEY_EVENT)
-
{
-
#if (defined HAL_KEY) && (HAL_KEY == TRUE)
-
/* Check for keys */
-
HalKeyPoll();
-
/* if interrupt disabled, do next polling */
-
if (!Hal_KeyIntEnable)
-
{
-
osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);
-
}
-
#endif
-
return events ^ HAL_KEY_EVENT;
-
}
这里我们可以注意到,当使用循环扫描时,扫描周期为100ms,每次处理该事件时调用的函数就是HalKeyPoll()。
3.2 中断扫描
和循环扫描所不同的是,它不会定时扫描,只有当中断触发时才会设置HAL_KEY_EVENT事件。
在中断处理中,当检测到是相应开关触发的中断后,会调用函数halProcessKeyInterrupt():
-
/**************************************************************************************************
-
* @fn halProcessKeyInterrupt
-
*
-
* @brief Checks to see if it‘s a valid key interrupt, saves interrupt driven key states for
-
* processing by HalKeyRead(), and debounces keys by scheduling HalKeyRead() 25ms later.
-
*
-
* @param
-
*
-
* @return
-
**************************************************************************************************/
-
void halProcessKeyInterrupt (void)
-
{
-
#if (HAL_KEY == TRUE)
-
//ghostyu from EM
-
bool valid=FALSE;
-
if (HAL_KEY_SW_6_PXIFG & HAL_KEY_SW_6_BIT) /* Interrupt Flag has been set */
-
{
-
HAL_KEY_SW_6_PXIFG = ~(HAL_KEY_SW_6_BIT); /* Clear Interrupt Flag */
-
valid = TRUE;
-
}
-
if (HAL_KEY_JOY_MOVE_PXIFG & HAL_KEY_JOY_MOVE_BIT) /* Interrupt Flag has been set */
-
{
-
HAL_KEY_JOY_MOVE_PXIFG = ~(HAL_KEY_JOY_MOVE_BIT); /* Clear Interrupt Flag */
-
valid = TRUE;
-
}
-
if (valid)
-
{
-
if (!halKeyTimerRunning)
-
{
-
halKeyTimerRunning = TRUE;
-
osalTimeUpdate();
-
osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_DEBOUNCE_VALUE);
-
}
-
}
-
#endif /* HAL_KEY */
-
}
原文地址:https://www.cnblogs.com/F-beifeng/p/9739297.html