Compare commits
19 Commits
master
...
v1.22-rele
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8cd15829e2 | ||
![]() |
ee3c9ccb54 | ||
![]() |
e72d03855e | ||
![]() |
02df2b09d4 | ||
![]() |
e7ff724a87 | ||
![]() |
cc7cfc7e8d | ||
![]() |
b979c5a92a | ||
![]() |
f53ee9f12b | ||
![]() |
2531a15200 | ||
![]() |
8b6e89a8ca | ||
![]() |
a2e9ab362b | ||
![]() |
1e8cc6c503 | ||
![]() |
4c7d955a62 | ||
![]() |
068aa28fc5 | ||
![]() |
d5f3fcd935 | ||
![]() |
9b8c64c9ce | ||
![]() |
ac5e0b9f62 | ||
![]() |
61b8361f5f | ||
![]() |
4b4f6011e8 |
2
LICENSE
2
LICENSE
@ -69,6 +69,8 @@ used during the build process and is not part of the compiled source code.
|
||||
/FreeRTOS (GPL-2.0 with FreeRTOS exception)
|
||||
/esp32
|
||||
/ppp_set_auth.* (Apache-2.0)
|
||||
/rp2
|
||||
/mutex_extra.c (BSD-3-clause)
|
||||
/stm32
|
||||
/usbd*.c (MCD-ST Liberty SW License Agreement V2)
|
||||
/stm32_it.* (MIT + BSD-3-clause)
|
||||
|
@ -219,6 +219,11 @@ def run_until_complete(main_task=None):
|
||||
elif t.state is None:
|
||||
# Task is already finished and nothing await'ed on the task,
|
||||
# so call the exception handler.
|
||||
|
||||
# Save exception raised by the coro for later use.
|
||||
t.data = exc
|
||||
|
||||
# Create exception context and call the exception handler.
|
||||
_exc_context["exception"] = exc
|
||||
_exc_context["future"] = t
|
||||
Loop.call_exception_handler(_exc_context)
|
||||
|
@ -63,9 +63,6 @@ class _Remove:
|
||||
|
||||
# async
|
||||
def gather(*aws, return_exceptions=False):
|
||||
if not aws:
|
||||
return []
|
||||
|
||||
def done(t, er):
|
||||
# Sub-task "t" has finished, with exception "er".
|
||||
nonlocal state
|
||||
@ -86,20 +83,33 @@ def gather(*aws, return_exceptions=False):
|
||||
# Gather waiting is done, schedule the main gather task.
|
||||
core._task_queue.push(gather_task)
|
||||
|
||||
# Prepare the sub-tasks for the gather.
|
||||
# The `state` variable counts the number of tasks to wait for, and can be negative
|
||||
# if the gather should not run at all (because a task already had an exception).
|
||||
ts = [core._promote_to_task(aw) for aw in aws]
|
||||
state = 0
|
||||
for i in range(len(ts)):
|
||||
if ts[i].state is not True:
|
||||
# Task is not running, gather not currently supported for this case.
|
||||
raise RuntimeError("can't gather")
|
||||
# Register the callback to call when the task is done.
|
||||
if ts[i].state is True:
|
||||
# Task is running, register the callback to call when the task is done.
|
||||
ts[i].state = done
|
||||
state += 1
|
||||
elif not ts[i].state:
|
||||
# Task finished already.
|
||||
if not isinstance(ts[i].data, StopIteration):
|
||||
# Task finished by raising an exception.
|
||||
if not return_exceptions:
|
||||
# Do not run this gather at all.
|
||||
state = -len(ts)
|
||||
else:
|
||||
# Task being waited on, gather not currently supported for this case.
|
||||
raise RuntimeError("can't gather")
|
||||
|
||||
# Set the state for execution of the gather.
|
||||
gather_task = core.cur_task
|
||||
state = len(ts)
|
||||
cancel_all = False
|
||||
|
||||
# Wait for the a sub-task to need attention.
|
||||
# Wait for a sub-task to need attention (if there are any to wait for).
|
||||
if state > 0:
|
||||
gather_task.data = _Remove
|
||||
try:
|
||||
yield
|
||||
@ -118,8 +128,13 @@ def gather(*aws, return_exceptions=False):
|
||||
# Sub-task ran to completion, get its return value.
|
||||
ts[i] = ts[i].data.value
|
||||
else:
|
||||
# Sub-task had an exception with return_exceptions==True, so get its exception.
|
||||
# Sub-task had an exception.
|
||||
if return_exceptions:
|
||||
# Get the sub-task exception to return in the list of return values.
|
||||
ts[i] = ts[i].data
|
||||
elif isinstance(state, int):
|
||||
# Raise the sub-task exception, if there is not already an exception to raise.
|
||||
state = ts[i].data
|
||||
|
||||
# Either this gather was cancelled, or one of the sub-tasks raised an exception with
|
||||
# return_exceptions==False, so reraise the exception here.
|
||||
|
@ -462,8 +462,9 @@ STATIC void btstack_packet_handler_read(uint8_t packet_type, uint16_t channel, u
|
||||
if (!conn) {
|
||||
return;
|
||||
}
|
||||
mp_bluetooth_gattc_on_read_write_status(MP_BLUETOOTH_IRQ_GATTC_READ_DONE, conn_handle, conn->pending_value_handle, status);
|
||||
uint16_t value_handle = conn->pending_value_handle;
|
||||
conn->pending_value_handle = 0xffff;
|
||||
mp_bluetooth_gattc_on_read_write_status(MP_BLUETOOTH_IRQ_GATTC_READ_DONE, conn_handle, value_handle, status);
|
||||
} else if (event_type == GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) {
|
||||
DEBUG_printf(" --> gatt characteristic value query result\n");
|
||||
uint16_t conn_handle = gatt_event_characteristic_value_query_result_get_handle(packet);
|
||||
@ -490,11 +491,12 @@ STATIC void btstack_packet_handler_write_with_response(uint8_t packet_type, uint
|
||||
if (!conn) {
|
||||
return;
|
||||
}
|
||||
mp_bluetooth_gattc_on_read_write_status(MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE, conn_handle, conn->pending_value_handle, status);
|
||||
uint16_t value_handle = conn->pending_value_handle;
|
||||
conn->pending_value_handle = 0xffff;
|
||||
m_del(uint8_t, conn->pending_write_value, conn->pending_write_value_len);
|
||||
conn->pending_write_value = NULL;
|
||||
conn->pending_write_value_len = 0;
|
||||
mp_bluetooth_gattc_on_read_write_status(MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE, conn_handle, value_handle, status);
|
||||
}
|
||||
}
|
||||
#endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
|
||||
|
@ -1274,7 +1274,7 @@ STATIC mp_obj_t invoke_irq_handler(uint16_t event,
|
||||
if (ts_orig == NULL) {
|
||||
mp_thread_set_state(&ts);
|
||||
mp_stack_set_top(&ts + 1); // need to include ts in root-pointer scan
|
||||
mp_stack_set_limit(MICROPY_PY_BLUETOOTH_SYNC_EVENT_STACK_SIZE - 1024);
|
||||
mp_stack_set_limit(MICROPY_PY_BLUETOOTH_SYNC_EVENT_STACK_SIZE);
|
||||
ts.gc_lock_depth = 0;
|
||||
ts.nlr_jump_callback_top = NULL;
|
||||
ts.mp_pending_exception = MP_OBJ_NULL;
|
||||
|
@ -311,10 +311,6 @@ STATIC mp_obj_t ssl_context_get_ciphers(mp_obj_t self_in) {
|
||||
for (const int *cipher_list = mbedtls_ssl_list_ciphersuites(); *cipher_list; ++cipher_list) {
|
||||
const char *cipher_name = mbedtls_ssl_get_ciphersuite_name(*cipher_list);
|
||||
mp_obj_list_append(list, MP_OBJ_FROM_PTR(mp_obj_new_str(cipher_name, strlen(cipher_name))));
|
||||
cipher_list++;
|
||||
if (!*cipher_list) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
@ -14,3 +14,7 @@ CONFIG_BT_NIMBLE_MAX_CONNECTIONS=4
|
||||
CONFIG_BT_NIMBLE_PINNED_TO_CORE_0=n
|
||||
CONFIG_BT_NIMBLE_PINNED_TO_CORE_1=y
|
||||
CONFIG_BT_NIMBLE_PINNED_TO_CORE=1
|
||||
|
||||
# Increase NimBLE task stack size from the default, because Python code
|
||||
# (BLE IRQ handlers) will most likely run on this task.
|
||||
CONFIG_BT_NIMBLE_TASK_STACK_SIZE=6144
|
||||
|
@ -95,7 +95,7 @@
|
||||
#define MICROPY_PY_BLUETOOTH (1)
|
||||
#define MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS (1)
|
||||
#define MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS_WITH_INTERLOCK (1)
|
||||
#define MICROPY_PY_BLUETOOTH_SYNC_EVENT_STACK_SIZE (CONFIG_BT_NIMBLE_TASK_STACK_SIZE)
|
||||
#define MICROPY_PY_BLUETOOTH_SYNC_EVENT_STACK_SIZE (CONFIG_BT_NIMBLE_TASK_STACK_SIZE - 2048)
|
||||
#define MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE (1)
|
||||
#define MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING (1)
|
||||
#define MICROPY_BLUETOOTH_NIMBLE (1)
|
||||
|
@ -63,6 +63,11 @@ void mp_bluetooth_nimble_port_start(void) {
|
||||
void mp_bluetooth_nimble_port_shutdown(void) {
|
||||
DEBUG_printf("mp_bluetooth_nimble_port_shutdown\n");
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS_WITH_INTERLOCK
|
||||
// Release the GIL so any callbacks can run during the shutdown calls below.
|
||||
MP_THREAD_GIL_EXIT();
|
||||
#endif
|
||||
|
||||
// Despite the name, these is an ESP32-specific (no other NimBLE ports have these functions).
|
||||
// Calls ble_hs_stop() and waits for stack shutdown.
|
||||
nimble_port_stop();
|
||||
@ -70,6 +75,10 @@ void mp_bluetooth_nimble_port_shutdown(void) {
|
||||
// Shuts down the event queue.
|
||||
nimble_port_deinit();
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS_WITH_INTERLOCK
|
||||
MP_THREAD_GIL_ENTER();
|
||||
#endif
|
||||
|
||||
// Mark stack as shutdown.
|
||||
mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF;
|
||||
}
|
||||
|
@ -139,10 +139,10 @@ NORETURN STATIC void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MIMXRT117x_SERIES
|
||||
#if defined(MIMXRT117x_SERIES)
|
||||
machine_pin_config(pin_WAKEUP_DIG, PIN_MODE_IT_RISING, PIN_PULL_DISABLED, PIN_DRIVE_OFF, 0, PIN_AF_MODE_ALT5);
|
||||
GPC_CM_EnableIrqWakeup(GPC_CPU_MODE_CTRL_0, GPIO13_Combined_0_31_IRQn, true);
|
||||
#elif defined IOMUXC_SNVS_WAKEUP_GPIO5_IO00
|
||||
#elif defined(pin_WAKEUP)
|
||||
machine_pin_config(pin_WAKEUP, PIN_MODE_IT_RISING, PIN_PULL_DISABLED, PIN_DRIVE_OFF, 0, PIN_AF_MODE_ALT5);
|
||||
GPC_EnableIRQ(GPC, GPIO5_Combined_0_15_IRQn);
|
||||
#endif
|
||||
|
@ -28,7 +28,7 @@
|
||||
// peripheral config
|
||||
#define MICROPY_HW_ENABLE_RNG (1)
|
||||
#define MICROPY_HW_ENABLE_RTC (1)
|
||||
#define MICROPY_HW_RTC_SOURCE (1)
|
||||
#define MICROPY_HW_RTC_SOURCE (0)
|
||||
#define MICROPY_HW_ENABLE_ADC (1)
|
||||
#define MICROPY_HW_HAS_FLASH (1)
|
||||
#define MICROPY_HW_ENABLE_USBDEV (1)
|
||||
|
@ -26,6 +26,7 @@
|
||||
#define RA_RA_CONFIG_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "py/mpconfig.h"
|
||||
|
||||
#if defined(RA4M1) | defined(RA4W1)
|
||||
#define SCI_CH_MAX 10
|
||||
|
@ -426,7 +426,7 @@ void ra_i2c_init(R_IIC0_Type *i2c_inst, uint32_t scl, uint32_t sda, uint32_t bau
|
||||
i2c_inst->ICCR1_b.ICE = 1; // I2C enable
|
||||
ra_i2c_set_baudrate(i2c_inst, baudrate);
|
||||
i2c_inst->ICSER = 0x00; // I2C reset bus status enable register
|
||||
i2c_inst->ICMR3_b.ACKWP = 0x01; // I2C allow to write ACKBT (transfer acknowledge bit)
|
||||
i2c_inst->ICMR3_b.ACKWP = 0x00; // I2C not allow to write ACKBT (transfer acknowledge bit)
|
||||
i2c_inst->ICIER = 0xFF; // Enable all interrupts
|
||||
i2c_inst->ICCR1_b.IICRST = 0; // I2C internal reset
|
||||
ra_i2c_irq_enable(i2c_inst);
|
||||
@ -480,6 +480,7 @@ void ra_i2c_xunit_read_byte(R_IIC0_Type *i2c_inst, xaction_unit_t *unit) {
|
||||
void ra_i2c_xunit_init(xaction_unit_t *unit, uint8_t *buf, uint32_t size, bool fread, void *next) {
|
||||
unit->m_bytes_transferred = 0;
|
||||
unit->m_bytes_transfer = size;
|
||||
unit->m_bytes_total = size;
|
||||
unit->m_fread = fread;
|
||||
unit->buf = buf;
|
||||
unit->next = (void *)next;
|
||||
@ -531,6 +532,37 @@ static void ra_i2c_iceri_isr(R_IIC0_Type *i2c_inst) {
|
||||
static void ra_i2c_icrxi_isr(R_IIC0_Type *i2c_inst) {
|
||||
xaction_unit_t *unit = current_xaction_unit;
|
||||
xaction_t *action = current_xaction;
|
||||
// 1 byte or 2 bytes
|
||||
if (unit->m_bytes_total <= 2) {
|
||||
if (action->m_status == RA_I2C_STATUS_AddrWriteCompleted) {
|
||||
action->m_status = RA_I2C_STATUS_FirstReceiveCompleted;
|
||||
i2c_inst->ICMR3_b.WAIT = 1;
|
||||
// need dummy read processes for 1 byte and 2 bytes receive
|
||||
if (unit->m_bytes_total == 2) {
|
||||
(void)i2c_inst->ICDRR; // dummy read for 2 bytes receive
|
||||
} else { // m_bytes_total == 1
|
||||
i2c_inst->ICMR3_b.ACKWP = 0x01; // enable write ACKBT (transfer acknowledge bit)
|
||||
i2c_inst->ICMR3_b.ACKBT = 1;
|
||||
i2c_inst->ICMR3_b.ACKWP = 0x00; // disable write ACKBT (transfer acknowledge bit)
|
||||
(void)i2c_inst->ICDRR; // dummy read for 1 byte receive
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (unit->m_bytes_transfer == 2) { // last two data
|
||||
i2c_inst->ICMR3_b.ACKWP = 0x01; // enable write ACKBT (transfer acknowledge bit)
|
||||
i2c_inst->ICMR3_b.ACKBT = 1;
|
||||
i2c_inst->ICMR3_b.ACKWP = 0x00; // disable write ACKBT (transfer acknowledge bit)
|
||||
ra_i2c_xunit_read_byte(i2c_inst, unit);
|
||||
} else { // last data
|
||||
action->m_status = RA_I2C_STATUS_LastReceiveCompleted;
|
||||
if (action->m_stop == true) {
|
||||
i2c_inst->ICCR2_b.SP = 1; // request top condition
|
||||
}
|
||||
ra_i2c_xunit_read_byte(i2c_inst, unit);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// 3 bytes or more
|
||||
if (action->m_status == RA_I2C_STATUS_AddrWriteCompleted) {
|
||||
(void)i2c_inst->ICDRR; // dummy read
|
||||
action->m_status = RA_I2C_STATUS_FirstReceiveCompleted;
|
||||
@ -542,7 +574,9 @@ static void ra_i2c_icrxi_isr(R_IIC0_Type *i2c_inst) {
|
||||
}
|
||||
ra_i2c_xunit_read_byte(i2c_inst, unit);
|
||||
} else if (unit->m_bytes_transfer == 2) {
|
||||
i2c_inst->ICMR3_b.ACKWP = 0x01; // enable write ACKBT (transfer acknowledge bit)
|
||||
i2c_inst->ICMR3_b.ACKBT = 1;
|
||||
i2c_inst->ICMR3_b.ACKWP = 0x00; // disable write ACKBT (transfer acknowledge bit)
|
||||
ra_i2c_xunit_read_byte(i2c_inst, unit);
|
||||
} else {
|
||||
// last data
|
||||
|
@ -47,9 +47,9 @@ typedef enum
|
||||
RA_I2C_STATUS_AddrWriteCompleted = 3,
|
||||
RA_I2C_STATUS_DataWriteCompleted = 4,
|
||||
RA_I2C_STATUS_DataSendCompleted = 5,
|
||||
RA_I2C_STATUS_FirstReceiveCompleted = 5,
|
||||
RA_I2C_STATUS_LastReceiveCompleted = 6,
|
||||
RA_I2C_STATUS_Stopped = 7,
|
||||
RA_I2C_STATUS_FirstReceiveCompleted = 6,
|
||||
RA_I2C_STATUS_LastReceiveCompleted = 7,
|
||||
RA_I2C_STATUS_Stopped = 8,
|
||||
} xaction_status_t;
|
||||
|
||||
typedef enum
|
||||
@ -64,6 +64,7 @@ typedef enum
|
||||
typedef struct {
|
||||
volatile uint32_t m_bytes_transferred;
|
||||
volatile uint32_t m_bytes_transfer;
|
||||
uint32_t m_bytes_total;
|
||||
bool m_fread;
|
||||
uint8_t *buf;
|
||||
void *next;
|
||||
@ -75,7 +76,7 @@ typedef struct {
|
||||
uint32_t m_current;
|
||||
uint32_t m_address;
|
||||
volatile xaction_status_t m_status;
|
||||
xaction_error_t m_error;
|
||||
volatile xaction_error_t m_error;
|
||||
bool m_stop;
|
||||
} xaction_t;
|
||||
|
||||
|
@ -30,7 +30,7 @@
|
||||
|
||||
void ra_init(void) {
|
||||
ra_int_init();
|
||||
SysTick_Config(PCLK / 1000);
|
||||
SysTick_Config(MICROPY_HW_MCU_SYSCLK / 1000);
|
||||
internal_flash_init();
|
||||
}
|
||||
|
||||
|
@ -129,6 +129,7 @@ set(MICROPY_SOURCE_PORT
|
||||
mphalport.c
|
||||
mpnetworkport.c
|
||||
mpthreadport.c
|
||||
mutex_extra.c
|
||||
pendsv.c
|
||||
rp2_flash.c
|
||||
rp2_pio.c
|
||||
@ -473,6 +474,7 @@ target_compile_definitions(${MICROPY_TARGET} PRIVATE
|
||||
PICO_FLOAT_PROPAGATE_NANS=1
|
||||
PICO_STACK_SIZE=0x2000
|
||||
PICO_CORE1_STACK_SIZE=0
|
||||
PICO_MAX_SHARED_IRQ_HANDLERS=8 # we need more than the default
|
||||
PICO_PROGRAM_NAME="MicroPython"
|
||||
PICO_NO_PROGRAM_VERSION_STRING=1 # do it ourselves in main.c
|
||||
MICROPY_BUILD_TYPE="${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION} ${CMAKE_BUILD_TYPE}"
|
||||
|
@ -232,20 +232,18 @@ STATIC void feed_dma(machine_i2s_obj_t *self, uint8_t *dma_buffer_p) {
|
||||
|
||||
STATIC void irq_configure(machine_i2s_obj_t *self) {
|
||||
if (self->i2s_id == 0) {
|
||||
irq_set_exclusive_handler(DMA_IRQ_0, dma_irq0_handler);
|
||||
irq_add_shared_handler(DMA_IRQ_0, dma_irq0_handler, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
} else {
|
||||
irq_set_exclusive_handler(DMA_IRQ_1, dma_irq1_handler);
|
||||
irq_add_shared_handler(DMA_IRQ_1, dma_irq1_handler, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
|
||||
irq_set_enabled(DMA_IRQ_1, true);
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void irq_deinit(machine_i2s_obj_t *self) {
|
||||
if (self->i2s_id == 0) {
|
||||
irq_set_enabled(DMA_IRQ_0, false);
|
||||
irq_remove_handler(DMA_IRQ_0, dma_irq0_handler);
|
||||
} else {
|
||||
irq_set_enabled(DMA_IRQ_1, false);
|
||||
irq_remove_handler(DMA_IRQ_1, dma_irq1_handler);
|
||||
}
|
||||
}
|
||||
|
@ -483,7 +483,7 @@ STATIC mp_uint_t mp_machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t
|
||||
return i;
|
||||
}
|
||||
}
|
||||
mp_event_wait_ms(timeout - elapsed);
|
||||
mp_event_handle_nowait();
|
||||
}
|
||||
*dest++ = ringbuf_get(&(self->read_buffer));
|
||||
start = mp_hal_ticks_ms(); // Inter-character timeout
|
||||
@ -559,7 +559,7 @@ STATIC mp_uint_t mp_machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, uint
|
||||
if (now >= timeout) {
|
||||
break;
|
||||
}
|
||||
mp_event_wait_ms((timeout - now) / 1000);
|
||||
mp_event_handle_nowait();
|
||||
}
|
||||
*errcode = MP_ETIMEDOUT;
|
||||
ret = MP_STREAM_ERROR;
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "py/mpthread.h"
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/multicore.h"
|
||||
#include "mutex_extra.h"
|
||||
|
||||
#if MICROPY_PY_THREAD
|
||||
|
||||
@ -45,27 +46,23 @@ STATIC uint32_t *core1_stack = NULL;
|
||||
STATIC size_t core1_stack_num_words = 0;
|
||||
|
||||
// Thread mutex.
|
||||
STATIC mp_thread_mutex_t atomic_mutex;
|
||||
STATIC mutex_t atomic_mutex;
|
||||
|
||||
uint32_t mp_thread_begin_atomic_section(void) {
|
||||
if (core1_entry) {
|
||||
// When both cores are executing, we also need to provide
|
||||
// full mutual exclusion.
|
||||
mp_thread_mutex_lock(&atomic_mutex, 1);
|
||||
// In case this atomic section is for flash access, then
|
||||
// suspend the other core.
|
||||
multicore_lockout_start_blocking();
|
||||
}
|
||||
|
||||
return mutex_enter_blocking_and_disable_interrupts(&atomic_mutex);
|
||||
} else {
|
||||
return save_and_disable_interrupts();
|
||||
}
|
||||
}
|
||||
|
||||
void mp_thread_end_atomic_section(uint32_t state) {
|
||||
if (atomic_mutex.owner != LOCK_INVALID_OWNER_ID) {
|
||||
mutex_exit_and_restore_interrupts(&atomic_mutex, state);
|
||||
} else {
|
||||
restore_interrupts(state);
|
||||
|
||||
if (core1_entry) {
|
||||
multicore_lockout_end_blocking();
|
||||
mp_thread_mutex_unlock(&atomic_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,7 +70,7 @@ void mp_thread_end_atomic_section(uint32_t state) {
|
||||
void mp_thread_init(void) {
|
||||
assert(get_core_num() == 0);
|
||||
|
||||
mp_thread_mutex_init(&atomic_mutex);
|
||||
mutex_init(&atomic_mutex);
|
||||
|
||||
// Allow MICROPY_BEGIN_ATOMIC_SECTION to be invoked from core1.
|
||||
multicore_lockout_victim_init();
|
||||
|
30
ports/rp2/mutex_extra.c
Normal file
30
ports/rp2/mutex_extra.c
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include "mutex_extra.h"
|
||||
|
||||
// These functions are taken from lib/pico-sdk/src/common/pico_sync/mutex.c and modified
|
||||
// so that they atomically obtain the mutex and disable interrupts.
|
||||
|
||||
uint32_t __time_critical_func(mutex_enter_blocking_and_disable_interrupts)(mutex_t * mtx) {
|
||||
lock_owner_id_t caller = lock_get_caller_owner_id();
|
||||
do {
|
||||
uint32_t save = spin_lock_blocking(mtx->core.spin_lock);
|
||||
if (!lock_is_owner_id_valid(mtx->owner)) {
|
||||
mtx->owner = caller;
|
||||
spin_unlock_unsafe(mtx->core.spin_lock);
|
||||
return save;
|
||||
}
|
||||
lock_internal_spin_unlock_with_wait(&mtx->core, save);
|
||||
} while (true);
|
||||
}
|
||||
|
||||
void __time_critical_func(mutex_exit_and_restore_interrupts)(mutex_t * mtx, uint32_t save) {
|
||||
spin_lock_unsafe_blocking(mtx->core.spin_lock);
|
||||
assert(lock_is_owner_id_valid(mtx->owner));
|
||||
mtx->owner = LOCK_INVALID_OWNER_ID;
|
||||
lock_internal_spin_unlock_with_notify(&mtx->core, save);
|
||||
}
|
34
ports/rp2/mutex_extra.h
Normal file
34
ports/rp2/mutex_extra.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2023 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_MUTEX_EXTRA_H
|
||||
#define MICROPY_INCLUDED_RP2_MUTEX_EXTRA_H
|
||||
|
||||
#include "pico/mutex.h"
|
||||
|
||||
uint32_t mutex_enter_blocking_and_disable_interrupts(mutex_t *mtx);
|
||||
void mutex_exit_and_restore_interrupts(mutex_t *mtx, uint32_t save);
|
||||
|
||||
#endif // MICROPY_INCLUDED_RP2_MUTEX_EXTRA_H
|
@ -86,7 +86,7 @@ STATIC const uint32_t rp2_dma_ctrl_field_count = MP_ARRAY_SIZE(rp2_dma_ctrl_fiel
|
||||
STATIC uint32_t rp2_dma_register_value_from_obj(mp_obj_t o, int reg_type) {
|
||||
if (reg_type == REG_TYPE_ADDR_READ || reg_type == REG_TYPE_ADDR_WRITE) {
|
||||
mp_buffer_info_t buf_info;
|
||||
mp_uint_t flags = MP_BUFFER_READ;
|
||||
mp_uint_t flags = (reg_type == REG_TYPE_ADDR_READ) ? MP_BUFFER_READ : MP_BUFFER_WRITE;
|
||||
if (mp_get_buffer(o, &buf_info, flags)) {
|
||||
return (uint32_t)buf_info.buf;
|
||||
}
|
||||
@ -98,18 +98,16 @@ STATIC uint32_t rp2_dma_register_value_from_obj(mp_obj_t o, int reg_type) {
|
||||
STATIC void rp2_dma_irq_handler(void) {
|
||||
// Main IRQ handler
|
||||
uint32_t irq_bits = dma_hw->ints0;
|
||||
dma_hw->ints0 = 0xffff;
|
||||
|
||||
for (int i = 0; i < NUM_DMA_CHANNELS; i++) {
|
||||
if (irq_bits & (1u << i)) {
|
||||
mp_irq_obj_t *handler = MP_STATE_PORT(rp2_dma_irq_obj[i]);
|
||||
if (handler) {
|
||||
// An rp2.DMA IRQ handler is registered for this channel, so handle it.
|
||||
dma_channel_acknowledge_irq0(i);
|
||||
rp2_dma_obj_t *self = (rp2_dma_obj_t *)handler->parent;
|
||||
self->irq_flag = 1;
|
||||
mp_irq_handler(handler);
|
||||
} else {
|
||||
// We got an interrupt with no handler. Disable the channel
|
||||
dma_channel_set_irq0_enabled(i, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -389,6 +387,9 @@ STATIC mp_obj_t rp2_dma_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k
|
||||
if (irq == NULL) {
|
||||
irq = mp_irq_new(&rp2_dma_irq_methods, MP_OBJ_FROM_PTR(self));
|
||||
MP_STATE_PORT(rp2_dma_irq_obj[self->channel]) = irq;
|
||||
|
||||
// Clear any existing IRQs on this DMA channel, they are not for us.
|
||||
dma_channel_acknowledge_irq0(self->channel);
|
||||
}
|
||||
|
||||
if (n_args > 1 || kw_args->used != 0) {
|
||||
@ -457,12 +458,11 @@ MP_DEFINE_CONST_OBJ_TYPE(
|
||||
void rp2_dma_init(void) {
|
||||
// Set up interrupts.
|
||||
memset(MP_STATE_PORT(rp2_dma_irq_obj), 0, sizeof(MP_STATE_PORT(rp2_dma_irq_obj)));
|
||||
irq_set_exclusive_handler(DMA_IRQ_0, rp2_dma_irq_handler);
|
||||
irq_add_shared_handler(DMA_IRQ_0, rp2_dma_irq_handler, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
|
||||
}
|
||||
|
||||
void rp2_dma_deinit(void) {
|
||||
// Disable and clear interrupts.
|
||||
irq_set_mask_enabled(1u << DMA_IRQ_0, false);
|
||||
// Remove our interrupt handler.
|
||||
irq_remove_handler(DMA_IRQ_0, rp2_dma_irq_handler);
|
||||
}
|
||||
|
||||
|
@ -70,6 +70,22 @@ bi_decl(bi_block_device(
|
||||
BINARY_INFO_BLOCK_DEV_FLAG_WRITE |
|
||||
BINARY_INFO_BLOCK_DEV_FLAG_PT_UNKNOWN));
|
||||
|
||||
// Flash erase and write must run with interrupts disabled and the other core suspended,
|
||||
// because the XIP bit gets disabled.
|
||||
static uint32_t begin_critical_flash_section(void) {
|
||||
if (multicore_lockout_victim_is_initialized(1 - get_core_num())) {
|
||||
multicore_lockout_start_blocking();
|
||||
}
|
||||
return save_and_disable_interrupts();
|
||||
}
|
||||
|
||||
static void end_critical_flash_section(uint32_t state) {
|
||||
restore_interrupts(state);
|
||||
if (multicore_lockout_victim_is_initialized(1 - get_core_num())) {
|
||||
multicore_lockout_end_blocking();
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t rp2_flash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
|
||||
// Parse arguments
|
||||
enum { ARG_start, ARG_len };
|
||||
@ -135,19 +151,17 @@ STATIC mp_obj_t rp2_flash_writeblocks(size_t n_args, const mp_obj_t *args) {
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ);
|
||||
if (n_args == 3) {
|
||||
// Flash erase/program must run in an atomic section because the XIP bit gets disabled.
|
||||
mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
|
||||
mp_uint_t atomic_state = begin_critical_flash_section();
|
||||
flash_range_erase(self->flash_base + offset, bufinfo.len);
|
||||
MICROPY_END_ATOMIC_SECTION(atomic_state);
|
||||
end_critical_flash_section(atomic_state);
|
||||
mp_event_handle_nowait();
|
||||
// TODO check return value
|
||||
} else {
|
||||
offset += mp_obj_get_int(args[3]);
|
||||
}
|
||||
// Flash erase/program must run in an atomic section because the XIP bit gets disabled.
|
||||
mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
|
||||
mp_uint_t atomic_state = begin_critical_flash_section();
|
||||
flash_range_program(self->flash_base + offset, bufinfo.buf, bufinfo.len);
|
||||
MICROPY_END_ATOMIC_SECTION(atomic_state);
|
||||
end_critical_flash_section(atomic_state);
|
||||
mp_event_handle_nowait();
|
||||
// TODO check return value
|
||||
return mp_const_none;
|
||||
@ -170,10 +184,9 @@ STATIC mp_obj_t rp2_flash_ioctl(mp_obj_t self_in, mp_obj_t cmd_in, mp_obj_t arg_
|
||||
return MP_OBJ_NEW_SMALL_INT(BLOCK_SIZE_BYTES);
|
||||
case MP_BLOCKDEV_IOCTL_BLOCK_ERASE: {
|
||||
uint32_t offset = mp_obj_get_int(arg_in) * BLOCK_SIZE_BYTES;
|
||||
// Flash erase/program must run in an atomic section because the XIP bit gets disabled.
|
||||
mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
|
||||
mp_uint_t atomic_state = begin_critical_flash_section();
|
||||
flash_range_erase(self->flash_base + offset, BLOCK_SIZE_BYTES);
|
||||
MICROPY_END_ATOMIC_SECTION(atomic_state);
|
||||
end_critical_flash_section(atomic_state);
|
||||
// TODO check return value
|
||||
return MP_OBJ_NEW_SMALL_INT(0);
|
||||
}
|
||||
|
11
py/compile.c
11
py/compile.c
@ -1650,9 +1650,11 @@ STATIC void compile_try_except(compiler_t *comp, mp_parse_node_t pn_body, int n_
|
||||
if (qstr_exception_local != 0) {
|
||||
EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE);
|
||||
EMIT_ARG(label_assign, l3);
|
||||
EMIT_ARG(adjust_stack_size, 1); // stack adjust for possible return value
|
||||
EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE);
|
||||
compile_store_id(comp, qstr_exception_local);
|
||||
compile_delete_id(comp, qstr_exception_local);
|
||||
EMIT_ARG(adjust_stack_size, -1);
|
||||
compile_decrease_except_level(comp);
|
||||
}
|
||||
|
||||
@ -1682,9 +1684,18 @@ STATIC void compile_try_finally(compiler_t *comp, mp_parse_node_t pn_body, int n
|
||||
} else {
|
||||
compile_try_except(comp, pn_body, n_except, pn_except, pn_else);
|
||||
}
|
||||
|
||||
// If the code reaches this point then the try part of the try-finally exited normally.
|
||||
// This is indicated to the runtime by None sitting on the stack.
|
||||
EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE);
|
||||
|
||||
// Compile the finally block.
|
||||
// The stack needs to be adjusted by 1 to account for the possibility that the finally is
|
||||
// being executed as part of a return, and the return value is on the top of the stack.
|
||||
EMIT_ARG(label_assign, l_finally_block);
|
||||
EMIT_ARG(adjust_stack_size, 1);
|
||||
compile_node(comp, pn_finally);
|
||||
EMIT_ARG(adjust_stack_size, -1);
|
||||
|
||||
compile_decrease_except_level(comp);
|
||||
}
|
||||
|
@ -31,7 +31,7 @@
|
||||
// are unavailable.
|
||||
#define MICROPY_VERSION_MAJOR 1
|
||||
#define MICROPY_VERSION_MINOR 22
|
||||
#define MICROPY_VERSION_MICRO 0
|
||||
#define MICROPY_VERSION_MICRO 2
|
||||
#define MICROPY_VERSION_PRERELEASE 0
|
||||
|
||||
// Combined version as a 32-bit number for convenience to allow version
|
||||
|
@ -70,3 +70,13 @@ def f():
|
||||
finally:
|
||||
print('finally 1')
|
||||
print(f())
|
||||
|
||||
# the finally block uses a lot of Python stack and then a local is accessed
|
||||
# (tests that the use of the stack doesn't clobber the local)
|
||||
def f(x):
|
||||
try:
|
||||
return x
|
||||
finally:
|
||||
print(2, 3, 4, 5, 6)
|
||||
print(x)
|
||||
print(f(1))
|
||||
|
65
tests/extmod/asyncio_gather_finished_early.py
Normal file
65
tests/extmod/asyncio_gather_finished_early.py
Normal file
@ -0,0 +1,65 @@
|
||||
# Test asyncio.gather() when a task is already finished before the gather starts.
|
||||
|
||||
try:
|
||||
import asyncio
|
||||
except ImportError:
|
||||
print("SKIP")
|
||||
raise SystemExit
|
||||
|
||||
|
||||
# CPython and MicroPython differ in when they signal (and print) that a task raised an
|
||||
# uncaught exception. So define an empty custom_handler() to suppress this output.
|
||||
def custom_handler(loop, context):
|
||||
pass
|
||||
|
||||
|
||||
async def task_that_finishes_early(id, event, fail):
|
||||
print("task_that_finishes_early", id)
|
||||
event.set()
|
||||
if fail:
|
||||
raise ValueError("intentional exception", id)
|
||||
|
||||
|
||||
async def task_that_runs():
|
||||
for i in range(5):
|
||||
print("task_that_runs", i)
|
||||
await asyncio.sleep(0)
|
||||
|
||||
|
||||
async def main(start_task_that_runs, task_fail, return_exceptions):
|
||||
print("== start", start_task_that_runs, task_fail, return_exceptions)
|
||||
|
||||
# Set exception handler to suppress exception output.
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.set_exception_handler(custom_handler)
|
||||
|
||||
# Create tasks.
|
||||
event_a = asyncio.Event()
|
||||
event_b = asyncio.Event()
|
||||
tasks = []
|
||||
if start_task_that_runs:
|
||||
tasks.append(asyncio.create_task(task_that_runs()))
|
||||
tasks.append(asyncio.create_task(task_that_finishes_early("a", event_a, task_fail)))
|
||||
tasks.append(asyncio.create_task(task_that_finishes_early("b", event_b, task_fail)))
|
||||
|
||||
# Make sure task_that_finishes_early() are both done, before calling gather().
|
||||
await event_a.wait()
|
||||
await event_b.wait()
|
||||
|
||||
# Gather the tasks.
|
||||
try:
|
||||
result = "complete", await asyncio.gather(*tasks, return_exceptions=return_exceptions)
|
||||
except Exception as er:
|
||||
result = "exception", er, start_task_that_runs and tasks[0].done()
|
||||
|
||||
# Wait for the final task to finish (if it was started).
|
||||
if start_task_that_runs:
|
||||
await tasks[0]
|
||||
|
||||
# Print results.
|
||||
print(result)
|
||||
|
||||
|
||||
# Run the test in the 8 different combinations of its arguments.
|
||||
for i in range(8):
|
||||
asyncio.run(main(bool(i & 4), bool(i & 2), bool(i & 1)))
|
@ -12,6 +12,8 @@ ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
||||
ciphers = ctx.get_ciphers()
|
||||
|
||||
for ci in ciphers:
|
||||
# Only print those ciphers know to exist on all ports.
|
||||
if ("TLS-ECDHE-ECDSA-WITH-AES" in ci or "TLS-RSA-WITH-AES" in ci) and "CBC" in ci:
|
||||
print(ci)
|
||||
|
||||
ctx.set_ciphers(ciphers[:1])
|
||||
|
@ -1,6 +1,10 @@
|
||||
TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384
|
||||
TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA
|
||||
TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256
|
||||
TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA
|
||||
TLS-RSA-WITH-AES-256-CBC-SHA256
|
||||
TLS-RSA-WITH-AES-256-CBC-SHA
|
||||
TLS-RSA-WITH-AES-128-CBC-SHA256
|
||||
TLS-RSA-WITH-AES-128-CBC-SHA
|
||||
object 'str' isn't a tuple or list
|
||||
(-24192, 'MBEDTLS_ERR_SSL_BAD_CONFIG')
|
||||
|
206
tests/multi_bluetooth/ble_irq_calls.py
Normal file
206
tests/multi_bluetooth/ble_irq_calls.py
Normal file
@ -0,0 +1,206 @@
|
||||
# Test calling BLE methods from within the BLE.irq event handler.
|
||||
|
||||
from micropython import const
|
||||
import struct
|
||||
import time
|
||||
import bluetooth
|
||||
|
||||
_IRQ_CENTRAL_CONNECT = const(1)
|
||||
_IRQ_CENTRAL_DISCONNECT = const(2)
|
||||
_IRQ_PERIPHERAL_CONNECT = const(7)
|
||||
_IRQ_PERIPHERAL_DISCONNECT = const(8)
|
||||
_IRQ_GATTC_SERVICE_RESULT = const(9)
|
||||
_IRQ_GATTC_SERVICE_DONE = const(10)
|
||||
_IRQ_GATTC_CHARACTERISTIC_RESULT = const(11)
|
||||
_IRQ_GATTC_CHARACTERISTIC_DONE = const(12)
|
||||
_IRQ_GATTC_DESCRIPTOR_RESULT = const(13)
|
||||
_IRQ_GATTC_DESCRIPTOR_DONE = const(14)
|
||||
_IRQ_GATTC_READ_RESULT = const(15)
|
||||
_IRQ_GATTC_READ_DONE = const(16)
|
||||
_IRQ_GATTC_WRITE_DONE = const(17)
|
||||
_IRQ_MTU_EXCHANGED = const(21)
|
||||
_IRQ_GET_SECRET = const(29)
|
||||
_IRQ_SET_SECRET = const(30)
|
||||
|
||||
EVENT_NAMES = {
|
||||
1: "_IRQ_CENTRAL_CONNECT",
|
||||
2: "_IRQ_CENTRAL_DISCONNECT",
|
||||
3: "_IRQ_GATTS_WRITE",
|
||||
4: "_IRQ_GATTS_READ_REQUEST",
|
||||
7: "_IRQ_PERIPHERAL_CONNECT",
|
||||
8: "_IRQ_PERIPHERAL_DISCONNECT",
|
||||
9: "_IRQ_GATTC_SERVICE_RESULT",
|
||||
10: "_IRQ_GATTC_SERVICE_DONE",
|
||||
11: "_IRQ_GATTC_CHARACTERISTIC_RESULT",
|
||||
12: "_IRQ_GATTC_CHARACTERISTIC_DONE",
|
||||
13: "_IRQ_GATTC_DESCRIPTOR_RESULT",
|
||||
14: "_IRQ_GATTC_DESCRIPTOR_DONE",
|
||||
15: "_IRQ_GATTC_READ_RESULT",
|
||||
16: "_IRQ_GATTC_READ_DONE",
|
||||
17: "_IRQ_GATTC_WRITE_DONE",
|
||||
18: "_IRQ_GATTC_NOTIFY",
|
||||
21: "_IRQ_MTU_EXCHANGED",
|
||||
}
|
||||
|
||||
_ADV_TYPE_FLAGS = const(0x01)
|
||||
_ADV_TYPE_NAME = const(0x09)
|
||||
_ADV_TYPE_UUID128_COMPLETE = const(0x7)
|
||||
|
||||
_NOTIFY_ENABLE = const(1)
|
||||
|
||||
ACCESSORY_UUID = bluetooth.UUID("a5a5a5a5-ffff-9999-1111-5a5a5a5a5a5a")
|
||||
STATE_UUID = bluetooth.UUID("a5a5a5a5-eeee-9999-1111-5a5a5a5a5a5a")
|
||||
CCC_UUID = bluetooth.UUID(0x2902)
|
||||
|
||||
STATE_CHARACTERISTIC = (
|
||||
STATE_UUID,
|
||||
bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,
|
||||
)
|
||||
|
||||
ACCESSORY_SERVICE = (ACCESSORY_UUID, (STATE_CHARACTERISTIC,))
|
||||
|
||||
|
||||
class Central:
|
||||
def __init__(self):
|
||||
self.done = False
|
||||
self._conn_handle = None
|
||||
self._service = None
|
||||
self._characteristic_handle = None
|
||||
self._cccd_handle = None
|
||||
self._reads_remaining = None
|
||||
ble.active(1)
|
||||
ble.irq(self._ble_event_handler)
|
||||
ble.gap_connect(*BDADDR)
|
||||
|
||||
def _ble_event_handler(self, event, data):
|
||||
print(EVENT_NAMES[event])
|
||||
|
||||
if event == _IRQ_PERIPHERAL_CONNECT:
|
||||
conn_handle, _, _ = data
|
||||
self._conn_handle = conn_handle
|
||||
ble.gattc_discover_services(self._conn_handle, ACCESSORY_UUID)
|
||||
|
||||
elif event == _IRQ_PERIPHERAL_DISCONNECT:
|
||||
conn_handle, _, addr = data
|
||||
assert self._conn_handle == conn_handle
|
||||
self._conn_handle = None
|
||||
print("connection closed")
|
||||
|
||||
elif event == _IRQ_GATTC_SERVICE_RESULT:
|
||||
_, first_handle, last_handle, uuid = data
|
||||
print("service found:", last_handle - first_handle, uuid)
|
||||
if uuid == ACCESSORY_UUID:
|
||||
assert self._service is None
|
||||
self._service = (first_handle, last_handle)
|
||||
|
||||
elif event == _IRQ_GATTC_SERVICE_DONE:
|
||||
print("service handle range:", self._service[1] - self._service[0])
|
||||
start_handle, end_handle = self._service
|
||||
ble.gattc_discover_characteristics(self._conn_handle, start_handle, end_handle)
|
||||
|
||||
elif event == _IRQ_GATTC_CHARACTERISTIC_RESULT:
|
||||
_, end_handle, value_handle, properties, uuid = data
|
||||
assert uuid == STATE_UUID
|
||||
print("characteristic found:", uuid)
|
||||
assert self._characteristic_handle is None
|
||||
self._characteristic_handle = value_handle
|
||||
|
||||
elif event == _IRQ_GATTC_CHARACTERISTIC_DONE:
|
||||
start_handle, end_handle = self._service
|
||||
ble.gattc_discover_descriptors(self._conn_handle, start_handle, end_handle)
|
||||
|
||||
elif event == _IRQ_GATTC_DESCRIPTOR_RESULT:
|
||||
_, dsc_handle, uuid = data
|
||||
if uuid == CCC_UUID:
|
||||
print("CCCD found:", uuid)
|
||||
assert self._cccd_handle is None
|
||||
self._cccd_handle = dsc_handle
|
||||
|
||||
elif event == _IRQ_GATTC_DESCRIPTOR_DONE:
|
||||
# Discovery complete, proceed to MTU exchange.
|
||||
ble.gattc_exchange_mtu(self._conn_handle)
|
||||
|
||||
elif event == _IRQ_MTU_EXCHANGED:
|
||||
# MTU exchanged, proceed to enable CCCD.
|
||||
print("CCCD write")
|
||||
ble.gattc_write(
|
||||
self._conn_handle, self._cccd_handle, struct.pack("<h", _NOTIFY_ENABLE), 1
|
||||
)
|
||||
|
||||
elif event == _IRQ_GATTC_WRITE_DONE:
|
||||
conn_handle, _, result = data
|
||||
print("CCCD write result:", result)
|
||||
print("issue gattc_read")
|
||||
self._reads_remaining = 2
|
||||
ble.gattc_read(self._conn_handle, self._characteristic_handle)
|
||||
|
||||
elif event == _IRQ_GATTC_READ_RESULT:
|
||||
_, _, char_data = data
|
||||
print("gattc_read result:", bytes(char_data))
|
||||
|
||||
elif event == _IRQ_GATTC_READ_DONE:
|
||||
self._reads_remaining -= 1
|
||||
if self._reads_remaining > 0:
|
||||
ble.gattc_read(self._conn_handle, self._characteristic_handle)
|
||||
else:
|
||||
self.done = True
|
||||
ble.gap_disconnect(self._conn_handle)
|
||||
|
||||
|
||||
class Peripheral:
|
||||
def __init__(self):
|
||||
self.done = False
|
||||
ble.active(1)
|
||||
ble.irq(self._ble_event_handler)
|
||||
ble.gatts_register_services((ACCESSORY_SERVICE,))
|
||||
add_payload = self.advertising_payload("acc", (ACCESSORY_UUID,))
|
||||
ble.gap_advertise(500000, add_payload)
|
||||
|
||||
def advertising_payload(self, name, services):
|
||||
payload = bytearray()
|
||||
|
||||
def _append(adv_type, value):
|
||||
nonlocal payload
|
||||
payload.extend(struct.pack("BB", len(value) + 1, adv_type) + value)
|
||||
|
||||
_append(_ADV_TYPE_FLAGS, struct.pack("B", 0x02 + 0x04))
|
||||
_append(_ADV_TYPE_NAME, name)
|
||||
|
||||
for uuid in services:
|
||||
b = bytes(uuid)
|
||||
assert len(b) == 16
|
||||
_append(_ADV_TYPE_UUID128_COMPLETE, b)
|
||||
|
||||
return payload
|
||||
|
||||
def _ble_event_handler(self, event, data):
|
||||
if event not in (_IRQ_GET_SECRET, _IRQ_SET_SECRET):
|
||||
print(EVENT_NAMES[event])
|
||||
if event == _IRQ_CENTRAL_DISCONNECT:
|
||||
self.done = True
|
||||
|
||||
|
||||
# Acting in peripheral role.
|
||||
def instance0():
|
||||
print("peripheral start")
|
||||
peripheral = Peripheral()
|
||||
multitest.globals(BDADDR=ble.config("mac"))
|
||||
multitest.next()
|
||||
while not peripheral.done:
|
||||
time.sleep_ms(100)
|
||||
multitest.broadcast("finished")
|
||||
ble.active(0)
|
||||
|
||||
|
||||
# Acting in central role.
|
||||
def instance1():
|
||||
print("central start")
|
||||
multitest.next()
|
||||
central = Central()
|
||||
while not central.done:
|
||||
time.sleep_ms(100)
|
||||
multitest.wait("finished")
|
||||
ble.active(0)
|
||||
|
||||
|
||||
ble = bluetooth.BLE()
|
35
tests/multi_bluetooth/ble_irq_calls.py.exp
Normal file
35
tests/multi_bluetooth/ble_irq_calls.py.exp
Normal file
@ -0,0 +1,35 @@
|
||||
--- instance0 ---
|
||||
peripheral start
|
||||
_IRQ_CENTRAL_CONNECT
|
||||
_IRQ_MTU_EXCHANGED
|
||||
_IRQ_GATTS_READ_REQUEST
|
||||
_IRQ_GATTS_READ_REQUEST
|
||||
_IRQ_CENTRAL_DISCONNECT
|
||||
--- instance1 ---
|
||||
central start
|
||||
_IRQ_PERIPHERAL_CONNECT
|
||||
_IRQ_GATTC_SERVICE_RESULT
|
||||
service found: 3 UUID('a5a5a5a5-ffff-9999-1111-5a5a5a5a5a5a')
|
||||
_IRQ_GATTC_SERVICE_DONE
|
||||
service handle range: 3
|
||||
_IRQ_GATTC_CHARACTERISTIC_RESULT
|
||||
characteristic found: UUID('a5a5a5a5-eeee-9999-1111-5a5a5a5a5a5a')
|
||||
_IRQ_GATTC_CHARACTERISTIC_DONE
|
||||
_IRQ_GATTC_DESCRIPTOR_RESULT
|
||||
_IRQ_GATTC_DESCRIPTOR_RESULT
|
||||
_IRQ_GATTC_DESCRIPTOR_RESULT
|
||||
CCCD found: UUID(0x2902)
|
||||
_IRQ_GATTC_DESCRIPTOR_DONE
|
||||
_IRQ_MTU_EXCHANGED
|
||||
CCCD write
|
||||
_IRQ_GATTC_WRITE_DONE
|
||||
CCCD write result: 0
|
||||
issue gattc_read
|
||||
_IRQ_GATTC_READ_RESULT
|
||||
gattc_read result: b''
|
||||
_IRQ_GATTC_READ_DONE
|
||||
_IRQ_GATTC_READ_RESULT
|
||||
gattc_read result: b''
|
||||
_IRQ_GATTC_READ_DONE
|
||||
_IRQ_PERIPHERAL_DISCONNECT
|
||||
connection closed
|
Loading…
x
Reference in New Issue
Block a user