杰理AC632N系列是一款很好的蓝牙SOC,于2021年推出,同时也公开了SDK,对于开发者来说应该足够用了。以下内容基于杰理官方SDK(fw-AC63_GP_MCU-AC63_GP_MCU_v1.4.0)梳理总结。

1.使用iokey前所需工作

在用户main函数里初始化iokey,该函数位于apps/main.c文件。

int user_main()
{#if (USE_KEY_DRIVER == 1)         //使用IOkeykey_driver_init(&iokey_para);  //初始化IOkey
#endif#if (DUAL_BANK_UPDATE_BY_UFW)//使用双备份升级方式dual_bank_update_init();
#endifwhile (1) {wdt_clear();//喂狗user_msg_handler();//处理消息,包含iokey按键消息//        __asm__ volatile("idle");//__asm__是GCC 关键字asm 的宏定义 #define __asm__ asm   用来声明一个内联汇编表达式}
//__volatile__是GCC 关键字volatile 的宏定义   #define __volatile__ volatile  向GCC 声明不允许对该内联汇编优化,否则当 使用了优化选项(-O)进行编译时,GCC 将会根据自己的判断决定是否将这个内联汇编表达式中的指令优化掉。return 0;
}

key_driver_init(&iokey_para); 用来初始化IOkey的各种配置,该函数位于bsp/AC632N/key_driver.c文件。注意看一下该函数的参数&iokey_para,该参数是一个结构体指针,其成员中.get_value .key_init .key_process是三个函数指针,该参数明确了具体指向的函数实体分别是io_get_key_value()、io_key_init(),io_key_process()。下面代码里列出该结构体原型以及三个函数原型。

/*----------------------------------------------------------------------------*/
/**@brief   按键初始化@param   key_para:按键参数@return  -1:初始化错误0:初始化成功@note
*/
/*----------------------------------------------------------------------------*/
int key_driver_init(struct key_driver_para *key_para)
{//初始化if (key_para->key_init) {return key_para->key_init(key_para);}return -1;
}//注意扫描频率,这里供的时间默认为10ms扫描一次
//则对应的消抖时间为 4*10 =40ms,其他以此类推
struct key_driver_para iokey_para = {.last_key           = NO_KEY,            //上一次get_value按键值, 初始化为NO_KEY;.filter_time        = 4,                 //按键消抖延时;.long_time           = 75,                //按键判定长按数量.hold_time          = (75 + 15),        //按键判定HOLD数量.get_value        = io_get_key_value,   //按键值获取.key_init         = io_key_init,        //按键状态初始化.key_process      = io_key_process,     //按键处理.key_init_ok      = 0,                  //按键初始化完成标志
};
//上面的结构体定义的原型在对应的key_driver.h头文件里,具体如下:
struct key_driver_para {u8  last_key;            //上一次get_value按键值
//== 用于消抖类参数u8 filter_value;       //用于按键消抖u8 filter_cnt;          //用于按键消抖时的累加值u8 filter_time;            //当filter_cnt累加到base_cnt值时, 消抖有效
//== 用于判定长按和HOLD事件参数u8 long_time;             //按键判定长按数量u8 hold_time;             //按键判定HOLD数量u8 press_cnt;           //与long_time和hold_time对比, 判断long和hold状态u8(*get_value)(void);                                  //按键值获取int (*key_init)(void *key_para);                       //按键初始化void (* key_process)(u8 key_status, u8 key_value);     //按键处理u8 key_init_ok;                                        //按键初始化完成标志
};
//----------------------------------------------------------
//结构体内三个函数指针成员的原型:
int io_key_init(void *para)
{struct key_driver_para *scan_para = (struct key_driver_para *)para;gpio_set_direction(io_key_table[0], 1);//设置io_key_table[0]引脚为输入gpio_set_direction(io_key_table[1], 1);//设置io_key_table[1]引脚为输入gpio_set_pull_down(io_key_table[0], 0);//设置io_key_table[0]引脚为不下拉gpio_set_pull_down(io_key_table[1], 0);//设置io_key_table[1]引脚为不下拉gpio_set_pull_up(io_key_table[0], 1);//设置io_key_table[0]引脚为上拉gpio_set_pull_up(io_key_table[1], 1);//设置io_key_table[1]引脚为上拉gpio_set_die(io_key_table[0], 1);//设置io_key_table[0]引脚为数字功能引脚(非模拟电压adc之类功能)gpio_set_die(io_key_table[1], 1);//设置io_key_table[1]引脚为数字功能引脚(非模拟电压adc之类功能)scan_para->key_init_ok = 1;  //标志置位,显示初始化过了puts("___io_key_init_ok___");return 0;
}/*----------------------------------------------------------------------------*/
/**@brief   IO按键值获取@param   void@return  按键值@note
*/
/*----------------------------------------------------------------------------*/
u8 io_get_key_value(void)
{//按键接地为按下if (!gpio_read(io_key_table[0])) {return 0;} else if (!gpio_read(io_key_table[1])) {return 1;}return NO_KEY;
}/*----------------------------------------------------------------------------*/
/**@brief   按键处理函数@param   key_status:  按键状态(短按、长按、连按、抬起等)@param   key_value:   按键值@return  viod@note    注意按键处理时间不宜过长
*/
/*----------------------------------------------------------------------------*/
void io_key_process(u8 key_status, u8 key_value)
{//按键处理,中断时间不宜过久,这里仅仅是作按键的区分//建议依靠消息机制把按键信息发送到主循环处理按键u16 key_msg;u8 err;key_msg = io_key_msg_table[key_value][key_status];//根据键值和按键状态在二维数组里找出对应的消息值。key_msg消息值是一个枚举类型中的某个元素值。err = msg_put_fifo(key_msg);//把按键消息放进消息池,先进先出方式if (err != 0) {puts("can not put msg");//串口打印消息}
}

初始化函数里用到了一个数组,io_key_table[ ],该数组在bsp/AC632N/board/board_demo.c文件里定义,指明了具体要初始化的两个引脚,一个是PA1,一个是PA2,在板级别=定义应该是为了方便开发者修改。


/************************** key driver config****************************/
#if (USE_KEY_DRIVER == 1)
//2个测试io按键
u16 io_key_table[2] = {IO_PORTA_01,IO_PORTA_02,
};
#endif

2.按键扫描

扫描到按键有动作后,根据键值和键状态,找到对应的消息值,把该消息放到消息池中。

/*----------------------------------------------------------------------------*/
/**@brief   按键扫描函数,判断是否有按键按下,按下的时间状态@param   _scan_para:按键参数@return  viod@note
*/
/*----------------------------------------------------------------------------*/
void  key_driver_scan(void *_scan_para)
{struct key_driver_para *scan_para = (struct key_driver_para *)_scan_para;u8 key_status = 0;u8 cur_key_value = NO_KEY;u8 key_value = NO_KEY;if (!scan_para->key_init_ok) {return;}//===== 按键值获取cur_key_value = scan_para->get_value();/* if (cur_key_value != NO_KEY) { *//*     log_info(">>>cur_key_value: %d", cur_key_value); *//* } *///===== 按键消抖处理if (cur_key_value != scan_para->filter_value && scan_para->filter_time) {  //当前按键值与上一次按键值如果不相等, 重新消抖处理, 注意filter_time != 0;scan_para->filter_cnt = 0;                     //消抖次数清0, 重新开始消抖scan_para->filter_value = cur_key_value;    //记录上一次的按键值return;                                  //第一次检测, 返回不做处理}if (scan_para->filter_cnt < scan_para->filter_time) {//当前按键值与上一次按键值相等, filter_cnt开始累加;scan_para->filter_cnt++;return;}//===== 按键类型判断if (cur_key_value == scan_para->last_key) {if (cur_key_value == NO_KEY) {                                     //没有按键按下return;}scan_para->press_cnt++;if (scan_para->press_cnt == scan_para->long_time) {                //长按key_status = KEY_LONG;} else if (scan_para->press_cnt == scan_para->hold_time) {         //连按(一直按着不放)key_status = KEY_HOLD;scan_para->press_cnt = scan_para->long_time;} else {return;//计数不做操作}key_value = cur_key_value;} else {if (cur_key_value == NO_KEY) {                                   //按键被抬起if (scan_para->press_cnt < scan_para->long_time) {           //短按key_status = KEY_SHORT_UP;key_value = scan_para->last_key;} else if (scan_para->press_cnt >= scan_para->long_time) {   //长按/HOLD状态之后按键抬起;key_status = KEY_LONG_HLOD_UP;key_value = scan_para->last_key;}scan_para->last_key = cur_key_value;scan_para->press_cnt = 0;} else {scan_para->last_key = cur_key_value;scan_para->press_cnt = 0;return;}}//====== 按键处理//按键处理,中断时间不宜过久,这里仅仅是作按键的区分//建议依靠消息机制把按键信息发送到主循环处理按键scan_para->key_process(key_status, key_value);//根据键值和键状态,找到对应的消息值,把该消息放到消息池中
}//该函数内部调用了初始化时结构体内函数指针指定的两个函数:u8 io_get_key_value(void)和void io_key_process(u8 key_status, u8 key_value)/*----------------------------------------------------------------------------*/
/**@brief   IO按键值获取@param   void@return  按键值@note
*/
/*----------------------------------------------------------------------------*/
u8 io_get_key_value(void)
{//按键接地为按下if (!gpio_read(io_key_table[0])) {return 0;} else if (!gpio_read(io_key_table[1])) {return 1;}return NO_KEY;
}/*----------------------------------------------------------------------------*/
/**@brief   按键处理函数@param   key_status:  按键状态(短按、长按、连按、抬起等)@param   key_value:   按键值@return  viod@note    注意按键处理时间不宜过长
*/
/*----------------------------------------------------------------------------*/
void io_key_process(u8 key_status, u8 key_value)
{//按键处理,中断时间不宜过久,这里仅仅是作按键的区分//建议依靠消息机制把按键信息发送到主循环处理按键u16 key_msg;u8 err;key_msg = io_key_msg_table[key_value][key_status];err = msg_put_fifo(key_msg);if (err != 0) {puts("can not put msg");}
}

io_key_process()函数里使用了消息数组,也就是所谓的按键消息表u16 io_key_msg_table[ ][ ]。它是一个二维数组,2个维度分别是键值和键状态。所谓键值,就是用来分辨哪一个引脚对应按键按下了,键状态就是按下了时间多久,用于区分短按、长按、连续按、抬起等。该数组定义在key_driver.c文件里。

//io 按键消息表
u16 io_key_msg_table[2][4] = {//短按                  //长按                 //连按(hold)           //长、连按抬起[0] = {MSG_TEST_IO_KEY1_SHORT, MSG_TEST_IO_KEY1_LONG, MSG_TEST_IO_KEY1_HOLD, MSG_TEST_IO_KEY1_LONG_HOLD_UP},[1] = {MSG_TEST_IO_KEY2_SHORT, MSG_TEST_IO_KEY2_LONG, MSG_TEST_IO_KEY2_HOLD, MSG_TEST_IO_KEY2_LONG_HOLD_UP},
};

按键消息表u16 io_key_msg_table[ ][ ]里每一个元素对应一个消息值,该消息值在msg.h文件定义,是一个枚举类型。

//定义空消息
#define   NO_MSG       0xffff//消息表
enum {MSG_TEST_IO_KEY1_SHORT,MSG_TEST_IO_KEY1_LONG,MSG_TEST_IO_KEY1_HOLD,MSG_TEST_IO_KEY1_LONG_HOLD_UP,MSG_TEST_IO_KEY2_SHORT,MSG_TEST_IO_KEY2_LONG,MSG_TEST_IO_KEY2_HOLD,MSG_TEST_IO_KEY2_LONG_HOLD_UP,MSG_MAX = NO_MSG,
};

scan_para->key_process(key_status, key_value),用来根据键值和键状态,从二维数组按键消息表中找到对应的消息值,把该消息值放到消息池中。其中调用的函数为u8 msg_put_fifo(u16 msg)。

//消息池大小
#define   MAX_POOL   10//临界区管理
#define   MSG_ENTER_CRITICAL()    local_irq_disable()
#define   MSG_EXIT_CRITICAL()     local_irq_enable()u16 msg_pool[MAX_POOL];             //消息池
u16 msg_read = 0;                   //消息读位置
u16 msg_write = 0;                  //消息写位置
u16 msg_pool_residue = MAX_POOL;    //消息池剩余大小/*----------------------------------------------------------------------------*/
/**@brief   先进先出的方式将消息放入消息池@param   void@return  0:消息放入消息池成功-1:消息放放入消息池不成功@note
*/
/*----------------------------------------------------------------------------*/
u8 msg_put_fifo(u16 msg)
{MSG_ENTER_CRITICAL();//关闭中断if (msg_pool_residue == 0) {      //检查消息池子是否满了MSG_EXIT_CRITICAL();puts("err:msg pool is full!\n");return -1;}msg_pool[msg_write] = msg;        //把消息值放到消息池子msg_write++;if (msg_write == MAX_POOL) {msg_write = 0;}msg_pool_residue--;             //剩余容量-1MSG_EXIT_CRITICAL();        //打开中断return 0;
}

所谓消息池子,就是一个数组,u16 msg_pool[MAX_POOL];其中已经定义了MAX_POOL为10,也就是说最大只能装10个消息,如果再不取走,就停止新消息入池。取出消息需要使用u16 get_msg(void)函数。
按键扫描函数你会发现再用户主函数里并没有该按键扫描函数,它被放在timer的中断服务函数里了。timer1定时器2ms中断一次,然后每隔5次就扫描一次按键。该中断服务函数如下:


___interrupt
static void timer1_isr()
{const struct timer_target *p;JL_TIMER1->CON |= BIT(14);list_for_each_timer_target(p) {if (p->timer_handle) {p->timer_handle();}}++cnt;
#if (USE_KEY_DRIVER == 1) //10ms 按键扫描if ((cnt % 5) == 0) {key_driver_scan(&iokey_para);}
#endifif ((cnt % 50) == 0) {//100ms}if ((cnt % 100) == 0) {//200ms}if ((cnt % 250) == 0) {if (cnt == 500) {cnt = 0;}//500ms
#if 0powerdown_sleep();
#elseputchar('h');//这里每隔500ms串口打印一个字符h
#endif}/*lrc_scan();*/
}

3.消息获取

从上面程序可以看出,按键扫描后把消息值推送到了消息池子了,开发者并不需要直接在按键扫描函数里做任何处理工作,只要在user_main主函数里从消息池子里循环取出消息值即可,然后根据该值做相应的处理工作。
在主函数里循环取出消息并处理,使用的是user_msg_handler();函数,该函数被直接定义在user_main函数的上面。它使用了switch语句根据消息值做对应动作。
该函数并不是只能处理按键消息,所有其他消息均可以放入消息池子,然后交由该函数处理。


static void user_msg_handler()
{#if (DUAL_BANK_UPDATE_BY_UFW)update_download_opt();
#endif#if (USE_KEY_DRIVER == 1)int msg = get_msg();//从消息池子里取出一个消息if (msg == NO_MSG) {return;}switch (msg) {case MSG_TEST_IO_KEY1_SHORT://按键1短按log_info("IO_KEY1_SHOURT");break;case MSG_TEST_IO_KEY1_LONG:log_info("IO_KEY1___LONG");break;case MSG_TEST_IO_KEY1_HOLD:log_info("IO_KEY1_HOLD");break;case MSG_TEST_IO_KEY1_LONG_HOLD_UP:log_info("IO_KEY1_LONG_HOLD_UP");break;case MSG_TEST_IO_KEY2_SHORT://按键2短按log_info("IO_KEY2_SHOURT");break;case MSG_TEST_IO_KEY2_LONG:log_info("IO_KEY2___LONG");break;case MSG_TEST_IO_KEY2_HOLD:log_info("IO_KEY2_HOLD");break;case MSG_TEST_IO_KEY2_LONG_HOLD_UP:log_info("IO_KEY2_LONG_HOLD_UP");break;default:break;}#endif
}

消息处理函数调用了u16 get_msg(void)函数用于从消息池子里取出一个消息,该代码里消息池子使用的是先进先出方式。查看代码,可以发现官方还定义了一种先进后出的方式,该SDK未使用。

/*----------------------------------------------------------------------------*/
/**@brief   获取一个消息@param   void@return  消息值@note
*/
/*----------------------------------------------------------------------------*/
u16 get_msg(void)
{u16 msg = NO_MSG;MSG_ENTER_CRITICAL();if (msg_pool_residue < MAX_POOL) {msg = msg_pool[msg_read];//从消息池子里取出一个消息msg_read++;if (msg_read == MAX_POOL) {msg_read = 0;}msg_pool_residue++;//消息取走一个,消息池子剩余容量+1}MSG_EXIT_CRITICAL();return msg;
}

以上代码全部分析完了,可以看出官方SDK使用的是一种消息池的机制,用于各种事件的处理。该机制的好处在事件的处理很集中,可以进一步把消息发生源封装起了不对开发者可见,开发者只关心如何进行初始化和处理消息即可。另外还可以把消息池子升维,加上权重优先级等,这样各种形形色色的消息到了池子里,就按照优先级规则等进行取走,满足各种性质事件的处理需求。这就类似于DA14580的ke内核消息机制了。
写的有点多了,苏州高温40℃,继续搬砖。

杰理AC632N蓝牙芯片iokey使用解析(通用MCU版)相关推荐

  1. 【杰理AC632n】IIC-VCNL36826S

    -[杰理AC632n]IIC-VCNL36826S- < 代 码 > //<board_ac632n_demo_cfg.h> /*软件IIC设置*/ #define TCFG_ ...

  2. 小爱同学100个奇葩回复_杰理新一代蓝牙芯片将内置小爱同学,语音唤醒、降噪、连续对话加持蓝牙音箱...

    在 11 月 20 日召开的小米 AI 生态峰会暨小爱同学 3.0 发布会上,珠海杰理科技作为小爱同学在芯片端赋能的代表企业,带来了 3 款内置小爱同学标准 SDK 的第二代降噪.语音唤醒蓝牙芯片.据 ...

  3. 杰理的蓝牙芯片的key是什么?以及该如何添加key?杰理key文件原理

    目录 一.简介 关于杰理芯片的key文件,实际上 杰理芯片特有的一种机制,而这种机制就是存在于杰理芯片特有的架构,也是杰理公司延续将近10年的特点,估计以后也会是这种机制.具体为什么,请听我娓娓道来, ...

  4. 杰理AC632蓝牙芯片ADC

    蓝牙芯片有2种ADC,一种是普通多通道10bit ADC,一种是音频mic单通道16bit LADC. 普通ADC => 无DMA,单次采样单通道,带校准带参考电压,ADC采样值可以换算成绝对电 ...

  5. 【杰理AC632n】

    [1](添加/删减)定时器 static u32 my_led_timer1 = 0;//初始化 u16 usr_timer_add(void *priv, void (*func)(void *pr ...

  6. 杰理AC632N—ADC电压检测

    ADC电压检测-Note2 //**************app_power_manage.c**************// u32 my_adc_get_voltage(u32 ch); voi ...

  7. 2020手机音频解码芯片_2020杰理音频芯片全解析,14款音频产品代表作拆解汇总...

    珠海市杰理科技股份有限公司,成立于2010年.杰理科技主要从事射频智能终端.多媒体智能终端等系统级芯片(SoC)的研究.开发和销售. 杰理科技的芯片产品主要应用于AI智能音箱.蓝牙音箱.蓝牙耳机.智能 ...

  8. AC6928B 杰理蓝牙芯片 上电蓝牙模式 标准程序 免费修改功能

    实物图正面 封装TSSOP20 实物图反面 封装TSSOP20 特点1 杰理双模蓝牙芯片 内置flash 程序远程可修改 蓝牙5.0 8个io 可以随便用 支持u盘 或者 sd卡 使用 支持 音频输入 ...

  9. 杰理蓝牙AC692N按键调试

    1.首先在sdk_cfg.hl里打开#define __DEBUG,用于串口打印 2.在key.h打开KEY_AD_VDDIO_EN,采用AD按键,比KEY IO可以节约很多IO口 /*按键类型定义* ...

  10. 杰理强制升级工具4.0使用和原理解析

    用那个有8个挡位的烧录工具(4.0工具)的话,默认是走USB的,不是走串口,工具的DP接芯片的DP,工具的DM接芯片的DM,工具的+5V接芯片的VBAT(要保证能控制芯片供电通断才能从mask启动), ...

最新文章

  1. HDU 1242 Rescue BFS+优先队列
  2. CodeForces - 1307D Cow and Fields(最短路+思维)
  3. PAT_B_1060_Java(25分)
  4. Unix 下的 vim 如何使用系统剪贴板
  5. Anti-Screen Capture(Prevent Screen Captures)截屏与反截屏
  6. SQL Alias(别名)
  7. poj 1611 TheSuspects 并查集 连通图
  8. 理解 JavaScript 闭包{转载}
  9. 服务器不支持mysql_服务器不支持 MySql 数据库的解决方法
  10. 自己对Java的一些认识
  11. 范凯:对移动社交型app的一点思考
  12. MongoDB CookBook读书笔记之备份与恢复
  13. 提高COOKIE的安全性--相关解决方案
  14. linux主机路由命令,linux查看路由命令
  15. 软件测试之测试的分类
  16. 如何突破百度云下载速度限制
  17. 遗传算法优化SVM支持向量机分类预测的参数代码模型
  18. 内存超频时序怎么调_内存超频(ddr4内存时序多少为好)
  19. Squid代理权限的设置
  20. 超市网店营销与接口测试

热门文章

  1. 计算机视觉轮廓检测,轮廓检测
  2. 【CPM同步】连续相位调制(CPM)通信调制方法的载波同步和定时同步研究和matlab仿真
  3. 《编程之美》中买书问题算法。空间复杂度O(n),时间复杂度O(n),求挑战
  4. winrar捆绑软件
  5. Axure RP9入门介绍----小白
  6. ArcGIS API For Javascript之地图基本加载与显示,地图切换、缩放、定位、比例尺、鹰眼图、坐标显示、查询搜索功能实现
  7. 2021-2024年中国两轮电动车企业经营情况对比
  8. 功能完整的矢量图编辑软件——Boxy SVG for Mac 3.25.0激活版
  9. LaTex常用特殊符号对应表
  10. 雷电云手机无限雷币版v1.60