extmod/nimble: Make stm32 and unix NimBLE ports use synchronous events.
This changes stm32 from using PENDSV to run NimBLE to use the MicroPython scheduler instead. This allows Python BLE callbacks to be invoked directly (and therefore synchronously) rather than via the ringbuffer. The NimBLE UART HCI and event processing now happens in a scheduled task every 128ms. When RX IRQ idle events arrive, it will also schedule this task to improve latency. There is a similar change for the unix port where the background thread now queues the scheduled task. Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
This commit is contained in:
committed by
Damien George
parent
81e92d3d6e
commit
61d1e4b01b
@@ -50,22 +50,67 @@
|
||||
|
||||
uint8_t mp_bluetooth_hci_cmd_buf[4 + 256];
|
||||
|
||||
STATIC int uart_fd = -1;
|
||||
|
||||
// Must be provided by the stack bindings (e.g. mpnimbleport.c or mpbtstackport.c).
|
||||
extern bool mp_bluetooth_hci_poll(void);
|
||||
|
||||
STATIC const useconds_t UART_POLL_INTERVAL_US = 1000;
|
||||
#if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
|
||||
|
||||
STATIC int uart_fd = -1;
|
||||
// For synchronous mode, we run all BLE stack code inside a scheduled task.
|
||||
// This task is scheduled periodically (every 1ms) by a background thread.
|
||||
|
||||
// Allows the stack to tell us that we should stop trying to schedule.
|
||||
extern bool mp_bluetooth_hci_active(void);
|
||||
|
||||
// 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;
|
||||
MICROPY_PY_BLUETOOTH_ENTER
|
||||
events_task_is_scheduled = false;
|
||||
MICROPY_PY_BLUETOOTH_EXIT
|
||||
mp_bluetooth_hci_poll();
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(run_events_scheduled_task_obj, run_events_scheduled_task);
|
||||
|
||||
#endif // MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
|
||||
|
||||
STATIC const useconds_t UART_POLL_INTERVAL_US = 1000;
|
||||
STATIC pthread_t hci_poll_thread_id;
|
||||
|
||||
STATIC void *hci_poll_thread(void *arg) {
|
||||
(void)arg;
|
||||
|
||||
DEBUG_printf("hci_poll_thread: starting\n");
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
|
||||
|
||||
events_task_is_scheduled = false;
|
||||
|
||||
while (mp_bluetooth_hci_active()) {
|
||||
MICROPY_PY_BLUETOOTH_ENTER
|
||||
if (!events_task_is_scheduled) {
|
||||
events_task_is_scheduled = mp_sched_schedule(MP_OBJ_FROM_PTR(&run_events_scheduled_task_obj), mp_const_none);
|
||||
}
|
||||
MICROPY_PY_BLUETOOTH_EXIT
|
||||
usleep(UART_POLL_INTERVAL_US);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// In asynchronous (i.e. ringbuffer) mode, we run the BLE stack directly from the thread.
|
||||
// This will return false when the stack is shutdown.
|
||||
while (mp_bluetooth_hci_poll()) {
|
||||
usleep(UART_POLL_INTERVAL_US);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
DEBUG_printf("hci_poll_thread: stopped\n");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -122,6 +167,11 @@ int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) {
|
||||
|
||||
DEBUG_printf("mp_bluetooth_hci_uart_init (unix)\n");
|
||||
|
||||
if (uart_fd != -1) {
|
||||
DEBUG_printf("mp_bluetooth_hci_uart_init: already active\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
char uart_device_name[256] = "/dev/ttyUSB0";
|
||||
|
||||
char *path = getenv("MICROPYBTUART");
|
||||
|
||||
@@ -47,38 +47,28 @@ bool mp_bluetooth_hci_poll(void) {
|
||||
}
|
||||
|
||||
if (mp_bluetooth_nimble_ble_state >= MP_BLUETOOTH_NIMBLE_BLE_STATE_WAITING_FOR_SYNC) {
|
||||
|
||||
// Pretend like we're running in IRQ context (i.e. other things can't be running at the same time).
|
||||
mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
|
||||
|
||||
// Ask NimBLE to process UART data.
|
||||
mp_bluetooth_nimble_hci_uart_process();
|
||||
|
||||
// Run pending background operations and events, but only after HCI sync.
|
||||
// Run any timers.
|
||||
mp_bluetooth_nimble_os_callout_process();
|
||||
mp_bluetooth_nimble_os_eventq_run_all();
|
||||
|
||||
MICROPY_END_ATOMIC_SECTION(atomic_state);
|
||||
// Process incoming UART data, and run events as they are generated.
|
||||
mp_bluetooth_nimble_hci_uart_process(true);
|
||||
|
||||
// Run any remaining events (e.g. if there was no UART data).
|
||||
mp_bluetooth_nimble_os_eventq_run_all();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mp_bluetooth_hci_active(void) {
|
||||
return mp_bluetooth_nimble_ble_state != MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF;
|
||||
}
|
||||
|
||||
// Extra port-specific helpers.
|
||||
void mp_bluetooth_nimble_hci_uart_wfi(void) {
|
||||
// DEBUG_printf("mp_bluetooth_nimble_hci_uart_wfi\n");
|
||||
// TODO: this should do a select() on the uart_fd.
|
||||
}
|
||||
|
||||
uint32_t mp_bluetooth_nimble_hci_uart_enter_critical(void) {
|
||||
// DEBUG_printf("mp_bluetooth_nimble_hci_uart_enter_critical\n");
|
||||
MICROPY_PY_BLUETOOTH_ENTER
|
||||
return atomic_state; // Always 0xffffffff
|
||||
}
|
||||
|
||||
void mp_bluetooth_nimble_hci_uart_exit_critical(uint32_t atomic_state) {
|
||||
MICROPY_PY_BLUETOOTH_EXIT
|
||||
// DEBUG_printf("mp_bluetooth_nimble_hci_uart_exit_critical\n");
|
||||
// This is called while NimBLE is waiting in ble_npl_sem_pend, i.e. waiting for an HCI ACK.
|
||||
// Do not need to run events here, only processing incoming HCI data.
|
||||
mp_bluetooth_nimble_hci_uart_process(false);
|
||||
}
|
||||
|
||||
#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE
|
||||
|
||||
Reference in New Issue
Block a user