From 59f34461d76867e371ec0b9d3df4f63e7e6c2128 Mon Sep 17 00:00:00 2001 From: wangmengyang Date: Tue, 11 Dec 2018 16:49:01 +0800 Subject: [PATCH] component/bt: bugfix of bluetooth modem sleep not being able to work with Dynamic Frequency Scaling 1. start an esp_timer on entering sleep to acquire pm_lock before wake up from modem sleep 2. decrease the clock division of XTAL fed to bluetooth low power clock from 32us to 2us period to allow to work under 240MHz Max CPU frequency 3. decrease the minimum sleep duration threshold to allow shorter bluetooth modem sleep period, especially for BLE with short connection interval 4. reconfigure bluetooth baseband(BT-BB) settings after PHY/RF init upon waking up from modem sleep to avoid packet RX/TX performance degradation --- components/bt/Kconfig | 4 +- components/bt/bt.c | 194 ++++++++++++++++++------ components/bt/include/esp_bt.h | 1 - components/bt/lib | 2 +- components/esp32/include/esp_phy_init.h | 6 + components/esp32/phy_init.c | 23 ++- 6 files changed, 175 insertions(+), 55 deletions(-) diff --git a/components/bt/Kconfig b/components/bt/Kconfig index c8e63ad9e..4aad362ab 100644 --- a/components/bt/Kconfig +++ b/components/bt/Kconfig @@ -136,7 +136,6 @@ config BTDM_CONTROLLER_MODEM_SLEEP default y help Enable/disable bluetooth controller low power mode. - Note that currently there is problem in the combination use of bluetooth modem sleep and Dynamic Frequency Scaling(DFS). So do not enable DFS if bluetooth modem sleep is in use. choice BTDM_MODEM_SLEEP_MODE prompt "Bluetooth Modem sleep mode" @@ -162,6 +161,9 @@ choice BTDM_LOW_POWER_CLOCK config BTDM_LPCLK_SEL_MAIN_XTAL bool "Main crystal" + help + Main crystal can be used as low power clock for bluetooth modem sleep. If this option is selected, bluetooth + modem sleep can work under Dynamic Frequency Scaling(DFS) enabled, but cannot work when light sleep is enabled. config BTDM_LPCLK_SEL_EXT_32K_XTAL bool "External 32kHz crystal" depends on ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL diff --git a/components/bt/bt.c b/components/bt/bt.c index 7d8cfed4a..afecc9a5d 100644 --- a/components/bt/bt.c +++ b/components/bt/bt.c @@ -74,8 +74,9 @@ #define BTDM_LPCLK_SEL_RTC_SLOW (2) #define BTDM_LPCLK_SEL_8M (3) -/* Sleep duration */ -#define BTDM_MIN_SLEEP_DURATION (20) +/* Sleep and wakeup interval control */ +#define BTDM_MIN_SLEEP_DURATION (12) // threshold of interval in slots to allow to fall into modem sleep +#define BTDM_MODEM_WAKE_UP_DELAY (4) // delay in slots of modem wake up procedure, including re-enable PHY/RF #define BT_DEBUG(...) #define BT_API_CALL_CHECK(info, api_call, ret) \ @@ -160,8 +161,11 @@ struct osi_funcs_t { uint32_t (* _btdm_lpcycles_2_us)(uint32_t cycles); uint32_t (* _btdm_us_2_lpcycles)(uint32_t us); bool (* _btdm_sleep_check_duration)(uint32_t *slot_cnt); - void (* _btdm_sleep_enter)(void); - void (* _btdm_sleep_exit)(void); /* called from ISR */ + void (* _btdm_sleep_enter_phase1)(uint32_t lpcycles); /* called when interrupt is disabled */ + void (* _btdm_sleep_enter_phase2)(void); + void (* _btdm_sleep_exit_phase1)(void); /* called from ISR */ + void (* _btdm_sleep_exit_phase2)(void); /* called from ISR */ + void (* _btdm_sleep_exit_phase3)(void); /* called from task */ uint32_t _magic; }; @@ -180,7 +184,7 @@ extern int btdm_controller_enable(esp_bt_mode_t mode); extern void btdm_controller_disable(void); extern uint8_t btdm_controller_get_mode(void); extern const char *btdm_controller_get_compile_version(void); -extern void btdm_rf_bb_init(void); +extern void btdm_rf_bb_init_phase2(void); // shall be called after PHY/RF is enabled /* Sleep */ extern void btdm_controller_enable_sleep(bool enable); extern void btdm_controller_set_sleep_mode(uint8_t mode); @@ -256,8 +260,10 @@ static int IRAM_ATTR rand_wrapper(void); static uint32_t IRAM_ATTR btdm_lpcycles_2_us(uint32_t cycles); static uint32_t IRAM_ATTR btdm_us_2_lpcycles(uint32_t us); static bool IRAM_ATTR btdm_sleep_check_duration(uint32_t *slot_cnt); -static void IRAM_ATTR btdm_sleep_enter_wrapper(void); -static void IRAM_ATTR btdm_sleep_exit_wrapper(void); +static void btdm_sleep_enter_phase1_wrapper(uint32_t lpcycles); +static void btdm_sleep_enter_phase2_wrapper(void); +static void IRAM_ATTR btdm_sleep_exit_phase1_wrapper(void); +static void btdm_sleep_exit_phase3_wrapper(void); /* Local variable definition *************************************************************************** @@ -300,8 +306,11 @@ static const struct osi_funcs_t osi_funcs_ro = { ._btdm_lpcycles_2_us = btdm_lpcycles_2_us, ._btdm_us_2_lpcycles = btdm_us_2_lpcycles, ._btdm_sleep_check_duration = btdm_sleep_check_duration, - ._btdm_sleep_enter = btdm_sleep_enter_wrapper, - ._btdm_sleep_exit = btdm_sleep_exit_wrapper, + ._btdm_sleep_enter_phase1 = btdm_sleep_enter_phase1_wrapper, + ._btdm_sleep_enter_phase2 = btdm_sleep_enter_phase2_wrapper, + ._btdm_sleep_exit_phase1 = btdm_sleep_exit_phase1_wrapper, + ._btdm_sleep_exit_phase2 = NULL, + ._btdm_sleep_exit_phase3 = btdm_sleep_exit_phase3_wrapper, ._magic = OSI_MAGIC_VALUE, }; @@ -334,7 +343,8 @@ SemaphoreHandle_t btdm_queue_table_mux = NULL; #endif /* #if CONFIG_SPIRAM_USE_MALLOC */ /* Static variable declare */ -static bool btdm_bb_init_flag = false; +// timestamp when PHY/RF was switched on +static int64_t s_time_phy_rf_just_enabled = 0; static esp_bt_controller_status_t btdm_controller_status = ESP_BT_CONTROLLER_STATUS_IDLE; static portMUX_TYPE global_int_mux = portMUX_INITIALIZER_UNLOCKED; @@ -345,8 +355,22 @@ static uint8_t btdm_lpcycle_us_frac = 0; // number of fractional bit for btdm_lp #ifdef CONFIG_PM_ENABLE static esp_pm_lock_handle_t s_pm_lock; +static esp_timer_handle_t s_btdm_slp_tmr; +static QueueHandle_t s_pm_lock_sem = NULL; +static void btdm_slp_tmr_callback(void *arg); #endif +static inline void btdm_check_and_init_bb(void) +{ + /* init BT-BB if PHY/RF has been switched off since last BT-BB init */ + int64_t latest_ts = esp_phy_rf_get_on_ts(); + if (latest_ts != s_time_phy_rf_just_enabled || + s_time_phy_rf_just_enabled == 0) { + btdm_rf_bb_init_phase2(); + s_time_phy_rf_just_enabled = latest_ts; + } +} + #if CONFIG_SPIRAM_USE_MALLOC static bool btdm_queue_generic_register(const btdm_queue_item_t *queue) { @@ -729,7 +753,7 @@ static int IRAM_ATTR rand_wrapper(void) static uint32_t IRAM_ATTR btdm_lpcycles_2_us(uint32_t cycles) { - // The number of lp cycles should not lead to overflow. Thrs: 100s (for 32kHz freq) + // The number of lp cycles should not lead to overflow. Thrs: 100s // clock measurement is conducted uint64_t us = (uint64_t)btdm_lpcycle_us * cycles; us = (us + (1 << (btdm_lpcycle_us_frac - 1))) >> btdm_lpcycle_us_frac; @@ -754,16 +778,41 @@ static bool IRAM_ATTR btdm_sleep_check_duration(uint32_t *slot_cnt) if (*slot_cnt < BTDM_MIN_SLEEP_DURATION) { return false; } - /* wake up 3 slots in advance */ - *slot_cnt = *slot_cnt -3; + /* wake up in advance considering the delay in enabling PHY/RF */ + *slot_cnt -= BTDM_MODEM_WAKE_UP_DELAY; return true; } -static void IRAM_ATTR btdm_sleep_enter_wrapper(void) +static void btdm_sleep_enter_phase1_wrapper(uint32_t lpcycles) +{ +#ifdef CONFIG_PM_ENABLE + // start a timer to wake up and acquire the pm_lock before modem_sleep awakes + uint32_t us_to_sleep = btdm_lpcycles_2_us(lpcycles); + +#define BTDM_MIN_TIMER_UNCERTAINTY_US (1800) + assert(us_to_sleep > BTDM_MIN_TIMER_UNCERTAINTY_US); + // allow a maximum time uncertainty to be about 488ppm(1/2048) at least as clock drift + // and set the timer in advance + uint32_t uncertainty = (us_to_sleep >> 11); + if (uncertainty < BTDM_MIN_TIMER_UNCERTAINTY_US) { + uncertainty = BTDM_MIN_TIMER_UNCERTAINTY_US; + } + + if (esp_timer_start_once(s_btdm_slp_tmr, us_to_sleep - uncertainty) != ESP_OK) { + ESP_LOGW(BTDM_LOG_TAG, "timer start failed"); + } +#endif +} + +static void btdm_sleep_enter_phase2_wrapper(void) { if (btdm_controller_get_sleep_mode() == BTDM_MODEM_SLEEP_MODE_ORIG) { esp_modem_sleep_enter(MODEM_BLE_MODULE); esp_modem_sleep_enter(MODEM_CLASSIC_BT_MODULE); +#ifdef CONFIG_PM_ENABLE + esp_pm_lock_release(s_pm_lock); + semphr_give_wrapper(s_pm_lock_sem); +#endif } else if (btdm_controller_get_sleep_mode() == BTDM_MODEM_SLEEP_MODE_EVED) { esp_modem_sleep_enter(MODEM_BLE_MODULE); // pause bluetooth baseband @@ -771,11 +820,24 @@ static void IRAM_ATTR btdm_sleep_enter_wrapper(void) } } -static void IRAM_ATTR btdm_sleep_exit_wrapper(void) +static void IRAM_ATTR btdm_sleep_exit_phase1_wrapper(void) +{ +#ifdef CONFIG_PM_ENABLE + if (semphr_take_from_isr_wrapper(s_pm_lock_sem, NULL) == pdTRUE) { + esp_pm_lock_acquire(s_pm_lock); + } +#endif +} + +static void btdm_sleep_exit_phase3_wrapper(void) { if (btdm_controller_get_sleep_mode() == BTDM_MODEM_SLEEP_MODE_ORIG) { esp_modem_sleep_exit(MODEM_BLE_MODULE); esp_modem_sleep_exit(MODEM_CLASSIC_BT_MODULE); + btdm_check_and_init_bb(); +#ifdef CONFIG_PM_ENABLE + esp_timer_stop(s_btdm_slp_tmr); +#endif } else if (btdm_controller_get_sleep_mode() == BTDM_MODEM_SLEEP_MODE_EVED) { // resume bluetooth baseband periph_module_enable(PERIPH_BT_BASEBAND_MODULE); @@ -783,6 +845,15 @@ static void IRAM_ATTR btdm_sleep_exit_wrapper(void) } } +#ifdef CONFIG_PM_ENABLE +static void IRAM_ATTR btdm_slp_tmr_callback(void *arg) +{ + if (semphr_take_wrapper(s_pm_lock_sem, 0) == pdTRUE) { + esp_pm_lock_acquire(s_pm_lock); + } +} +#endif + bool esp_vhci_host_check_send_available(void) { return API_vhci_host_check_send_available(); @@ -791,6 +862,12 @@ bool esp_vhci_host_check_send_available(void) void esp_vhci_host_send_packet(uint8_t *data, uint16_t len) { if (!btdm_power_state_active()) { +#if CONFIG_PM_ENABLE + if (semphr_take_wrapper(s_pm_lock_sem, 0)) { + esp_pm_lock_acquire(s_pm_lock); + } + esp_timer_stop(s_btdm_slp_tmr); +#endif btdm_wakeup_request(); } API_vhci_host_send_packet(data, len); @@ -946,7 +1023,7 @@ esp_err_t esp_bt_mem_release(esp_bt_mode_t mode) esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg) { - BaseType_t ret; + esp_err_t err; uint32_t btdm_cfg_mask = 0; osi_funcs_p = (struct osi_funcs_t *)malloc_internal_wrapper(sizeof(struct osi_funcs_t)); @@ -987,27 +1064,36 @@ esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg) return ESP_ERR_INVALID_ARG; } -#ifdef CONFIG_PM_ENABLE - esp_err_t err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "bt", &s_pm_lock); - if (err != ESP_OK) { - return err; - } -#endif - ESP_LOGI(BTDM_LOG_TAG, "BT controller compile version [%s]", btdm_controller_get_compile_version()); #if CONFIG_SPIRAM_USE_MALLOC btdm_queue_table_mux = xSemaphoreCreateMutex(); - if (btdm_queue_table == NULL) { -#ifdef CONFIG_PM_ENABLE - esp_pm_lock_delete(s_pm_lock); - s_pm_lock = NULL; -#endif + if (btdm_queue_table_mux == NULL) { return ESP_ERR_NO_MEM; } memset(btdm_queue_table, 0, sizeof(btdm_queue_item_t) * BTDM_MAX_QUEUE_NUM); #endif +#ifdef CONFIG_PM_ENABLE + if ((err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "bt", &s_pm_lock)) != ESP_OK) { + goto error; + } + esp_timer_create_args_t create_args = { + .callback = btdm_slp_tmr_callback, + .arg = NULL, + .name = "btSlp" + }; + if ((err = esp_timer_create(&create_args, &s_btdm_slp_tmr)) != ESP_OK) { + goto error; + } + + s_pm_lock_sem = semphr_create_wrapper(1, 0); + if (s_pm_lock_sem == NULL) { + err = ESP_ERR_NO_MEM; + goto error; + } +#endif + btdm_controller_mem_init(); periph_module_enable(PERIPH_BT_MODULE); @@ -1019,10 +1105,10 @@ esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg) bool set_div_ret = false; #if CONFIG_BTDM_LPCLK_SEL_MAIN_XTAL select_src_ret = btdm_lpclk_select_src(BTDM_LPCLK_SEL_XTAL); - set_div_ret = btdm_lpclk_set_div(rtc_clk_xtal_freq_get() * 32 - 1); + set_div_ret = btdm_lpclk_set_div(rtc_clk_xtal_freq_get() * 2 - 1); assert(select_src_ret && set_div_ret); btdm_lpcycle_us_frac = RTC_CLK_CAL_FRACT; - btdm_lpcycle_us = 32 << btdm_lpcycle_us_frac; + btdm_lpcycle_us = 2 << (btdm_lpcycle_us_frac); #elif CONFIG_BTDM_LPCLK_SEL_EXT_32K_XTAL select_src_ret = btdm_lpclk_select_src(BTDM_LPCLK_SEL_XTAL32K); set_div_ret = btdm_lpclk_set_div(0); @@ -1040,17 +1126,31 @@ esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg) btdm_cfg_mask = btdm_config_mask_load(); - ret = btdm_controller_init(btdm_cfg_mask, cfg); - if (ret) { -#ifdef CONFIG_PM_ENABLE - esp_pm_lock_delete(s_pm_lock); - s_pm_lock = NULL; -#endif - return ESP_ERR_NO_MEM; + if (btdm_controller_init(btdm_cfg_mask, cfg) != 0) { + err = ESP_ERR_NO_MEM; + goto error; } btdm_controller_status = ESP_BT_CONTROLLER_STATUS_INITED; + return ESP_OK; + +error: +#ifdef CONFIG_PM_ENABLE + if (s_pm_lock != NULL) { + esp_pm_lock_delete(s_pm_lock); + s_pm_lock = NULL; + } + if (s_btdm_slp_tmr != NULL) { + esp_timer_delete(s_btdm_slp_tmr); + s_btdm_slp_tmr = NULL; + } + if (s_pm_lock_sem) { + semphr_delete_wrapper(s_pm_lock_sem); + s_pm_lock_sem = NULL; + } +#endif + return err; } esp_err_t esp_bt_controller_deinit(void) @@ -1063,6 +1163,16 @@ esp_err_t esp_bt_controller_deinit(void) periph_module_disable(PERIPH_BT_MODULE); +#ifdef CONFIG_PM_ENABLE + esp_pm_lock_delete(s_pm_lock); + s_pm_lock = NULL; + esp_timer_stop(s_btdm_slp_tmr); + esp_timer_delete(s_btdm_slp_tmr); + s_btdm_slp_tmr = NULL; + semphr_delete_wrapper(s_pm_lock_sem); + s_pm_lock_sem = NULL; +#endif + #if CONFIG_SPIRAM_USE_MALLOC vSemaphoreDelete(btdm_queue_table_mux); btdm_queue_table_mux = NULL; @@ -1076,10 +1186,6 @@ esp_err_t esp_bt_controller_deinit(void) btdm_lpcycle_us = 0; btdm_controller_set_sleep_mode(BTDM_MODEM_SLEEP_MODE_NONE); -#ifdef CONFIG_PM_ENABLE - esp_pm_lock_delete(s_pm_lock); - s_pm_lock = NULL; -#endif return ESP_OK; } @@ -1121,10 +1227,8 @@ esp_err_t esp_bt_controller_enable(esp_bt_mode_t mode) btdm_controller_enable_sleep(true); } - if (btdm_bb_init_flag == false) { - btdm_bb_init_flag = true; - btdm_rf_bb_init(); /* only initialise once */ - } + // inititalize bluetooth baseband + btdm_check_and_init_bb(); ret = btdm_controller_enable(mode); if (ret) { diff --git a/components/bt/include/esp_bt.h b/components/bt/include/esp_bt.h index 3115cacd0..b1dbc6467 100644 --- a/components/bt/include/esp_bt.h +++ b/components/bt/include/esp_bt.h @@ -405,7 +405,6 @@ esp_err_t esp_bt_mem_release(esp_bt_mode_t mode); * * For ORIG mode: * Bluetooth modem sleep is enabled in controller start up by default if CONFIG_BTDM_CONTROLLER_MODEM_SLEEP is set and "ORIG mode" is selected. In ORIG modem sleep mode, bluetooth controller will switch off some components and pause to work every now and then, if there is no event to process; and wakeup according to the scheduled interval and resume the work. It can also wakeup earlier upon external request using function "esp_bt_controller_wakeup_request". - * Note that currently there is problem in the combination use of bluetooth modem sleep and Dynamic Frequency Scaling(DFS). So do not enable DFS if bluetooth modem sleep is in use. * * @return * - ESP_OK : success diff --git a/components/bt/lib b/components/bt/lib index 1b7ad41d2..77bfaacde 160000 --- a/components/bt/lib +++ b/components/bt/lib @@ -1 +1 @@ -Subproject commit 1b7ad41d258dae8b60a1324bedd027de723c9a14 +Subproject commit 77bfaacde6b2cb306a91c5d51045f01bbe599c12 diff --git a/components/esp32/include/esp_phy_init.h b/components/esp32/include/esp_phy_init.h index 2a1a8ec7a..6783ff54b 100644 --- a/components/esp32/include/esp_phy_init.h +++ b/components/esp32/include/esp_phy_init.h @@ -226,6 +226,12 @@ esp_err_t esp_modem_sleep_register(modem_sleep_module_t module); */ esp_err_t esp_modem_sleep_deregister(modem_sleep_module_t module); +/** + * @brief Get the time stamp when PHY/RF was switched on + * @return return 0 if PHY/RF is never switched on. Otherwise return time in + * microsecond since boot when phy/rf was last switched on +*/ +int64_t esp_phy_rf_get_on_ts(void); #ifdef __cplusplus } #endif diff --git a/components/esp32/phy_init.c b/components/esp32/phy_init.c index d538f93bc..d72f60dc0 100644 --- a/components/esp32/phy_init.c +++ b/components/esp32/phy_init.c @@ -48,7 +48,7 @@ static _lock_t s_phy_rf_init_lock; /* Bit mask of modules needing to call phy_rf_init */ static uint32_t s_module_phy_rf_init = 0; -/* Whether modern sleep in turned on */ +/* Whether modem sleep is turned on */ static volatile bool s_is_phy_rf_en = false; /* Bit mask of modules needing to enter modem sleep mode */ @@ -64,6 +64,9 @@ static volatile bool s_is_modem_sleep_en = false; static _lock_t s_modem_sleep_lock; +/* time stamp updated when the PHY/RF is turned on */ +static int64_t s_phy_rf_en_ts = 0; + uint32_t IRAM_ATTR phy_enter_critical(void) { return portENTER_CRITICAL_NESTED(); @@ -74,16 +77,20 @@ void IRAM_ATTR phy_exit_critical(uint32_t level) portEXIT_CRITICAL_NESTED(level); } -static inline void phy_update_wifi_mac_time(bool en_clock_stopped) +int64_t esp_phy_rf_get_on_ts(void) +{ + return s_phy_rf_en_ts; +} + +static inline void phy_update_wifi_mac_time(bool en_clock_stopped, int64_t now) { static uint32_t s_common_clock_disable_time = 0; if (en_clock_stopped) { - s_common_clock_disable_time = esp_timer_get_time(); + s_common_clock_disable_time = (uint32_t)now; } else { if (s_common_clock_disable_time) { - uint64_t now = esp_timer_get_time(); - uint32_t diff = now - s_common_clock_disable_time; + uint32_t diff = (uint64_t)now - s_common_clock_disable_time; esp_wifi_internal_update_mac_time(diff); s_common_clock_disable_time = 0; @@ -135,8 +142,10 @@ esp_err_t esp_phy_rf_init(const esp_phy_init_data_t* init_data, esp_phy_calibrat } } if (s_is_phy_rf_en == true){ + // Update time stamp + s_phy_rf_en_ts = esp_timer_get_time(); // Update WiFi MAC time before WiFi/BT common clock is enabled - phy_update_wifi_mac_time( false ); + phy_update_wifi_mac_time(false, s_phy_rf_en_ts); // Enable WiFi/BT common peripheral clock periph_module_enable(PERIPH_WIFI_BT_COMMON_MODULE); phy_set_wifi_mode_only(0); @@ -229,7 +238,7 @@ esp_err_t esp_phy_rf_deinit(phy_rf_module_t module) // Disable PHY and RF. phy_close_rf(); // Update WiFi MAC time before disalbe WiFi/BT common peripheral clock - phy_update_wifi_mac_time(true); + phy_update_wifi_mac_time(true, esp_timer_get_time()); // Disable WiFi/BT common peripheral clock. Do not disable clock for hardware RNG periph_module_disable(PERIPH_WIFI_BT_COMMON_MODULE); }