rp2: Add Bluetooth support via cyw43.

Using BTstack with CYW43 for Pico W.

Signed-off-by: Damien George <damien@micropython.org>
This commit is contained in:
Peter Harper 2022-07-13 13:08:09 +01:00 committed by Damien George
parent 35182a8688
commit 888a15cda3
9 changed files with 238 additions and 17 deletions

View File

@ -18,6 +18,8 @@ endif()
set(PICO_TINYUSB_PATH ${MICROPY_DIR}/lib/tinyusb)
# Use the local lwip instead of the one in pico-sdk
set(PICO_LWIP_PATH ${MICROPY_DIR}/lib/lwip)
# Use the local btstack instead of the one in pico-sdk
set(PICO_BTSTACK_PATH ${MICROPY_DIR}/lib/btstack)
# Set the location of this port's directory.
set(MICROPY_PORT_DIR ${CMAKE_CURRENT_LIST_DIR})
@ -217,11 +219,40 @@ if(MICROPY_PY_BLUETOOTH)
MICROPY_PY_BLUETOOTH=1
MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS=1
MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE=1
MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING=1
MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS=1
)
endif()
if (MICROPY_PY_BLUETOOTH_CYW43)
target_compile_definitions(${MICROPY_TARGET} PRIVATE
CYW43_ENABLE_BLUETOOTH=1
MICROPY_PY_BLUETOOTH_CYW43=1
)
if (MICROPY_BLUETOOTH_BTSTACK)
target_link_libraries(${MICROPY_TARGET}
pico_btstack_hci_transport_cyw43
)
endif()
endif()
if (MICROPY_BLUETOOTH_BTSTACK)
string(CONCAT GIT_SUBMODULES "${GIT_SUBMODULES} " lib/btstack)
list(APPEND MICROPY_SOURCE_PORT mpbtstackport.c)
include(${MICROPY_DIR}/extmod/btstack/btstack.cmake)
target_link_libraries(${MICROPY_TARGET} micropy_extmod_btstack)
target_compile_definitions(${MICROPY_TARGET} PRIVATE
MICROPY_BLUETOOTH_BTSTACK=1
MICROPY_BLUETOOTH_BTSTACK_CONFIG_FILE=\"btstack_inc/btstack_config.h\"
)
# For modbluetooth_btstack.c includes
get_target_property(BTSTACK_INCLUDE micropy_extmod_btstack INTERFACE_INCLUDE_DIRECTORIES)
list(APPEND MICROPY_INC_CORE ${BTSTACK_INCLUDE})
endif()
if(MICROPY_BLUETOOTH_NIMBLE)
string(CONCAT GIT_SUBMODULES "${GIT_SUBMODULES} " lib/mynewt-nimble)
if(NOT (${ECHO_SUBMODULES}) AND NOT EXISTS ${MICROPY_DIR}/lib/mynewt-nimble/nimble/host/include/host/ble_hs.h)
@ -232,6 +263,8 @@ if(MICROPY_BLUETOOTH_NIMBLE)
target_compile_definitions(${MICROPY_TARGET} PRIVATE
MICROPY_BLUETOOTH_NIMBLE=1
MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY=0
MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING=1
MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS=1
)
target_compile_options(${MICROPY_TARGET} PRIVATE
# TODO: This flag is currently needed to make nimble build.
@ -272,6 +305,10 @@ if (MICROPY_PY_NETWORK_CYW43)
cyw43_driver_picow
cmsis_core
)
target_include_directories(${MICROPY_TARGET} PRIVATE
${MICROPY_DIR}/lib/cyw43-driver/
)
endif()
if (MICROPY_PY_NETWORK_NINAW10)

View File

@ -0,0 +1,9 @@
#ifndef MICROPY_INCLUDED_BTSTACK_BTSTACK_CONFIG_H
#define MICROPY_INCLUDED_BTSTACK_BTSTACK_CONFIG_H
#define HCI_OUTGOING_PRE_BUFFER_SIZE 4
#define HCI_ACL_CHUNK_SIZE_ALIGNMENT 4
#include "btstack_config_common.h"
#endif

View File

@ -33,7 +33,6 @@
#include "extmod/modnetwork.h"
#include "pendsv.h"
#define CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE "w43439A0_7_95_49_00_combined.h"
#define CYW43_WIFI_NVRAM_INCLUDE_FILE "wifi_nvram_43439.h"
#define CYW43_IOCTL_TIMEOUT_US (1000000)
#define CYW43_SLEEP_MAX (10)
@ -91,6 +90,15 @@
#define cyw43_schedule_internal_poll_dispatch(func) pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, func)
// Bluetooth uses the C heap to load its firmware (provided by pico-sdk).
// Space is reserved for this, see MICROPY_C_HEAP_SIZE.
#ifndef cyw43_malloc
#define cyw43_malloc malloc
#endif
#ifndef cyw43_free
#define cyw43_free free
#endif
void cyw43_post_poll_hook(void);
extern volatile int cyw43_has_pending;

View File

@ -122,7 +122,7 @@ int main(int argc, char **argv) {
#endif
#endif
#if MICROPY_PY_NETWORK_CYW43
#if MICROPY_PY_NETWORK_CYW43 || MICROPY_PY_BLUETOOTH_CYW43
{
cyw43_init(&cyw43_state);
cyw43_irq_init();

View File

@ -50,11 +50,6 @@ void mp_bluetooth_hci_init(void) {
events_task_is_scheduled = false;
}
STATIC void mp_bluetooth_hci_start_polling(void) {
events_task_is_scheduled = false;
mp_bluetooth_hci_poll_now();
}
static int64_t mp_bluetooth_hci_timer_callback(alarm_id_t id, void *user_data) {
poll_timer_id = 0;
mp_bluetooth_hci_poll_now();
@ -70,7 +65,7 @@ void mp_bluetooth_hci_poll_in_ms(uint32_t ms) {
STATIC mp_obj_t run_events_scheduled_task(mp_obj_t none_in) {
(void)none_in;
events_task_is_scheduled = false;
// This will process all buffered HCI UART data, and run any callouts or events.
// This will process all HCI data, and run any callouts or events.
mp_bluetooth_hci_poll();
return mp_const_none;
}
@ -88,8 +83,15 @@ void mp_bluetooth_hci_poll_now(void) {
}
}
#if defined(MICROPY_HW_BLE_UART_ID)
mp_obj_t mp_bthci_uart;
STATIC void mp_bluetooth_hci_start_polling(void) {
events_task_is_scheduled = false;
mp_bluetooth_hci_poll_now();
}
int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) {
debug_printf("mp_bluetooth_hci_uart_init\n");
@ -121,11 +123,6 @@ int mp_bluetooth_hci_uart_deinit(void) {
return 0;
}
int mp_bluetooth_hci_uart_set_baudrate(uint32_t baudrate) {
debug_printf("mp_bluetooth_hci_uart_set_baudrate(%lu)\n", 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);
@ -171,6 +168,13 @@ int mp_bluetooth_hci_uart_readchar(void) {
}
}
int mp_bluetooth_hci_uart_set_baudrate(uint32_t baudrate) {
debug_printf("mp_bluetooth_hci_uart_set_baudrate(%lu)\n", baudrate);
return 0;
}
#endif // defined(MICROPY_HW_BLE_UART_ID)
// 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).

View File

@ -34,7 +34,7 @@ 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.
// Request new HCI data 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);

157
ports/rp2/mpbtstackport.c Normal file
View File

@ -0,0 +1,157 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2020-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.
*/
#include "py/runtime.h"
#include "py/mperrno.h"
#include "py/mphal.h"
#define DEBUG_printf(...)
#define BTSTACK_ENABLE_HCI_DUMP (0)
#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK
#include "lib/btstack/src/btstack.h"
#include "lib/btstack/platform/embedded/hci_dump_embedded_stdout.h"
#include "extmod/mpbthci.h"
#include "extmod/btstack/modbluetooth_btstack.h"
#include "mpbthciport.h"
#include "pico/btstack_hci_transport_cyw43.h"
void mp_bluetooth_hci_poll_in_ms(uint32_t ms);
static btstack_linked_list_t mp_btstack_runloop_timers;
static void mp_btstack_runloop_init(void) {
mp_btstack_runloop_timers = NULL;
}
static void mp_btstack_runloop_set_timer(btstack_timer_source_t *tim, uint32_t timeout_ms) {
DEBUG_printf("mp_btstack_runloop_set_timer %lu\n", timeout_ms);
tim->timeout = mp_hal_ticks_ms() + timeout_ms + 1;
}
static void mp_btstack_runloop_add_timer(btstack_timer_source_t *tim) {
DEBUG_printf("mp_btstack_runloop_add_timer %lu\n", tim->timeout);
btstack_linked_item_t **node = &mp_btstack_runloop_timers;
for (; *node; node = &(*node)->next) {
btstack_timer_source_t *node_tim = (btstack_timer_source_t *)*node;
if (node_tim == tim) {
// Timer is already in the list, don't add it.
return;
}
int32_t delta = btstack_time_delta(tim->timeout, node_tim->timeout);
if (delta < 0) {
// Found sorted location in list.
break;
}
}
// Insert timer into list in sorted location.
tim->item.next = *node;
*node = &tim->item;
// Reschedule the HCI poll if this timer is at the head of the list.
if (mp_btstack_runloop_timers == &tim->item) {
int32_t delta_ms = btstack_time_delta(tim->timeout, mp_hal_ticks_ms());
mp_bluetooth_hci_poll_in_ms(delta_ms);
}
}
static bool mp_btstack_runloop_remove_timer(btstack_timer_source_t *tim) {
return btstack_linked_list_remove(&mp_btstack_runloop_timers, (btstack_linked_item_t *)tim);
}
static uint32_t mp_btstack_runloop_get_time_ms(void) {
return mp_hal_ticks_ms();
}
static const btstack_run_loop_t mp_btstack_runloop_rp2 = {
&mp_btstack_runloop_init,
&btstack_run_loop_base_add_data_source,
&btstack_run_loop_base_remove_data_source,
&btstack_run_loop_base_enable_data_source_callbacks,
&btstack_run_loop_base_disable_data_source_callbacks,
&mp_btstack_runloop_set_timer,
&mp_btstack_runloop_add_timer,
&mp_btstack_runloop_remove_timer,
NULL, // execute
NULL, // dump_timer
&mp_btstack_runloop_get_time_ms,
&mp_bluetooth_hci_poll_now, // poll_data_sources_from_irq
NULL, // execute_on_main_thread
NULL, // trigger_exit
};
void mp_bluetooth_hci_poll(void) {
if (mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_OFF) {
return;
}
// Process BT data.
if (mp_bluetooth_btstack_state != MP_BLUETOOTH_BTSTACK_STATE_HALTING) {
btstack_run_loop_base_poll_data_sources();
}
// Process any BTstack timers.
while (mp_btstack_runloop_timers != NULL) {
btstack_timer_source_t *tim = (btstack_timer_source_t *)mp_btstack_runloop_timers;
int32_t delta_ms = btstack_time_delta(tim->timeout, mp_hal_ticks_ms());
if (delta_ms > 0) {
// Timer has not expired yet, reschedule HCI poll for this timer.
mp_bluetooth_hci_poll_in_ms(delta_ms);
break;
}
btstack_linked_list_pop(&mp_btstack_runloop_timers);
tim->process(tim);
}
}
void mp_bluetooth_btstack_port_init(void) {
btstack_run_loop_init(&mp_btstack_runloop_rp2);
#if BTSTACK_ENABLE_HCI_DUMP
hci_dump_init(hci_dump_embedded_stdout_get_instance());
#endif
const hci_transport_t *transport = hci_transport_cyw43_instance();
hci_init(transport, NULL);
}
void mp_bluetooth_btstack_port_deinit(void) {
hci_power_control(HCI_POWER_OFF);
// We really need hci to be closed or else we might get a BT interrupt going off and
// this will cause a crash when calling btstack_run_loop_poll_data_sources_from_irq after the btstack run loop has been deleted
if (mp_bluetooth_btstack_state != MP_BLUETOOTH_BTSTACK_STATE_OFF) {
hci_transport_cyw43_instance()->close();
}
hci_close();
}
void mp_bluetooth_btstack_port_start(void) {
hci_power_control(HCI_POWER_ON);
}
#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK

View File

@ -282,3 +282,9 @@ typedef intptr_t mp_off_t;
extern uint32_t rosc_random_u32(void);
extern void lwip_lock_acquire(void);
extern void lwip_lock_release(void);
#if MICROPY_PY_BLUETOOTH_CYW43
// 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;
#endif

View File

@ -62,7 +62,7 @@ static void gpio_irq_handler(void) {
}
void cyw43_irq_init(void) {
gpio_add_raw_irq_handler_with_order_priority(IO_IRQ_BANK0, gpio_irq_handler, CYW43_SHARED_IRQ_HANDLER_PRIORITY);
gpio_add_raw_irq_handler_with_order_priority(CYW43_PIN_WL_HOST_WAKE, gpio_irq_handler, CYW43_SHARED_IRQ_HANDLER_PRIORITY);
irq_set_enabled(IO_IRQ_BANK0, true);
NVIC_SetPriority(PendSV_IRQn, PICO_LOWEST_IRQ_PRIORITY);
}