我们使用官方的ble_app_profile工程配置睡眠模式,睡眠模式使用扩展睡眠。
项目功能:上电广播10s,然后进入睡眠。按下按键唤醒,广播10s,然后进入睡眠,以此循环。
1、打开文件 user_config.h,将app_default_sleep_mode 赋值为 ARCH_EXT_SLEEP_ON
const static sleep_state_t app_default_sleep_mode = ARCH_EXT_SLEEP_ON;
2、为了方便管理,我们新建1个.c文件,把用户配置睡眠相关的代码放在里面。该文件命名为user_sleepmode_task.c,然后把这个文件加入到工程的user_app目录下。
user_sleepmode_task.c的内容如下:
#include "user_sleepmode_task.h"
#include "wkupct_quadec.h"
#include "gpio.h"
#include "custs1_task.h"
#include "user_periph_setup.h"
#include "rwip_config.h"
#include "app_api.h"
#include "user_callback_config.h"
#include "app_default_handlers.h"
#include "custs1_task.h"
#include "wkupct_quadec.h"
/**
****************************************************************************************
* @brief Sets button as wakeup trigger
* @return void
****************************************************************************************
*/
void app_button_enable(void)
{
app_easy_wakeup_set(app_wakeup_cb);
wkupct_register_callback(app_button_press_cb);
wkupct_enable_irq(WKUPCT_PIN_SELECT(GPIO_BUTTON_PORT, GPIO_BUTTON_PIN),
WKUPCT_PIN_POLARITY(GPIO_BUTTON_PORT, GPIO_BUTTON_PIN, WKUPCT_PIN_POLARITY_LOW),
1,
40);
}
/**
****************************************************************************************
* @brief Button press callback function. Registered in WKUPCT driver.
* @return void
****************************************************************************************
*/
void app_button_press_cb(void)
{
if (GetBits16(SYS_STAT_REG, PER_IS_DOWN))
{
periph_init();
}
if (arch_ble_ext_wakeup_get())
{
arch_set_sleep_mode(app_default_sleep_mode); //给出默认睡眠模式
arch_ble_force_wakeup(); //强制唤醒BLE
arch_ble_ext_wakeup_off(); //退出外部唤醒
app_easy_wakeup(); //发送消息给内核,内核回调app_wakeup_cb
}
}
/**
****************************************************************************************
* @brief Application wakeup callback function. Registerd in API message utility.
* @return void
****************************************************************************************
*/
void app_wakeup_cb(void)
{
// If state is not idle, ignore the message
if (ke_state_get(TASK_APP) == APP_CONNECTABLE)
{
user_app_adv_start();
}
}
头文件user_sleepmode_task.h,声明函数。
#ifndef _USER_SLEEPMODE_TASK_H_
#define _USER_SLEEPMODE_TASK_H_
#include "custs1_task.h"
#include "ke_msg.h"
#include <stdint.h>
void app_button_enable(void);
void app_button_press_cb(void);
void app_wakeup_cb(void);
#endif // _USER_SLEEPMODE_TASK_H_
解析函数:
a、app_button_enable函数主要用来使能按键中断,注册回调函数,睡眠前用户调用。
void app_button_enable(void)
{
app_easy_wakeup_set(app_wakeup_cb);
wkupct_register_callback(app_button_press_cb);
wkupct_enable_irq(WKUPCT_PIN_SELECT(GPIO_BUTTON_PORT, GPIO_BUTTON_PIN),
WKUPCT_PIN_POLARITY(GPIO_BUTTON_PORT, GPIO_BUTTON_PIN, WKUPCT_PIN_POLARITY_LOW),
1, 40);
}
wkupct_register_callback(app_button_press_cb);
参数2:WKUPCT_PIN_POLARITY(GPIO_BUTTON_PORT, GPIO_BUTTON_PIN, WKUPCT_PIN_POLARITY_LOW) 选择中断的极性(即上升沿/下降沿中断),如果需要多个管脚唤醒,则同样可以使用 | 来配置。
WKUPCT_PIN_POLARITY(GPIO_BUTTON_PORT, GPIO_BUTTON_PIN, WKUPCT_PIN_POLARITY_LOW) |
WKUPCT_PIN_POLARITY(GPIO_BUTTON2_PORT, GPIO_BUTTON2_PIN, WKUPCT_PIN_POLARITY_LOW)
参数3:事件数,即按键按下多少次才触发中断。按下一次就中断,则填1即可。
参数4:消抖时间,消除按键抖动,防止误唤醒。最大可以设置63ms。
由于我们使用按键唤醒,所以要定义按键管脚和配置按键的工作模式。
在user_periph_setup.h中定义按键的管脚
#define GPIO_BUTTON_PORT GPIO_PORT_2
#define GPIO_BUTTON_PIN GPIO_PIN_4
然后在 user_periph_setup.c文件的GPIO_reservations和set_pad_functions中配置按键管脚。
void GPIO_reservations(void)
{
RESERVE_GPIO(PUSH_BUTTON, GPIO_BUTTON_PORT, GPIO_BUTTON_PIN, PID_GPIO);
}
void set_pad_functions(void)
{
GPIO_ConfigurePin(GPIO_BUTTON_PORT, GPIO_BUTTON_PIN, INPUT_PULLUP, PID_GPIO, false);
}
b、app_button_press_cb 该函数是按键中断唤醒时由系统回调的。
如果外设已经掉电了,则调用periph_init()初始化外设
if (GetBits16(SYS_STAT_REG, PER_IS_DOWN))
{
periph_init();
}
如果是外部唤醒,则执行里面的内容。
if (arch_ble_ext_wakeup_get())
{
arch_set_sleep_mode(app_default_sleep_mode);
arch_ble_force_wakeup();
arch_ble_ext_wakeup_off();
app_easy_wakeup();
}
arch_set_sleep_mode(app_default_sleep_mode) 给出下次要进入哪种睡眠模式,这里我们使用默认的睡眠模式,也就是我们之前定义为ARCH_EXT_SLEEP_ON。如果唤醒后广播期间不想进入睡眠,只有在广播结束后才进入睡眠,则这里可以先把它关闭arch_set_sleep_mode(ARCH_SLEEP_OFF)。然后在广播完成后再调用arch_set_sleep_mode(app_default_sleep_mode)即可。
arch_ble_force_wakeup(); 强制唤醒BLE内核。DA14580是由 Cortex M0和BLE内核组成,所以按键唤醒时只是Cortex M0唤醒,而BLE内核则需要Cortex M0去唤醒。
arch_ble_ext_wakeup_off(); 退出外部唤醒,下次进入睡眠再使能外部唤醒。
app_easy_wakeup(); 发送消息给BLE内核,然后回调我们之前注册的函数app_wakeup_cb
c、app_wakeup_cb BLE内核唤醒回调函数
void app_wakeup_cb(void)
{
if (ke_state_get(TASK_APP) == APP_CONNECTABLE)
{
user_app_adv_start();
}
}
唤醒后就可以调用user_app_adv_start()开始广播了。这里需要注意要在 user_modules_config.h文件中将EXCLUDE_DLG_MSG配置为0,否则不会调用app_wakeup_cb 函数。
#define EXCLUDE_DLG_MSG (0)
到这里这3个函数就介绍完了,接下来就配置如何进入睡眠。
void user_app_adv_start(void)
{
app_adv_data_update_timer_used = app_easy_timer(APP_ADV_DATA_UPDATE_TO, adv_data_update_timer_cb);
struct gapm_start_advertise_cmd* cmd;
cmd = app_easy_gap_undirected_advertise_get_active();
mnf_data_update();
app_add_ad_struct(cmd, &mnf_data, sizeof(struct mnf_specific_data_ad_structure));
app_easy_gap_undirected_advertise_start();
}
来到我们的user_app_adv_start()函数可以看到这里创建1个广播定时器app_adv_data_update_timer_used,它的定时时间为APP_ADV_DATA_UPDATE_TO ,我们设置它的值为1000,由于内核定时器的时基为10ms,所以它的定时时间为1000 * 10ms = 10s。
#define APP_ADV_DATA_UPDATE_TO (1000)
它的回调函数是adv_data_update_timer_cb,找到该函数原型,可以看到,它是停止广播。因此我们可以清楚的明白广播的工作流程是:开始广播10S后,如果没有连接设备,则停止广播。
static void adv_data_update_timer_cb()
{
app_easy_gap_advertise_stop();
}
停止广播后系统会回调user_app_adv_undirect_complete函数。
void user_app_adv_undirect_complete(uint8_t status)
{
if (status == GAP_ERR_CANCELED)
{
arch_set_sleep_mode(app_default_sleep_mode);
arch_ble_ext_wakeup_on();
app_button_enable();
}
}
arch_set_sleep_mode(app_default_sleep_mode); 给出需要进入哪种睡眠模式
arch_ble_ext_wakeup_on(); 使能外部唤醒
app_button_enable(); 使能按键唤醒
好了,到这里整个睡眠相关的就配置完成了,可以运行程序看看效果。
注意:
1、如果你没有外接32.768k的晶振,则要配置为内部32.768k的RC,在da1458x_config_advanced.h中更改CFG_LP_CLK的值定义为LP_CLK_RCX20,如果使用外部晶振则定义为LP_CLK_XTAL32。
#define CFG_LP_CLK LP_CLK_RCX20
2、如果使用外部Flash,则睡眠前要让它进入低功耗模式。
在periph_init函数中调用3个函数,即最下面3个函数,使Flash进入低功耗模式。
void periph_init(void)
{
SetBits16(PMU_CTRL_REG, PERIPH_SLEEP, 0);
while (!(GetWord16(SYS_STAT_REG) & PER_IS_UP));
SetBits16(CLK_16M_REG, XTAL16_BIAS_SH_ENABLE, 1);
patch_func();
set_pad_functions();
SetBits16(SYS_CTRL_REG, PAD_LATCH_EN, 1);
spi_flash_enable(SPI_EN_GPIO_PORT, SPI_EN_GPIO_PIN);
spi_flash_power_down();
spi_release();
}
void set_pad_functions(void)
{
GPIO_ConfigurePin(SPI_EN_GPIO_PORT, SPI_EN_GPIO_PIN, OUTPUT, PID_SPI_EN, true);
GPIO_ConfigurePin(SPI_CLK_GPIO_PORT, SPI_CLK_GPIO_PIN, OUTPUT, PID_SPI_CLK, false);
GPIO_ConfigurePin(SPI_DO_GPIO_PORT, SPI_DO_GPIO_PIN, OUTPUT, PID_SPI_DO, false);
GPIO_ConfigurePin(SPI_DI_GPIO_PORT, SPI_DI_GPIO_PIN, INPUT, PID_SPI_DI, false);
GPIO_ConfigurePin(GPIO_BUTTON_PORT, GPIO_BUTTON_PIN, INPUT_PULLUP, PID_GPIO, false);
}
void GPIO_reservations(void)
{
RESERVE_GPIO(PUSH_BUTTON, GPIO_BUTTON_PORT, GPIO_BUTTON_PIN, PID_GPIO);
RESERVE_GPIO(SPI_EN, SPI_EN_GPIO_PORT, SPI_EN_GPIO_PIN, PID_SPI_EN);
RESERVE_GPIO(SPI_CLK, SPI_CLK_GPIO_PORT, SPI_CLK_GPIO_PIN, PID_SPI_CLK);
RESERVE_GPIO(SPI_DO, SPI_DO_GPIO_PORT, SPI_DO_GPIO_PIN, PID_SPI_DO);
RESERVE_GPIO(SPI_DI, SPI_DI_GPIO_PORT, SPI_DI_GPIO_PIN, PID_SPI_DI);
由于我的工程在运行时没有用到Falsh,所以我在periph_init中就把它关闭了。如果你的工程需要在工作时使用Flash则可以在进入睡眠前才调用这3个函数即可。
3、内核定时器唤醒
在user_app_adv_undirect_complete函数中添加唤醒定时器
void user_app_adv_undirect_complete(uint8_t status)
{
// If advertising was canceled then update advertising data and start advertising again
if (status == GAP_ERR_CANCELED)
{
arch_set_sleep_mode(app_default_sleep_mode); //给出睡眠模式
arch_ble_ext_wakeup_on(); //使能外部唤醒
app_button_enable(); //配置外部唤醒按键/事件
app_easy_timer(500, timer_wake_up_cb); //定时器唤醒500*10=5S
}}
写定时回调函数timer_wake_up_cb
static void timer_wake_up_cb()
{
app_button_press_cb();
}
实验目的:DA14580睡眠5秒后自动唤醒。