stm32/mpbthciport: Change from systick to soft-timer for BT scheduling.

Instead of using systick the BT subsystem is now scheduled using a soft
timer.  This means it is scheduled only when it is enabled.

Signed-off-by: Damien George <damien@micropython.org>
This commit is contained in:
Damien George 2021-05-12 17:18:22 +10:00
parent 60f1f76984
commit 74c2c31811
7 changed files with 111 additions and 39 deletions

View File

@ -54,6 +54,7 @@
#endif
#include "boardctrl.h"
#include "mpbthciport.h"
#include "mpu.h"
#include "rfcore.h"
#include "systick.h"
@ -440,8 +441,7 @@ void stm32_main(uint32_t reset_mode) {
systick_enable_dispatch(SYSTICK_DISPATCH_LWIP, mod_network_lwip_poll_wrapper);
#endif
#if MICROPY_PY_BLUETOOTH
extern void mp_bluetooth_hci_systick(uint32_t ticks_ms);
systick_enable_dispatch(SYSTICK_DISPATCH_BLUETOOTH_HCI, mp_bluetooth_hci_systick);
mp_bluetooth_hci_init();
#endif
#if MICROPY_PY_NETWORK_CYW43

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Damien P. George
* Copyright (c) 2018-2021 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -28,7 +28,8 @@
#include "py/mphal.h"
#include "extmod/mpbthci.h"
#include "extmod/modbluetooth.h"
#include "systick.h"
#include "mpbthciport.h"
#include "softtimer.h"
#include "pendsv.h"
#include "lib/utils/mpirq.h"
@ -38,22 +39,48 @@
uint8_t mp_bluetooth_hci_cmd_buf[4 + 256];
// Must be provided by the stack bindings (e.g. mpnimbleport.c or mpbtstackport.c).
// Request new data from the uart and pass to the stack, and run pending events/callouts.
extern void mp_bluetooth_hci_poll(void);
// Soft timer for scheduling a HCI poll.
STATIC soft_timer_entry_t mp_bluetooth_hci_soft_timer;
// Hook for pendsv poller to run this periodically every 128ms
#define BLUETOOTH_HCI_TICK(tick) (((tick) & ~(SYSTICK_DISPATCH_NUM_SLOTS - 1) & 0x7f) == 0)
#if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
// Prevent double-enqueuing of the scheduled task.
STATIC volatile bool events_task_is_scheduled;
#endif
// This is called by soft_timer and executes at IRQ_PRI_PENDSV.
STATIC void mp_bluetooth_hci_soft_timer_callback(soft_timer_entry_t *self) {
mp_bluetooth_hci_poll_now();
}
void mp_bluetooth_hci_init(void) {
#if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
events_task_is_scheduled = false;
#endif
soft_timer_static_init(
&mp_bluetooth_hci_soft_timer,
SOFT_TIMER_MODE_ONE_SHOT,
0,
mp_bluetooth_hci_soft_timer_callback
);
}
STATIC void mp_bluetooth_hci_start_polling(void) {
#if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
events_task_is_scheduled = false;
#endif
mp_bluetooth_hci_poll_now();
}
void mp_bluetooth_hci_poll_in_ms(uint32_t ms) {
soft_timer_reinsert(&mp_bluetooth_hci_soft_timer, ms);
}
#if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
// For synchronous mode, we run all BLE stack code inside a scheduled task.
// This task is scheduled periodically (every 128ms) via SysTick, or
// This task is scheduled periodically via a soft timer, or
// immediately on HCI UART RXIDLE.
// Prevent double-enqueuing of the scheduled task.
STATIC volatile bool events_task_is_scheduled = false;
STATIC mp_obj_t run_events_scheduled_task(mp_obj_t none_in) {
(void)none_in;
events_task_is_scheduled = false;
@ -65,23 +92,20 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(run_events_scheduled_task_obj, run_events_sched
// Called periodically (systick) or directly (e.g. UART RX IRQ) in order to
// request that processing happens ASAP in the scheduler.
void mp_bluetooth_hci_systick(uint32_t ticks_ms) {
if (events_task_is_scheduled) {
return;
}
if (ticks_ms == 0 || BLUETOOTH_HCI_TICK(ticks_ms)) {
void mp_bluetooth_hci_poll_now(void) {
if (!events_task_is_scheduled) {
events_task_is_scheduled = mp_sched_schedule(MP_OBJ_FROM_PTR(&run_events_scheduled_task_obj), mp_const_none);
if (!events_task_is_scheduled) {
// The schedule queue is full, set callback to try again soon.
mp_bluetooth_hci_poll_in_ms(5);
}
}
}
#else // !MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
// Called periodically (systick) or directly (e.g. uart irq).
void mp_bluetooth_hci_systick(uint32_t ticks_ms) {
if (ticks_ms == 0 || BLUETOOTH_HCI_TICK(ticks_ms)) {
pendsv_schedule_dispatch(PENDSV_DISPATCH_BLUETOOTH_HCI, mp_bluetooth_hci_poll);
}
void mp_bluetooth_hci_poll_now(void) {
pendsv_schedule_dispatch(PENDSV_DISPATCH_BLUETOOTH_HCI, mp_bluetooth_hci_poll);
}
#endif
@ -104,14 +128,13 @@ int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) {
DEBUG_printf("mp_bluetooth_hci_uart_init (stm32 rfcore)\n");
#if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
events_task_is_scheduled = false;
#endif
rfcore_ble_init();
hci_uart_rx_buf_cur = 0;
hci_uart_rx_buf_len = 0;
// Start the HCI polling to process any initial events/packets.
mp_bluetooth_hci_start_polling();
return 0;
}
@ -163,7 +186,6 @@ int mp_bluetooth_hci_uart_readchar(void) {
/******************************************************************************/
// HCI over UART
#include "pendsv.h"
#include "uart.h"
pyb_uart_obj_t mp_bluetooth_hci_uart_obj;
@ -173,7 +195,7 @@ static uint8_t hci_uart_rxbuf[768];
mp_obj_t mp_uart_interrupt(mp_obj_t self_in) {
// Queue up the scheduler to run the HCI UART and event processing ASAP.
mp_bluetooth_hci_systick(0);
mp_bluetooth_hci_poll_now();
return mp_const_none;
}
@ -182,10 +204,6 @@ MP_DEFINE_CONST_FUN_OBJ_1(mp_uart_interrupt_obj, mp_uart_interrupt);
int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) {
DEBUG_printf("mp_bluetooth_hci_uart_init (stm32)\n");
#if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
events_task_is_scheduled = false;
#endif
// bits (8), stop (1), parity (none) and flow (rts/cts) are assumed to match MYNEWT_VAL_BLE_HCI_UART_ constants in syscfg.h.
mp_bluetooth_hci_uart_obj.base.type = &pyb_uart_type;
mp_bluetooth_hci_uart_obj.uart_id = port;
@ -208,6 +226,9 @@ int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) {
mp_bluetooth_hci_uart_irq_obj.ishard = true;
uart_irq_config(&mp_bluetooth_hci_uart_obj, true);
// Start the HCI polling to process any initial events/packets.
mp_bluetooth_hci_start_polling();
return 0;
}

42
ports/stm32/mpbthciport.h Normal file
View File

@ -0,0 +1,42 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_STM32_MPBTHCIPORT_H
#define MICROPY_INCLUDED_STM32_MPBTHCIPORT_H
// Initialise the HCI subsystem (should be called once, early on).
void mp_bluetooth_hci_init(void);
// Poll the HCI now, or after a certain timeout.
void mp_bluetooth_hci_poll_now(void);
void mp_bluetooth_hci_poll_in_ms(uint32_t ms);
// Must be provided by the stack bindings (e.g. mpnimbleport.c or mpbtstackport.c).
// Request new data from the uart and pass to the stack, and run pending events/callouts.
// This is a low-level function and should not be called directly, use
// mp_bluetooth_hci_poll_now/mp_bluetooth_hci_poll_in_ms instead.
void mp_bluetooth_hci_poll(void);
#endif // MICROPY_INCLUDED_STM32_MPBTHCIPORT_H

View File

@ -38,6 +38,7 @@
#include "extmod/mpbthci.h"
#include "extmod/btstack/btstack_hci_uart.h"
#include "extmod/btstack/modbluetooth_btstack.h"
#include "mpbthciport.h"
// The IRQ functionality in btstack_run_loop_embedded.c is not used, so the
// following three functions are empty.
@ -75,6 +76,10 @@ void mp_bluetooth_hci_poll(void) {
// Call the BTstack run loop.
btstack_run_loop_embedded_execute_once();
// Call this function again in 128ms to check for new events.
// TODO: improve this by only calling back when needed.
mp_bluetooth_hci_poll_in_ms(128);
}
void mp_bluetooth_btstack_port_init(void) {

View File

@ -40,6 +40,7 @@
#include "extmod/modbluetooth.h"
#include "extmod/nimble/modbluetooth_nimble.h"
#include "extmod/nimble/hal/hal_uart.h"
#include "mpbthciport.h"
// Get any pending data from the UART and send it to NimBLE's HCI buffers.
// Any further processing by NimBLE will be run via its event queue.
@ -56,6 +57,12 @@ void mp_bluetooth_hci_poll(void) {
// Run any remaining events (e.g. if there was no UART data).
mp_bluetooth_nimble_os_eventq_run_all();
}
if (mp_bluetooth_nimble_ble_state != MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) {
// Call this function again in 128ms to check for new events.
// TODO: improve this by only calling back when needed.
mp_bluetooth_hci_poll_in_ms(128);
}
}
// --- Port-specific helpers for the generic NimBLE bindings. -----------------

View File

@ -32,6 +32,7 @@
#include "py/mphal.h"
#include "py/runtime.h"
#include "extmod/modbluetooth.h"
#include "mpbthciport.h"
#include "rtc.h"
#include "rfcore.h"
@ -714,8 +715,7 @@ void IPCC_C1_RX_IRQHandler(void) {
#if MICROPY_PY_BLUETOOTH
// Queue up the scheduler to process UART data and run events.
extern void mp_bluetooth_hci_systick(uint32_t ticks_ms);
mp_bluetooth_hci_systick(0);
mp_bluetooth_hci_poll_now();
#endif
}

View File

@ -37,9 +37,6 @@ enum {
#if MICROPY_PY_NETWORK && MICROPY_PY_LWIP
SYSTICK_DISPATCH_LWIP,
#endif
#if MICROPY_PY_BLUETOOTH
SYSTICK_DISPATCH_BLUETOOTH_HCI,
#endif
SYSTICK_DISPATCH_MAX
};