diff --git a/drivers/ninaw10/nina_bt_hci.c b/drivers/ninaw10/nina_bt_hci.c index f0d1b9bc89..b93a605f90 100644 --- a/drivers/ninaw10/nina_bt_hci.c +++ b/drivers/ninaw10/nina_bt_hci.c @@ -50,6 +50,11 @@ #define OCF_SET_EVENT_MASK (0x0001) #define OCF_RESET (0x0003) +// Use the name CS instead of GPIO1 +#ifndef MICROPY_HW_NINA_CS +#define MICROPY_HW_NINA_CS MICROPY_HW_NINA_GPIO1 +#endif + #define error_printf(...) // mp_printf(&mp_plat_print, "nina_bt_hci.c: " __VA_ARGS__) #define debug_printf(...) // mp_printf(&mp_plat_print, "nina_bt_hci.c: " __VA_ARGS__) @@ -59,6 +64,12 @@ extern uint8_t mp_bluetooth_hci_cmd_buf[4 + 256]; static int nina_hci_cmd(int ogf, int ocf, size_t param_len, const uint8_t *param_buf) { uint8_t *buf = mp_bluetooth_hci_cmd_buf; + // Clear the UART input buffer before sending the reset command. + // The ESP32 reset message may stick in it. + while (mp_bluetooth_hci_uart_any()) { + mp_bluetooth_hci_uart_readchar(); + } + buf[0] = HCI_COMMAND_PACKET; buf[1] = ocf; buf[2] = ogf << 2 | ocf >> 8; @@ -125,8 +136,17 @@ int mp_bluetooth_hci_controller_init(void) { mp_hal_pin_output(MICROPY_HW_NINA_RESET); mp_hal_pin_write(MICROPY_HW_NINA_RESET, 0); - mp_hal_pin_output(MICROPY_HW_NINA_GPIO1); - mp_hal_pin_write(MICROPY_HW_NINA_GPIO1, 0); + // care for dedicated setting of RTS/CTS + #ifdef MICROPY_HW_NINA_RTS + mp_hal_pin_output(MICROPY_HW_NINA_RTS); + mp_hal_pin_write(MICROPY_HW_NINA_RTS, 0); + #endif + #ifdef MICROPY_HW_NINA_CTS + mp_hal_pin_input(MICROPY_HW_NINA_CTS); + #endif + + mp_hal_pin_output(MICROPY_HW_NINA_CS); + mp_hal_pin_write(MICROPY_HW_NINA_CS, 0); mp_hal_delay_ms(150); mp_hal_pin_write(MICROPY_HW_NINA_RESET, 1); diff --git a/ports/samd/modmachine.h b/ports/samd/modmachine.h index e1279a2886..1a2a7627f3 100644 --- a/ports/samd/modmachine.h +++ b/ports/samd/modmachine.h @@ -34,5 +34,6 @@ extern const mp_obj_type_t machine_dac_type; #endif void rtc_gettime(timeutils_struct_time_t *tm); +void machine_uart_set_baudrate(mp_obj_t self, uint32_t baudrate); #endif // MICROPY_INCLUDED_SAMD_MODMACHINE_H diff --git a/ports/samd/mpbthciport.c b/ports/samd/mpbthciport.c new file mode 100644 index 0000000000..4df6404f44 --- /dev/null +++ b/ports/samd/mpbthciport.c @@ -0,0 +1,204 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Ibrahim Abdelkader + * + * 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/stream.h" +#include "py/mphal.h" +#include "extmod/modbluetooth.h" +#include "extmod/mpbthci.h" +#include "shared/runtime/softtimer.h" +#include "modmachine.h" +#include "extmod/modmachine.h" +#include "mpbthciport.h" + +#if MICROPY_PY_BLUETOOTH + +#define debug_printf(...) // mp_printf(&mp_plat_print, "mpbthciport.c: " __VA_ARGS__) +#define error_printf(...) mp_printf(&mp_plat_print, "mpbthciport.c: " __VA_ARGS__) + +uint8_t mp_bluetooth_hci_cmd_buf[4 + 256]; + +MP_REGISTER_ROOT_POINTER(mp_obj_t mp_bthci_uart); + +// Soft timer for scheduling a HCI poll. +static soft_timer_entry_t mp_bluetooth_hci_soft_timer; + +// 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); +} + +static mp_sched_node_t mp_bluetooth_hci_sched_node; + +// 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); +} + +int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) { + debug_printf("mp_bluetooth_hci_uart_init\n"); + + mp_obj_t args[] = { + MP_OBJ_NEW_SMALL_INT(port), + MP_OBJ_NEW_SMALL_INT(baudrate), + MP_OBJ_NEW_QSTR(MP_QSTR_tx), MP_OBJ_NEW_SMALL_INT(MICROPY_HW_BLE_UART_TX), + MP_OBJ_NEW_QSTR(MP_QSTR_rx), MP_OBJ_NEW_SMALL_INT(MICROPY_HW_BLE_UART_RX), + MP_OBJ_NEW_QSTR(MP_QSTR_timeout), MP_OBJ_NEW_SMALL_INT(1000), + MP_OBJ_NEW_QSTR(MP_QSTR_timeout_char), MP_OBJ_NEW_SMALL_INT(1000), + MP_OBJ_NEW_QSTR(MP_QSTR_rxbuf), MP_OBJ_NEW_SMALL_INT(768), + MP_OBJ_NEW_QSTR(MP_QSTR_txbuf), MP_OBJ_NEW_SMALL_INT(768), + }; + + // This is not a statically-allocated UART (see machine_uart.c), + // so it has to be tracked as a root pointer. + MP_STATE_PORT(mp_bthci_uart) = MP_OBJ_TYPE_GET_SLOT(&machine_uart_type, make_new)((mp_obj_t)&machine_uart_type, 2, 6, args); + // Start the HCI polling to process any initial events/packets. + mp_bluetooth_hci_start_polling(); + return 0; +} + +int mp_bluetooth_hci_uart_deinit(void) { + debug_printf("mp_bluetooth_hci_uart_deinit\n"); + mp_obj_t uart = MP_OBJ_TO_PTR(MP_STATE_PORT(mp_bthci_uart)); + mp_obj_t uart_deinit_args[] = { + NULL, // Method pointer + uart, // uart object + }; + mp_load_method_maybe((mp_obj_t)uart, MP_QSTR_deinit, uart_deinit_args); + if (uart_deinit_args[0] != MP_OBJ_NULL && uart_deinit_args[1] != MP_OBJ_NULL) { + mp_call_method_n_kw(0, 0, uart_deinit_args); + } + soft_timer_remove(&mp_bluetooth_hci_soft_timer); + return 0; +} + +int mp_bluetooth_hci_uart_set_baudrate(uint32_t baudrate) { + debug_printf("mp_bluetooth_hci_uart_set_baudrate(%lu)\n", baudrate); + mp_obj_t uart = MP_OBJ_TO_PTR(MP_STATE_PORT(mp_bthci_uart)); + machine_uart_set_baudrate(uart, baudrate); + return 0; +} + +int mp_bluetooth_hci_uart_any(void) { + int errcode = 0; + const mp_stream_p_t *proto = (mp_stream_p_t *)MP_OBJ_TYPE_GET_SLOT(&machine_uart_type, protocol); + + mp_uint_t ret = proto->ioctl(MP_STATE_PORT(mp_bthci_uart), MP_STREAM_POLL, MP_STREAM_POLL_RD, &errcode); + if (errcode != 0) { + error_printf("Uart ioctl failed to poll UART %d\n", errcode); + return -1; + } + return ret & MP_STREAM_POLL_RD; +} + +int mp_bluetooth_hci_uart_write(const uint8_t *buf, size_t len) { + debug_printf("mp_bluetooth_hci_uart_write\n"); + + int errcode = 0; + const mp_stream_p_t *proto = (mp_stream_p_t *)MP_OBJ_TYPE_GET_SLOT(&machine_uart_type, protocol); + + mp_bluetooth_hci_controller_wakeup(); + + if (proto->write(MP_STATE_PORT(mp_bthci_uart), (void *)buf, len, &errcode) < 0) { + error_printf("mp_bluetooth_hci_uart_write: failed to write to UART %d\n", errcode); + } + 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) { + debug_printf("mp_bluetooth_hci_uart_readchar\n"); + if (mp_bluetooth_hci_uart_any()) { + int errcode = 0; + uint8_t buf = 0; + const mp_stream_p_t *proto = (mp_stream_p_t *)MP_OBJ_TYPE_GET_SLOT(&machine_uart_type, protocol); + if (proto->read(MP_STATE_PORT(mp_bthci_uart), (void *)&buf, 1, &errcode) < 0) { + error_printf("mp_bluetooth_hci_uart_readchar: failed to read UART %d\n", errcode); + return -1; + } + return buf; + } else { + debug_printf("mp_bluetooth_hci_uart_readchar: not ready\n"); + return -1; + } +} + +// Default (weak) implementation of the HCI controller interface. +// A driver (e.g. cywbt43.c) can override these for controller-specific +// functionality (i.e. power management). +MP_WEAK int mp_bluetooth_hci_controller_init(void) { + debug_printf("mp_bluetooth_hci_controller_init (default)\n"); + return 0; +} + +MP_WEAK int mp_bluetooth_hci_controller_deinit(void) { + debug_printf("mp_bluetooth_hci_controller_deinit (default)\n"); + return 0; +} + +MP_WEAK int mp_bluetooth_hci_controller_sleep_maybe(void) { + debug_printf("mp_bluetooth_hci_controller_sleep_maybe (default)\n"); + return 0; +} + +MP_WEAK bool mp_bluetooth_hci_controller_woken(void) { + debug_printf("mp_bluetooth_hci_controller_woken (default)\n"); + return true; +} + +MP_WEAK int mp_bluetooth_hci_controller_wakeup(void) { + debug_printf("mp_bluetooth_hci_controller_wakeup (default)\n"); + return 0; +} + +#endif // MICROPY_PY_BLUETOOTH diff --git a/ports/samd/mpbthciport.h b/ports/samd/mpbthciport.h new file mode 100644 index 0000000000..a732f347d5 --- /dev/null +++ b/ports/samd/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_RP2_MPBTHCIPORT_H +#define MICROPY_INCLUDED_RP2_MPBTHCIPORT_H + +#include "mphalport.h" + +void mp_bluetooth_hci_config_interface(mp_obj_t uart, mp_obj_t cs, mp_obj_t busy, mp_obj_t rts, mp_obj_t cts); + +// 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_RP2_MPBTHCIPORT_H diff --git a/ports/samd/mpnimbleport.c b/ports/samd/mpnimbleport.c new file mode 100644 index 0000000000..74e9ecb026 --- /dev/null +++ b/ports/samd/mpnimbleport.c @@ -0,0 +1,80 @@ +/* + * 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" +#include "py/stream.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/modbluetooth.h" +#include "extmod/mpbthci.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) { + #if defined(__WFI) + __WFI(); + #endif + // 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/samd/mpnimbleport.h b/ports/samd/mpnimbleport.h new file mode 100644 index 0000000000..64debea33a --- /dev/null +++ b/ports/samd/mpnimbleport.h @@ -0,0 +1,29 @@ +/* + * 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_RP2_MPNIMBLEPORT_H +#define MICROPY_INCLUDED_RP2_MPNIMBLEPORT_H + +#endif // MICROPY_INCLUDED_RP2_MPNIMBLEPORT_H