/**
xr871 sdk evb_audio 工程代码中的 adc_button 逻辑分析
*/
################################################################
主函数 main.c
################################################################
void ad_button_init()
{
AD_Button_Irq irq;------------------------------------------1.1
DRV_AD_ButtonInit(&AD_Button_Cfg); -------------------------1.2
irq.arg = NULL;
irq.buttonCallback = ad_button_Cb;
DRV_AD_ButtonCallBackRegister(&irq);------------------------1.3
if (OS_ThreadCreate(&g_ad_button_ctrl_thread,
"",
ad_button_ctrl_task,--------------------1.4
NULL,
OS_THREAD_PRIO_APP,
AD_BUTTON_CTRL_THREAD_STACK_SIZE) != OS_OK) {
COMPONENT_WARN("thread create error\n");
}
COMPONENT_TRACK("end\n");
}
########################################
1.1 实例化 AD_Button_Irq 对象:
########################################
/**
* @brief AD button callback set and irq mode set.
*/
typedef struct {
/*!< The callback for ad button. irq_sta is interrupt status, ad_Value is ad capture value */
void (*buttonCallback) (void *arg, ADC_IRQState irq_sta, uint32_t ad_Value);
void *arg;
}AD_Button_Irq;
########################################
1.2 实例化 AD_Button_Config 对象
########################################
typedef struct {
ADC_Channel channel; /*!< The ad channel used for ad button*/
ADC_IRQMode ad_Button_Irq_Mode; /*!< The channel's interrupt mode */
uint32_t lowValue; /*!< lower limit value in interrupt mode of ADC_IRQ_LOW,
ADC_IRQ_LOW_DATA, ADC_IRQ_LOW_HIGH or ADC_IRQ_LOW_HIGH_DATA*/
uint32_t highValue; /*!< Upper limit value in interrupt mode of ADC_IRQ_HIGH,
ADC_IRQ_HIGH_DATA, ADC_IRQ_LOW_HIGH or ADC_IRQ_LOW_HIGH_DATA*/
}AD_Button_Config;
static AD_Button_Config AD_Button_Cfg = {
ADC_CHANNEL_3, ------------通道参数
ADC_IRQ_LOW, ------------初始中断模式
3000, ------------低于此阈值产生adc_irq_low 中断
3500, ------------高于此阈值产生adc_irq_high 中断
};
/**
* @brief Init the ad button.
* @note This function is used to configure the ad button pin and interrut triggering conditions.
* @param ad_button_info:
* @arg ad_button_info->channel:The channels used for ad button.
* @arg ad_button_info->ad_Button_Irq_Mode: interrupt triggering conditions.
* @arg ad_button_info->lowValue: lower limit value in interrupt mode of ADC_IRQ_LOW,
* ADC_IRQ_LOW_DATA, ADC_IRQ_LOW_HIGH or ADC_IRQ_LOW_HIGH_DATA
* @arg ad_button_info->highValue: Upper limit value in interrupt mode of ADC_IRQ_HIGH,
* ADC_IRQ_HIGH_DATA, ADC_IRQ_LOW_HIGH or ADC_IRQ_LOW_HIGH_DATA
* @retval Component_Status: The status of driver.
*/
typedef struct {
uint16_t chanPinMux;
ADC_State state;
ADC_WorkMode mode;
uint32_t lowPending;
uint32_t highPending;
uint32_t dataPending;
ADC_IRQCallback IRQCallback[ADC_CHANNEL_NUM];
void *arg[ADC_CHANNEL_NUM];
} ADC_Private;
static ADC_Private gADCPrivate;
Component_Status DRV_AD_ButtonInit(AD_Button_Config *ad_button_info)
{
AD_Button = *ad_button_info;
ADC_InitParam initParam;
initParam.delay = 10;
initParam.freq = 500000; ---------adc采样率
initParam.mode = ADC_CONTI_CONV; ---------adc工作模式
HAL_Status sta = HAL_ADC_Init(&initParam); --------adc 时钟,采样率,工作模式,中断使能
{{{
在 hal_adc_init() 中
gADCPrivate->chanPinMux = 0;
gADCPrivate->lowPending = 0;
gADCPrivate->highPending = 0;
gADCPrivate->dataPending = 0;
gADCPrivate->mode = initParam->mode; //此处为 ADC_CONTI_CONV 在上面的 DRV_AD_ButtonInit() 方法中
}}}
if (sta == HAL_OK || sta == HAL_BUSY) {
HAL_ADC_ConfigChannel(AD_Button.channel, ADC_SELECT_ENABLE, AD_Button.ad_Button_Irq_Mode,
AD_Button.lowValue, AD_Button.highValue);
----------------- adc 通道,阈值,中断模式配置
DRV_EnableAD_Button(); ----------------- adc 中断回调注册 1.2.1
if (sta == HAL_OK)
HAL_ADC_Start_Conv_IT();------------- adc 开启持续中断转换 ADC_EnableADC
return COMP_OK;
} else
COMPONENT_WARN("AD init error %d\n", sta);
COMPONENT_TRACK("end\n");
return COMP_ERROR;
}
########################################
1.2.1 DRV_EnableAD_Button
########################################
/**
* @brief Enable ad button.
* @note This function is used to enable the ad button, if ad button is enable,
* when you push the button, the interrupt will be tigger.
* @retval Component_Status: The status of driver.
*/
Component_Status DRV_EnableAD_Button()
{
HAL_ADC_EnableIRQCallback(AD_Button.channel, AD_Button_Cb, NULL); ---------------------->
return COMP_OK;
}
----------------------------------------------------------->下面分析 HAL_ADC_EnableIRQCallback
/**
* @brief Enable interrupt callback function for the specified ADC channel
* @param[in] chan The specified ADC channel
* @param[in] cb The interrupt callback function
* @param[in] arg Argument of the interrupt callback function
* @retval HAL_Status, HAL_OK on success
*/
HAL_Status HAL_ADC_EnableIRQCallback(ADC_Channel chan, ADC_IRQCallback cb, void *arg)
{
unsigned long flags;
ADC_Private *priv;
ADC_ASSERT_CHANNEL(chan);
flags = HAL_EnterCriticalSection();
priv = &gADCPrivate;
if ((priv->state == ADC_STATE_READY) || (priv->state == ADC_STATE_BUSY)) {
priv->arg[chan] = arg;
priv->IRQCallback[chan] = cb; ---------- 回调注册 即:AD_Button_Cb
} else {
priv = NULL;
}
HAL_ExitCriticalSection(flags);
if (priv == NULL) {
HAL_WRN("ADC state: %d\n", gADCPrivate.state);
return HAL_ERROR;
}
return HAL_OK;
}
-----------------------------------------------------------------------> 下面看 AD_Button_Cb
通过高低中断的切换触发达到检测按下和抬起的目的
static void AD_Button_Cb(void *arg)
{
ADC_IRQState irq_sta = HAL_ADC_GetIRQState(AD_Button.channel);
if (irq_sta == ADC_LOW_IRQ) { -------------- 低于阈值触发的中断 ADC_LOW_IRQ (检测按键按下)
uint32_t ad_value = AD_button_filter( HAL_ADC_GetValue(AD_Button.channel)); --- 获取ad结果,滤波
if (ad_value) {
if (AD_Button_Private.buttonCallback)
AD_Button_Private.buttonCallback(AD_Button_Private.arg,
irq_sta, ad_value); ----- 调用已经注册的按键回调 后面分析
Private_ADC_Irq = ADC_IRQ_LOW_HIGH; ---------配置下次中断为高于阈值触发(检测按键提起)
HAL_ADC_ConfigChannel(AD_Button.channel, ADC_SELECT_ENABLE, Private_ADC_Irq,
ad_value - 50, AD_Button.highValue);
DRV_AD_BUTTON_DBG("<<<< @FILE: %s, @FUNC: %s, @LINE: %d >>>>, ad_value: %d\n",
__FILE__, __func__, __LINE__, ad_value);
}
} else if (irq_sta == ADC_HIGH_IRQ) { --------------高于阈值触发的中断 ADC_HIGH_IRQ
uint32_t ad_value = HAL_ADC_GetValue(AD_Button.channel);
if (AD_Button_Private.buttonCallback)
AD_Button_Private.buttonCallback(AD_Button_Private.arg,
irq_sta, HAL_ADC_GetValue(AD_Button.channel));
Private_ADC_Irq = ADC_IRQ_LOW; -----------------配置下次中断为低于阈值触发(检测按键按下)
HAL_ADC_ConfigChannel(AD_Button.channel, ADC_SELECT_ENABLE, Private_ADC_Irq,
AD_Button.lowValue, AD_Button.highValue);
DRV_AD_BUTTON_DBG("<<<< @FILE: %s, @FUNC: %s, @LINE: %d >>>>, ad_value: %d\n",
__FILE__, __func__, __LINE__, ad_value);
}
}
-----------------------------------------------------------------------> 下面看 adc 中断服务方法
此方法在硬件adc中断触发后自动调用,在startup.s 中 vi project/common/startup/gcc/startup.s +326
.word GPADC_IRQHandler 注册adc异常中断服务的名称和入口地址
.weak GPADC_IRQHandler 弱类型symbol,可被强类型symbol覆盖 也就是下面的自定义中断服务方法
.thumb_set GPADC_IRQHandler,Default_Handler
/******************************************************
Tips:当一个异常出现以后,ARM会自动执行以下几个步骤:
1.把下一条指令的地址放到连接寄存器LR(通常是R14).---保存位置
2.将相应的CPSR(当前程序状态寄存器)复制到SPSR(备份的程序状态寄存器)中---保存CPSR
3.根据异常类型,强制设置CPSR的运行模式位
4.强制PC(程序计数器)从相关异常向量地址取出下一条指令执行,从而跳转到相应的异常处理程序中
*********************************************************/
void GPADC_IRQHandler(void)
{
if(gADCPrivate.mode == ADC_BURST_CONV) { --------- 非burst此处,不进入
uint32_t i;
uint32_t fifoverunPending, fifodataPending;
fifoverunPending = ADC_GetFifoOverunPending();
fifodataPending = ADC_GetFifodataPending();
ADC_ClrFifoPending(fifoverunPending);
ADC_ClrFifoPending(fifodataPending);
if(fifodataPending) {
for (i = ADC_CHANNEL_0; i < ADC_CHANNEL_NUM; i++) {
if (ADC_GetChanPinMux(i) && (gADCPrivate.IRQCallback[i]))
gADCPrivate.IRQCallback[i](gADCPrivate.arg[i]);
}
}
} else { --------- 进入此分支
uint32_t i;
gADCPrivate.lowPending = ADC_GetLowPending();
gADCPrivate.highPending = ADC_GetHighPending();
gADCPrivate.dataPending = ADC_GetDataPending();
ADC_ClrLowPending(gADCPrivate.lowPending); ------- clear挂起标志
ADC_ClrHighPending(gADCPrivate.highPending);
ADC_ClrDataPending(gADCPrivate.dataPending);
for (i = ADC_CHANNEL_0; i < ADC_CHANNEL_NUM; i++) {
if (((HAL_GET_BIT(gADCPrivate.dataPending, HAL_BIT(i)) && ADC_GetChanDataIRQ(i))
|| (HAL_GET_BIT(gADCPrivate.lowPending, HAL_BIT(i)) && ADC_GetChanLowIRQ(i))
|| (HAL_GET_BIT(gADCPrivate.highPending, HAL_BIT(i)) && ADC_GetChanHighIRQ(i)))
&& (gADCPrivate.IRQCallback[i])) {
gADCPrivate.IRQCallback[i](gADCPrivate.arg[i]); --------- 调用注册的中断回调函数 AD_Button_Cb
}
}
}
}
详查 AD_Button_Cb 中的 代码段
static AD_Button_Irq AD_Button_Private = {NULL, NULL};
--------------------------------------------------------
if (ad_value) {
if (AD_Button_Private.buttonCallback)
AD_Button_Private.buttonCallback(AD_Button_Private.arg,
irq_sta, ad_value); ----- 调用已经注册的按键回调 后面分析 1.3
.......................
}
--------------------------------------------------------
########################################
1.3 DRV_AD_ButtonCallBackRegister
########################################
irq.arg = NULL;
irq.buttonCallback = ad_button_Cb;
DRV_AD_ButtonCallBackRegister(&irq);
{
AD_Button_Private.buttonCallback = irq->buttonCallback;
AD_Button_Private.arg = irq->arg;
}
最终回调到:ad_button_Cb
void ad_button_Cb(void *arg, ADC_IRQState sta, uint32_t ad_value)
{
if (sta == ADC_LOW_IRQ) { // 按下
if (ad_value) // 滤波后的ad值
ad_button_id_refresh(ad_value); ----------------------------> 下面分析
}else if(sta == ADC_HIGH_IRQ) // 释放
AD_Button = AD_BUTTON_ALL_RELEASE;
}
标定值
typedef enum {
AD_BUTTON_0_VALUE = 850, -----------------------------理论值0.50v
AD_BUTTON_1_VALUE = 1780, -----------------------------理论值1.05v
AD_BUTTON_2_VALUE = 2754, -----------------------------理论值1.65v
AD_BUTTON_VALUE_NULL = -1,
}AD_BUTTON_VALUE;
typedef struct {
AD_Button_RepeatPressMode *RepeatMode;
uint16_t long_Press_hold_Time_Ms;
uint16_t short_Press_hold_Time_Ms;
AD_BUTTON_VALUE button_Ad_Value;
AD_BUTTON_ID button_Id;
}AD_Button_Info;
typedef struct {
uint16_t repeat_Time_Ms;
uint16_t repeat_Period_Ms;
}AD_Button_RepeatPressMode;
AD_Button_RepeatPressMode Ad_Button_2_Repeat = {700, 10};
AD_Button_Info AD_Button_Register[AD_BUTTON_NUM] = {
{NULL, 0, 10, AD_BUTTON_0_VALUE, AD_BUTTON_0},
{NULL, 0, 10, AD_BUTTON_1_VALUE, AD_BUTTON_1},
{&Ad_Button_2_Repeat, 0, 10, AD_BUTTON_2_VALUE, AD_BUTTON_2},
};
// 判定哪颗按键按下,并将按键id赋值给全局变量AD_Button
void ad_button_id_refresh(uint32_t ad_value)
{
int16_t i = 0;
uint32_t d_v = 0;
AD_Button_Info *p = AD_Button_Register;
for (i = 0; i < AD_BUTTON_NUM; i++) {
d_v = ad_d_Value(ad_value, p->button_Ad_Value); ------ 与标定值之间,计算差值,
if (d_v <= AD_VALUE_DEVAATION) { ------ 在± 100的偏差范围内
AD_Button = p->button_Id; ------ 确定哪颗按键按下 1.4 中使用
return;
}
p ++;
}
AD_Button = AD_BUTTON_NUM; ------ 1.4 中使用
}
########################################
1.4 线程服务 ad_button_ctrl_task
########################################
void ad_button_ctrl_task(void *arg)
{
DRV_AD_BUTTON_CTRL_DBG("%s\n", __func__);
AD_BUTTON_ID button_id = AD_BUTTON_NUM;
while (1) { ---------------------------------------------------一直循环
AD_Button_Info *button_info = NULL;
if (AD_Button != AD_BUTTON_NUM && AD_Button != AD_BUTTON_ALL_RELEASE) { // 按下分支
if (AD_Button != button_id && AD_Button_Is_Trigger == 0) {
button_id = AD_Button; -------------------- 按键id
DRV_AD_BUTTON_CTRL_DBG("AD_Button %d\n", AD_Button);
AD_Button_Press_Time = OS_JiffiesToMSecs(OS_GetJiffies()); ----------- 记录按下时间点
DRV_AD_BUTTON_CTRL_DBG("AD_Button_Press_Time %d\n", AD_Button_Press_Time);
}
} else if (button_id != AD_BUTTON_NUM && AD_Button == AD_BUTTON_ALL_RELEASE) { // 抬起分支
//RELEASE
DRV_AD_BUTTON_CTRL_DBG("release\n");
button_info = &AD_Button_Register[button_id];
ad_button_short_press(button_info); ------------ 1.4.2 检测短按,并推送短按事件
ad_button_release_cmd (button_info); ------------ 1.4.3 推送释放事件
button_id = AD_BUTTON_NUM;
AD_Button_Is_Trigger = 0;
}
if (button_id != AD_BUTTON_NUM) { // 按下分支
button_info = &AD_Button_Register[button_id]; ------------ 按键信息提取赋值
ad_button_check(button_info); ------------ 检查 1.4.1
}
OS_MSleep(10); ----------- sleep 10 ms
}
}
########################################
1.4.1 ad_button_check(button_info);
########################################
void ad_button_check(AD_Button_Info *button)
{
uint32_t os_time = OS_JiffiesToMSecs(OS_GetJiffies()); ----------- 获得此刻的系统运行时间
ad_button_repeat(button, os_time); 下----> 检测重复按,并推送重复按事件
ad_button_long_press(button, os_time); 下----> 检测长按,并推送长按事件
}
typedef enum {
AD_BUTTON_CMD_LONG_PRESS,
AD_BUTTON_CMD_SHORT_PRESS,
AD_BUTTON_CMD_REPEAT,
AD_BUTTON_CMD_RELEASE,
AD_BUTTON_CMD_NULL,
}AD_BUTTON_CMD;
typedef enum {
AD_BUTTON_0,
AD_BUTTON_1,
AD_BUTTON_2,
AD_BUTTON_NUM,
AD_BUTTON_ALL_RELEASE,
}AD_BUTTON_ID;
typedef struct {
AD_BUTTON_CMD cmd;
AD_BUTTON_ID id;
}AD_Button_Cmd_Info;
void ad_button_repeat(AD_Button_Info *button, uint32_t os_time)
{
static uint32_t last_d_time = 0;
if (button->RepeatMode != NULL) { -------即:{&Ad_Button_2_Repeat, 0, 10, AD_BUTTON_2_VALUE, AD_BUTTON_2},
uint32_t repeat_time = button->RepeatMode->repeat_Time_Ms; ---------------- 重复时间 700ms
uint32_t repeat_period = button->RepeatMode->repeat_Period_Ms; ---------------- 重复周期 10ms
/**< 在任务大循环中持续获取系统时间 并与按下时间点做差值 */
uint32_t d_time = ad_button_d_time(AD_Button_Press_Time, os_time); ---- 1.4 中(记录按下时间点)
if (d_time >= repeat_time) { ------------ > 700 ms, 认为repeat
AD_Button_Is_Trigger = 1; ----- 设置触发标志
if (d_time - last_d_time >= repeat_period) {
AD_Button_Cmd_Info * p = &AD_Button_Cmd; -----全局 static AD_Button_Cmd_Info AD_Button_Cmd;
p->cmd = AD_BUTTON_CMD_REPEAT; -----重复按下
p->id = button->button_Id;
ad_button_send_vkey(p); ------------------------------->
last_d_time = d_time;
}
}
}
}
----------------------------------->
推送发布按键按下的消息到g_sys_queue(event_queue),由 main_publisher 线程来 从 g_sys_queue 中 recv 监听
并且 notify 所有已经 attach 在 g_sys_publisher 的 subscribers
----------------------------------->
static int ad_button_send_vkey(AD_Button_Cmd_Info *data)
{
// 消息类型,消息子类型, *data = {cmd = AD_BUTTON_CMD_REPEAT, id = button->button_Id}
if (sys_event_send(CTRL_MSG_TYPE_VKEY, CTRL_MSG_SUB_TYPE_AD_BUTTON, (uint32_t)data, 0) != 0) {
COMPONENT_WARN("send vkey error\n");
return -1;
}
return 0;
}
void ad_button_long_press(AD_Button_Info *button, uint32_t os_time)
{
// long_Press_hold_Time_Ms = 0ms, short_Press_hold_Time_Ms = 10ms; 此时不成立,不进行长按检测
if (button->long_Press_hold_Time_Ms > button->short_Press_hold_Time_Ms) {
uint32_t d_time = ad_button_d_time(AD_Button_Press_Time, os_time);
if (d_time >= button->long_Press_hold_Time_Ms && AD_Button_Is_Trigger == 0) { // 长按检测优先级低于repeat
AD_Button_Cmd_Info * p = &AD_Button_Cmd;
p->cmd = AD_BUTTON_CMD_LONG_PRESS;
p->id = button->button_Id;
ad_button_send_vkey(p); -------- 推送按下消息
AD_Button_Is_Trigger = 1;
}
}
}
########################################
1.4.2 ad_button_short_press(button_info);
########################################
void ad_button_short_press(AD_Button_Info *button)
{
// 背景逻辑,非长按,非重复
if (AD_Button_Is_Trigger == 0) {
uint32_t os_time = OS_JiffiesToMSecs(OS_GetJiffies());
uint32_t d_time = ad_button_d_time(AD_Button_Press_Time, os_time);
if (d_time >= button->short_Press_hold_Time_Ms) { // 10 ms s
AD_Button_Cmd_Info * p = &AD_Button_Cmd;
p->cmd = AD_BUTTON_CMD_SHORT_PRESS;
p->id = button->button_Id;
ad_button_send_vkey(p); -------- 推送按下消息
}
}
}
########################################
1.4.2 ad_button_release_cmd(button_info);
########################################
void ad_button_release_cmd (AD_Button_Info *button)
{
if (AD_Button_Is_Trigger) {
AD_Button_Cmd_Info * p = &AD_Button_Cmd;
p->cmd = AD_BUTTON_CMD_RELEASE; --- 释放
p->id = button->button_Id;
ad_button_send_vkey(p); -------- 推送按键释放消息
}
}
xr871 sdk evb_audio 的 adc_button 逻辑分析
最新推荐文章于 2018-10-22 23:15:43 发布