持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第28天,点击查看活动详情
本文介绍 BLE中简单外设模块研究。
SDK 中封装了 LED 和 BUTTON 外设接口,很多例程都使用了这些接口。本文针对这2个外设进行简单的跟踪。并不区别是否带BLE。
板级初始化
bsp_xx实现了板级的驱动,通过宏并屏蔽具体硬件。对于nrf52832,在文件components\boards\pca10040.h
定义一些外设的引脚。在components\boards\boards.c
实现了初始化及LED、BUTTON底层控制操作。
对于LED和按键,已默认有引脚的定义,在实际中则使用索引,比如第0号LED为是LED_1,按键亦然。
LED定义:
#define LEDS_NUMBER 4
#define LED_START 17
#define LED_1 17
#define LED_2 18
#define LED_3 19
#define LED_4 20
#define LED_STOP 20
#define LEDS_ACTIVE_STATE 0
#define LEDS_INV_MASK LEDS_MASK
#define LEDS_LIST { LED_1, LED_2, LED_3, LED_4 }
#define BSP_LED_0 LED_1
#define BSP_LED_1 LED_2
#define BSP_LED_2 LED_3
#define BSP_LED_3 LED_4
按键定义:
#define BUTTONS_NUMBER 4
#define BUTTON_START 13
#define BUTTON_1 13
#define BUTTON_2 14
#define BUTTON_3 15
#define BUTTON_4 16
#define BUTTON_STOP 16
#define BUTTON_PULL NRF_GPIO_PIN_PULLUP
#define BUTTONS_ACTIVE_STATE 0
#define BUTTONS_LIST { BUTTON_1, BUTTON_2, BUTTON_3, BUTTON_4 }
#define BSP_BUTTON_0 BUTTON_1
#define BSP_BUTTON_1 BUTTON_2
#define BSP_BUTTON_2 BUTTON_3
#define BSP_BUTTON_3 BUTTON_4
板级初始化有2个:bsp_board_init、bsp_init。怎么理解?
调用:
bsp_board_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS);
bsp_board_init(BSP_INIT_LEDS);
bsp_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS,
ant_advanced_burst_bsp_evt_handler);
bsp_init(BSP_INIT_LEDS, NULL);
板级初始化实现文件:components\boards\boards.c
bsp_board_init
--> 根据标志,调用 bsp_board_leds_init
--> 根据标志,调用 bsp_board_buttons_init
bsp_init
函数会初始LED和按键。并初始化定时器。
led定时事件:
app_timer_create(&m_bsp_leds_tmr, APP_TIMER_MODE_SINGLE_SHOT, leds_timer_handler);
事件处理函数leds_timer_handler
调用leds_timer_handler
,其调用bsp_led_indication
设置特定的指示状态(有对应的宏,例程中会使用到)。根据不同状态,亮灭灯,获取新的定时参数,再重启定时器
BUTTON定时事件:
app_timer_create(&m_bsp_button_tmr, APP_TIMER_MODE_SINGLE_SHOT, button_timer_handler);
button_timer_handler
--> bsp_button_event_handler
LED灯
实现文件:components\libraries\bsp.c。
使用方法
LED初始化:bsp_board_init
亮灭LED灯:bsp_board_led_on bsp_board_led_off 参数为LED索引
全部亮灭:bsp_board_leds_on bsp_board_leds_off (内部会遍历LED数组)
反转LED:bsp_board_led_invert
源码跟踪
应用程序调用bsp_indication_set
即可,内部实现了定时器任务。第一次调用该函数后,就会进入状态机。
bsp_indication_set
--> bsp_led_indication 根据状态执行不同的处理。比如扫描状态,
先获取表示该状态的GPIO(LED1)高低电平,如高,则off,反低则on,
期间计算下次延时时长,保存状态,再启动定时器,实现闪烁。
流程跟踪:
bsp_init
--> 创建定时器 m_bsp_leds_tmr, 回调函数为 leds_timer_handler,
--> 在回调函数中,调用 bsp_led_indication。
按键 BUTTON
在文件components\libraries\button\app_button.c
中实现。
使用方法
定义结构体,再初始化之。结构体定义引脚以及处理函数,初始化第三个参数为定时检测时间间隔。
自注:是否可多个?
static void buttons_init(void)
{
static app_button_cfg_t buttons[] =
{
{LEDBUTTON_BUTTON, false, BUTTON_PULL, button_event_handler}
};
err_code = app_button_init(buttons, ARRAY_SIZE(buttons),
BUTTON_DETECTION_DELAY);
}
处理函数:
static void button_event_handler(uint8_t pin_no, uint8_t button_action)
{
ret_code_t err_code;
switch (pin_no)
{
case LEDBUTTON_BUTTON:
NRF_LOG_INFO("Send button state change.");
default:
APP_ERROR_HANDLER(pin_no);
break;
}
}
static void button_event_handler(uint8_t pin_no, uint8_t button_action)
{
NRF_LOG_INFO("button_event_handler: pin_no: %u, button_action:%u\r\n",pin_no, button_action);
if(TILE_BUTTON == pin_no && true == button_action)
{
NRF_LOG_INFO("button press detected successfully\r\n");
tile_button_was_pressed();
}
}
获取状态:app_button_is_pushed,参数为按键索引号。返回值为true表示按下,反之释放。
使用、禁止:app_button_enable app_button_disable
源码跟踪
结构体:
typedef struct
{
uint8_t pin_no; /**< Pin to be used as a button. */
uint8_t active_state; /**< APP_BUTTON_ACTIVE_HIGH or APP_BUTTON_ACTIVE_LOW. */
#if defined(BUTTON_HIGH_ACCURACY_ENABLED) && (BUTTON_HIGH_ACCURACY_ENABLED == 1)
bool hi_accuracy; /**< True if GPIOTE high accuracy (IN_EVENT) is used. */
#endif
nrf_gpio_pin_pull_t pull_cfg; /**< Pull-up or -down configuration. */
app_button_handler_t button_handler; /**< Handler to be called when button is pushed. */
} app_button_cfg_t;
自定义回调函数:button_handler
代码跟踪:
app_button_init
--> 初始化 gpiote nrf_drv_gpiote_init
--> 赋值mp_buttons保存之
--> nrf_drv_gpiote_in_init
--> 创建定时器 m_detection_delay_timer_id,处理函数:detection_delay_timeout_handler
evt_handle
--> 遍历 mp_buttons,调用 evt_handle
--> 用state_get获取按键状态
--> 调用state_set设置状态,如 BTN_PRESSED
--> usr_event,如 APP_BUTTON_PUSH
--> 调用用户自定义的函数,即结构体 button_handler 指针函数
小结:定时检测按键状态,内部设置状态,但如何处理,在自定义函数响应。
按键状态:
typedef enum {
BTN_IDLE,
BTN_PRESS_ARMED,
BTN_PRESS_DETECTED,
BTN_PRESSED,
BTN_RELEASE_DETECTED
} btn_state_t;
按键的动作:
APP_BUTTON_PUSH APP_BUTTON_RELEASE APP_BUTTON_ACTIVE_HIGH APP_BUTTON_ACTIVE_LOW
使用实例
主机例程中,在扫描开始函数中调用bsp_indication_set(BSP_INDICATE_SCANNING);
,在连接事件中调用bsp_indication_set(BSP_INDICATE_CONNECTED);
。即:底层实现了某个状态的功能,但何时使用则可自由控制。但状态指标是固定的,即广播或扫描时LED1闪烁。
如完全自由控制,则可调用bsp_board_led_on、bsp_board_led_off直接控制。可再做一个定时器。
按键使能,可在某个状态后调用app_button_enable
。比如,只有连接上,按键才能使能(如按键发送数据)。连接断开,禁止app_button_disable
。
SDK中 按键1有特殊用途。按下为事件 BSP_EVENT_KEY_0,释放为事件 BSP_EVENT_SLEEP。应该在某处设置了,暂未查到。
可以用 bsp_event_to_button_action_assign 再次配置,参数依次为:索引、按下/释放动作、事件类型。但不能在bsp_init初始化。
高阶
SDK的例程已经实现了一套LED、BUTTON的处理接口。根据模块源码,可以单独再实现一套自定义的 LED 状态机。但要注意不能再使用原来的初始化接口。