diff options
| -rw-r--r-- | ports/alif/alif.mk | 4 | ||||
| -rw-r--r-- | ports/alif/cyw43_configport.h | 18 | ||||
| -rw-r--r-- | ports/alif/main.c | 9 | ||||
| -rw-r--r-- | ports/alif/mpbthciport.c | 140 | ||||
| -rw-r--r-- | ports/alif/mpbthciport.h | 46 | ||||
| -rw-r--r-- | ports/alif/mpconfigport.h | 6 | ||||
| -rw-r--r-- | ports/alif/mpnimbleport.c | 78 | ||||
| -rw-r--r-- | ports/alif/mpnimbleport.h | 32 |
8 files changed, 333 insertions, 0 deletions
diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index 179df67a5..eee27b3b7 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -142,6 +142,10 @@ ifeq ($(MICROPY_SSL_MBEDTLS),1) SRC_C += mbedtls/mbedtls_port.c endif +ifeq ($(MICROPY_PY_BLUETOOTH),1) +SRC_C += mpbthciport.c mpnimbleport.c +endif + ifeq ($(MICROPY_FLOAT_IMPL),float) LIBM_SRC_C += $(SRC_LIB_LIBM_C) LIBM_SRC_C += $(SRC_LIB_LIBM_SQRT_HW_C) diff --git a/ports/alif/cyw43_configport.h b/ports/alif/cyw43_configport.h index b7e4ad702..4b5cce671 100644 --- a/ports/alif/cyw43_configport.h +++ b/ports/alif/cyw43_configport.h @@ -32,6 +32,7 @@ #include "py/mphal.h" #include "py/runtime.h" #include "extmod/modnetwork.h" +#include "extmod/mpbthci.h" #include "pendsv.h" #ifndef static_assert @@ -39,6 +40,7 @@ #endif #define CYW43_USE_SPI (1) +#define CYW43_ENABLE_BLUETOOTH_OVER_UART (1) #define CYW43_LWIP (1) #define CYW43_USE_STATS (0) #define CYW43_PRINTF(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__) @@ -78,6 +80,7 @@ #define CYW43_HAL_PIN_PULL_NONE 0 #define CYW43_HAL_MAC_WLAN0 MP_HAL_MAC_WLAN0 +#define CYW43_HAL_MAC_BDADDR MP_HAL_MAC_BDADDR #define cyw43_hal_ticks_us mp_hal_ticks_us #define cyw43_hal_ticks_ms mp_hal_ticks_ms @@ -87,6 +90,10 @@ #define cyw43_hal_pin_low mp_hal_pin_low #define cyw43_hal_pin_high mp_hal_pin_high +#define cyw43_hal_uart_set_baudrate mp_bluetooth_hci_uart_set_baudrate +#define cyw43_hal_uart_write mp_bluetooth_hci_uart_write +#define cyw43_hal_uart_readchar mp_bluetooth_hci_uart_readchar + #define cyw43_hal_get_mac mp_hal_get_mac #define cyw43_hal_get_mac_ascii mp_hal_get_mac_ascii #define cyw43_hal_generate_laa_mac mp_hal_generate_laa_mac @@ -94,6 +101,11 @@ #define CYW43_PIN_WL_REG_ON pin_WL_REG_ON #define CYW43_PIN_WL_IRQ pin_WL_IRQ +#define CYW43_PIN_BT_REG_ON pin_BT_REG_ON +#define CYW43_PIN_BT_HOST_WAKE pin_BT_HOST_WAKE +#define CYW43_PIN_BT_DEV_WAKE pin_BT_DEV_WAKE +#define CYW43_PIN_BT_CTS pin_BT_UART_CTS + #define cyw43_schedule_internal_poll_dispatch(func) pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, func) void cyw43_post_poll_hook(void); @@ -120,4 +132,10 @@ static inline void cyw43_hal_pin_config_irq_falling(mp_hal_pin_obj_t pin, bool e mp_hal_pin_config_irq_falling(pin, enable); } +#define cyw43_bluetooth_controller_init mp_bluetooth_hci_controller_init +#define cyw43_bluetooth_controller_deinit mp_bluetooth_hci_controller_deinit +#define cyw43_bluetooth_controller_sleep_maybe mp_bluetooth_hci_controller_sleep_maybe +#define cyw43_bluetooth_controller_woken mp_bluetooth_hci_controller_woken +#define cyw43_bluetooth_controller_wakeup mp_bluetooth_hci_controller_wakeup + #endif // MICROPY_INCLUDED_ALIF_CYW43_CONFIGPORT_H diff --git a/ports/alif/main.c b/ports/alif/main.c index 3bff8cf4a..fd35604cb 100644 --- a/ports/alif/main.c +++ b/ports/alif/main.c @@ -30,6 +30,7 @@ #include "py/mperrno.h" #include "py/mphal.h" #include "py/stackctrl.h" +#include "extmod/modbluetooth.h" #include "extmod/modnetwork.h" #include "shared/readline/readline.h" #include "shared/runtime/gchelper.h" @@ -37,6 +38,7 @@ #include "shared/runtime/softtimer.h" #include "shared/tinyusb/mp_usbd.h" #include "tusb.h" +#include "mpbthciport.h" #include "mpuart.h" #include "ospi_flash.h" #include "pendsv.h" @@ -103,6 +105,10 @@ int main(void) { mod_network_lwip_init(); #endif + #if MICROPY_PY_BLUETOOTH + mp_bluetooth_hci_init(); + #endif + #if MICROPY_PY_NETWORK_CYW43 { cyw43_init(&cyw43_state); @@ -154,6 +160,9 @@ int main(void) { soft_reset_exit: mp_printf(MP_PYTHON_PRINTER, "MPY: soft reboot\n"); + #if MICROPY_PY_BLUETOOTH + mp_bluetooth_deinit(); + #endif soft_timer_deinit(); gc_sweep_all(); mp_deinit(); diff --git a/ports/alif/mpbthciport.c b/ports/alif/mpbthciport.c new file mode 100644 index 000000000..a37c96215 --- /dev/null +++ b/ports/alif/mpbthciport.c @@ -0,0 +1,140 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 OpenMV LLC. + * + * 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. + */ + +#include "py/mphal.h" +#include "py/runtime.h" +#include "extmod/mpbthci.h" +#include "shared/runtime/softtimer.h" +#include "mpbthciport.h" + +#if MICROPY_PY_BLUETOOTH + +uint8_t mp_bluetooth_hci_cmd_buf[4 + 256]; + +// Soft timer and scheduling node for scheduling a HCI poll. +static soft_timer_entry_t mp_bluetooth_hci_soft_timer; +static mp_sched_node_t mp_bluetooth_hci_sched_node; + +// 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) { + 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) { + mp_bluetooth_hci_poll_now(); +} + +void mp_bluetooth_hci_poll_in_ms(uint32_t ms) { + soft_timer_reinsert(&mp_bluetooth_hci_soft_timer, ms); +} + +// For synchronous mode, we run all BLE stack code inside a scheduled task. +// This task is scheduled periodically via a timer, or immediately after UART RX IRQ. +static void run_events_scheduled_task(mp_sched_node_t *node) { + (void)node; + // This will process all buffered HCI UART data, and run any callouts or events. + mp_bluetooth_hci_poll(); +} + +// 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_poll_now(void) { + mp_sched_schedule_node(&mp_bluetooth_hci_sched_node, run_events_scheduled_task); +} + +/******************************************************************************/ +// HCI over UART + +#include "mpuart.h" + +static uint32_t hci_uart_id; +static bool hci_uart_first_char; +static uint8_t hci_rx_ringbuf_array[768]; +static ringbuf_t hci_rx_ringbuf = { + .buf = hci_rx_ringbuf_array, + .size = sizeof(hci_rx_ringbuf_array), + .iget = 0, + .iput = 0, +}; + +int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) { + hci_uart_id = port; + hci_uart_first_char = true; + + // Initialise the UART. + mp_uart_init(hci_uart_id, baudrate, pin_BT_UART_TX, pin_BT_UART_RX, &hci_rx_ringbuf); + mp_uart_set_flow(hci_uart_id, pin_BT_UART_RTS, pin_BT_UART_CTS); + mp_uart_set_irq_callback(hci_uart_id, mp_bluetooth_hci_poll_now); + + // Start the HCI polling to process any initial events/packets. + mp_bluetooth_hci_start_polling(); + + return 0; +} + +int mp_bluetooth_hci_uart_deinit(void) { + mp_uart_deinit(hci_uart_id); + return 0; +} + +int mp_bluetooth_hci_uart_set_baudrate(uint32_t baudrate) { + mp_uart_set_baudrate(hci_uart_id, baudrate); + return 0; +} + +int mp_bluetooth_hci_uart_write(const uint8_t *buf, size_t len) { + mp_bluetooth_hci_controller_wakeup(); + mp_uart_tx_data(hci_uart_id, (void *)buf, len); + return 0; +} + +// This function expects the controller to be in the wake state via a previous call +// to mp_bluetooth_hci_controller_woken. +int mp_bluetooth_hci_uart_readchar(void) { + if (mp_uart_rx_any(hci_uart_id)) { + int c = mp_uart_rx_char(hci_uart_id); + if (hci_uart_first_char) { + if (c == 0) { + return -1; + } + hci_uart_first_char = false; + } + return c; + } else { + return -1; + } +} + +#endif // MICROPY_PY_BLUETOOTH diff --git a/ports/alif/mpbthciport.h b/ports/alif/mpbthciport.h new file mode 100644 index 000000000..65e9d1752 --- /dev/null +++ b/ports/alif/mpbthciport.h @@ -0,0 +1,46 @@ +/* + * 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_ALIF_MPBTHCIPORT_H +#define MICROPY_INCLUDED_ALIF_MPBTHCIPORT_H + +#include "py/mpconfig.h" + +// Initialise the HCI subsystem (should be called once, early on). +void mp_bluetooth_hci_init(void); + +// Call this to poll the HCI now. +void mp_bluetooth_hci_poll_now(void); + +// Call this to poll the HCI after a certain timeout. +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_ALIF_MPBTHCIPORT_H diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index 76b603027..73abb8924 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -170,6 +170,12 @@ extern const struct _mp_obj_type_t mp_network_cyw43_type; MICROPY_HW_NIC_CYW43 \ MICROPY_BOARD_NETWORK_INTERFACES \ +// Bluetooth code only runs in the scheduler, no locking/mutex required. +#define MICROPY_PY_BLUETOOTH_ENTER uint32_t atomic_state = 0; +#define MICROPY_PY_BLUETOOTH_EXIT (void)atomic_state; +#define MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE (1) +#define MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS (1) + #define MP_STATE_PORT MP_STATE_VM // Miscellaneous settings diff --git a/ports/alif/mpnimbleport.c b/ports/alif/mpnimbleport.c new file mode 100644 index 000000000..8da5ce293 --- /dev/null +++ b/ports/alif/mpnimbleport.c @@ -0,0 +1,78 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Jim Mussared + * Copyright (c) 2020 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. + */ + +#include "py/runtime.h" +#include "py/mperrno.h" +#include "py/mphal.h" + +#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE + +#define DEBUG_printf(...) // printf("mpnimbleport.c: " __VA_ARGS__) + +#include "host/ble_hs.h" +#include "nimble/nimble_npl.h" + +#include "extmod/mpbthci.h" +#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. +void mp_bluetooth_hci_poll(void) { + if (mp_bluetooth_nimble_ble_state >= MP_BLUETOOTH_NIMBLE_BLE_STATE_WAITING_FOR_SYNC) { + // DEBUG_printf("mp_bluetooth_hci_poll_uart %d\n", mp_bluetooth_nimble_ble_state); + + // Run any timers. + mp_bluetooth_nimble_os_callout_process(); + + // 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(); + } + + 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. ----------------- + +void mp_bluetooth_nimble_hci_uart_wfi(void) { + __WFE(); + + // 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 (it must not invoke Python code), only processing incoming HCI data. + mp_bluetooth_nimble_hci_uart_process(false); +} + +#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE diff --git a/ports/alif/mpnimbleport.h b/ports/alif/mpnimbleport.h new file mode 100644 index 000000000..150ec42da --- /dev/null +++ b/ports/alif/mpnimbleport.h @@ -0,0 +1,32 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jim Mussared + * + * 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_ALIF_NIMBLE_PORT_H +#define MICROPY_INCLUDED_ALIF_NIMBLE_PORT_H + +// To allow MICROPY_HW_BLE_UART_ID to be resolved. + +#endif // MICROPY_INCLUDED_ALIF_NIMBLE_PORT_H |
