diff options
| author | Dan Halbert <halbert@halwitz.org> | 2019-08-19 12:41:20 -0400 |
|---|---|---|
| committer | Dan Halbert <halbert@halwitz.org> | 2019-08-19 12:41:20 -0400 |
| commit | e3dc5e3a665782ac5360333eb1913aff9aeeadab (patch) | |
| tree | 8f7fabdeba205bccd8591a8110dfbbab59f5cddb /ports | |
| parent | e3c0428838efa2800dfa319089b869048dad851a (diff) | |
| parent | 6b54e6749e110674634143e38d3b6eb598b18ce0 (diff) | |
Merge remote-tracking branch 'adafruit/master' into run-background-tasks
Restore dependencies indicated by indentation in circuitpy_mpconfig.h.
Diffstat (limited to 'ports')
39 files changed, 1784 insertions, 800 deletions
diff --git a/ports/atmel-samd/audio_dma.c b/ports/atmel-samd/audio_dma.c index 6b5eeddcc..481a420cb 100644 --- a/ports/atmel-samd/audio_dma.c +++ b/ports/atmel-samd/audio_dma.c @@ -35,6 +35,8 @@ #include "py/mpstate.h" #include "py/runtime.h" +#if CIRCUITPY_AUDIOIO || CIRCUITPY_AUDIOBUSIO + static audio_dma_t* audio_dma_state[AUDIO_DMA_CHANNEL_COUNT]; // This cannot be in audio_dma_state because it's volatile. @@ -50,6 +52,18 @@ uint8_t find_free_audio_dma_channel(void) { return channel; } +void audio_dma_disable_channel(uint8_t channel) { + if (channel >= AUDIO_DMA_CHANNEL_COUNT) + return; + dma_disable_channel(channel); +} + +void audio_dma_enable_channel(uint8_t channel) { + if (channel >= AUDIO_DMA_CHANNEL_COUNT) + return; + dma_enable_channel(channel); +} + void audio_dma_convert_signed(audio_dma_t* dma, uint8_t* buffer, uint32_t buffer_length, uint8_t** output_buffer, uint32_t* output_buffer_length, uint8_t* output_spacing) { @@ -252,16 +266,19 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t* dma, } dma_configure(dma_channel, dma_trigger_source, true); - dma_enable_channel(dma_channel); + audio_dma_enable_channel(dma_channel); return AUDIO_DMA_OK; } void audio_dma_stop(audio_dma_t* dma) { - dma_disable_channel(dma->dma_channel); - disable_event_channel(dma->event_channel); - MP_STATE_PORT(playing_audio)[dma->dma_channel] = NULL; - + uint8_t channel = dma->dma_channel; + if (channel < AUDIO_DMA_CHANNEL_COUNT) { + audio_dma_disable_channel(channel); + disable_event_channel(dma->event_channel); + MP_STATE_PORT(playing_audio)[channel] = NULL; + audio_dma_state[channel] = NULL; + } dma->dma_channel = AUDIO_DMA_CHANNEL_COUNT; } @@ -290,7 +307,7 @@ void audio_dma_reset(void) { for (uint8_t i = 0; i < AUDIO_DMA_CHANNEL_COUNT; i++) { audio_dma_state[i] = NULL; audio_dma_pending[i] = false; - dma_disable_channel(i); + audio_dma_disable_channel(i); dma_descriptor(i)->BTCTRL.bit.VALID = false; MP_STATE_PORT(playing_audio)[i] = NULL; } @@ -333,3 +350,4 @@ void audio_dma_background(void) { audio_dma_pending[i] = false; } } +#endif diff --git a/ports/atmel-samd/audio_dma.h b/ports/atmel-samd/audio_dma.h index 53173c277..9d923c5ce 100644 --- a/ports/atmel-samd/audio_dma.h +++ b/ports/atmel-samd/audio_dma.h @@ -83,6 +83,9 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t* dma, bool output_signed, uint32_t output_register_address, uint8_t dma_trigger_source); + +void audio_dma_disable_channel(uint8_t channel); +void audio_dma_enable_channel(uint8_t channel); void audio_dma_stop(audio_dma_t* dma); bool audio_dma_get_playing(audio_dma_t* dma); void audio_dma_pause(audio_dma_t* dma); diff --git a/ports/atmel-samd/background.c b/ports/atmel-samd/background.c index 4e88d34e7..694238070 100644 --- a/ports/atmel-samd/background.c +++ b/ports/atmel-samd/background.c @@ -56,7 +56,7 @@ void run_background_tasks(void) { assert_heap_ok(); running_background_tasks = true; - #if (defined(SAMD21) && defined(PIN_PA02)) || defined(SAMD51) + #if CIRCUITPY_AUDIOIO || CIRCUITPY_AUDIOBUSIO audio_dma_background(); #endif #if CIRCUITPY_DISPLAYIO diff --git a/ports/atmel-samd/boards/pyportal_titano/board.c b/ports/atmel-samd/boards/pyportal_titano/board.c new file mode 100644 index 000000000..0ed1010a1 --- /dev/null +++ b/ports/atmel-samd/boards/pyportal_titano/board.c @@ -0,0 +1,129 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Scott Shawcroft for Adafruit Industries + * + * 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 "boards/board.h" +#include "mpconfigboard.h" +#include "hal/include/hal_gpio.h" + +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/displayio/FourWire.h" + +#include "shared-module/displayio/__init__.h" +#include "shared-module/displayio/mipi_constants.h" + +#include "tick.h" + +#define DELAY 0x80 + +uint8_t display_init_sequence[] = { + 0x01, DELAY, 100/5, // Soft reset, then delay 10 ms + 0xB9, 3, 0xFF, 0x83, 0x57, // Extension command set + 0xFF, DELAY, 500/5, + 0xB3, 4, 0x80, 0x00, 0x06, 0x06, // 0x80 enables SDO pin (0x00 disables) + 0xB6, 2, 0x01, 0x25, // -1.52V + 0xB0, 1, 0x68, // Normal mode 70Hz, Idle mode 55 Hz + 0xCC, 1, 0x05, + 0xB1, 6, + 0x00, // Not deep standby + 0x15, // BT + 0x1C, // VSPR + 0x1C, // VSNR + 0x83, // AP + 0xAA, // FS + 0xC0, 6, + 0x50, // OPON normal + 0x50, // OPON idle + 0x01, // STBA + 0x3C, // STBA + 0x1E, // STBA + 0x08, // GEN + 0xB4, 7, + 0x02, // NW 0x02 + 0x40, // RTN + 0x00, // DIV + 0x2A, // DUM + 0x2A, // DUM + 0x0D, // GDON + 0x78, // GDOFF + 0xE0, 34, + 0x02, 0x0A, 0x11, 0x1d, 0x23, 0x35, 0x41, 0x4b, 0x4b, + 0x42, 0x3A, 0x27, 0x1B, 0x08, 0x09, 0x03, 0x02, 0x0A, + 0x11, 0x1d, 0x23, 0x35, 0x41, 0x4b, 0x4b, 0x42, 0x3A, + 0x27, 0x1B, 0x08, 0x09, 0x03, 0x00, 0x01, + 0x3a, 1, 0x55, + 0x36, 1, 0x00, + 0x11, DELAY, 150/5, // Exit Sleep, then delay 150 ms + 0x29, DELAY, 50/5 +}; + +void board_init(void) { + busio_spi_obj_t* spi = &displays[0].fourwire_bus.inline_bus; + common_hal_busio_spi_construct(spi, &pin_PA13, &pin_PA12, &pin_PA14); + common_hal_busio_spi_never_reset(spi); + + displayio_fourwire_obj_t* bus = &displays[0].fourwire_bus; + bus->base.type = &displayio_fourwire_type; + common_hal_displayio_fourwire_construct(bus, + spi, + &pin_PB05, // TFT_DC Command or data + &pin_PB06, // TFT_CS Chip select + &pin_PA00, // TFT_RST Reset + 24000000); + + displayio_display_obj_t* display = &displays[0].display; + display->base.type = &displayio_display_type; + common_hal_displayio_display_construct(display, + bus, + 480, // Width + 320, // Height + 0, // column start + 0, // row start + 270, // rotation + 16, // Color depth + false, // grayscale + false, // pixels_in_byte_share_row (unused for depths > 8) + 1, // bytes per cell. Only valid for depths < 8 + false, // reverse_pixels_in_byte. Only valid for depths < 8 + MIPI_COMMAND_SET_COLUMN_ADDRESS, // Set column command + MIPI_COMMAND_SET_PAGE_ADDRESS, // Set row command + MIPI_COMMAND_WRITE_MEMORY_START, // Write memory command + 0x37, // Set vertical scroll command + display_init_sequence, + sizeof(display_init_sequence), + &pin_PB31, // Backlight pin + NO_BRIGHTNESS_COMMAND, + 1.0f, // brightness (ignored) + true, // auto_brightness + false, // single_byte_bounds + false); // data_as_commands +} + +bool board_requests_safe_mode(void) { + return false; +} + +void reset_board(void) { +} diff --git a/ports/atmel-samd/boards/pyportal_titano/mpconfigboard.h b/ports/atmel-samd/boards/pyportal_titano/mpconfigboard.h new file mode 100644 index 000000000..91c118810 --- /dev/null +++ b/ports/atmel-samd/boards/pyportal_titano/mpconfigboard.h @@ -0,0 +1,39 @@ +#define MICROPY_HW_BOARD_NAME "Adafruit PyPortal Titano" +#define MICROPY_HW_MCU_NAME "samd51j20" + +#define CIRCUITPY_MCU_FAMILY samd51 + + +#define MICROPY_HW_LED_STATUS (&pin_PA27) + +#define MICROPY_HW_NEOPIXEL (&pin_PB22) + +// These are pins not to reset. +// QSPI Data pins +#define MICROPY_PORT_A ( PORT_PA08 | PORT_PA09 | PORT_PA10 | PORT_PA11 ) +// QSPI CS, and QSPI SCK +#define MICROPY_PORT_B ( PORT_PB10 | PORT_PB11 | PORT_PB22 ) +#define MICROPY_PORT_C ( 0 ) +#define MICROPY_PORT_D (0) + +#define AUTORESET_DELAY_MS 500 + +// If you change this, then make sure to update the linker scripts as well to +// make sure you don't overwrite code +#define CIRCUITPY_INTERNAL_NVM_SIZE 8192 + +#define BOARD_FLASH_SIZE (FLASH_SIZE - 0x4000 - CIRCUITPY_INTERNAL_NVM_SIZE) + +#define DEFAULT_I2C_BUS_SCL (&pin_PB03) +#define DEFAULT_I2C_BUS_SDA (&pin_PB02) + +#define DEFAULT_SPI_BUS_SCK (&pin_PA13) +#define DEFAULT_SPI_BUS_MOSI (&pin_PA12) +#define DEFAULT_SPI_BUS_MISO (&pin_PA14) + +#define DEFAULT_UART_BUS_RX (&pin_PB13) +#define DEFAULT_UART_BUS_TX (&pin_PB12) + +// USB is always used internally so skip the pin objects for it. +#define IGNORE_PIN_PA24 1 +#define IGNORE_PIN_PA25 1 diff --git a/ports/atmel-samd/boards/pyportal_titano/mpconfigboard.mk b/ports/atmel-samd/boards/pyportal_titano/mpconfigboard.mk new file mode 100644 index 000000000..0ca438311 --- /dev/null +++ b/ports/atmel-samd/boards/pyportal_titano/mpconfigboard.mk @@ -0,0 +1,16 @@ +LD_FILE = boards/samd51x20-bootloader-external-flash.ld +USB_VID = 0x239A +USB_PID = 0x8054 +USB_PRODUCT = "PyPortal Titano" +USB_MANUFACTURER = "Adafruit Industries LLC" + +CHIP_VARIANT = SAMD51J20A +CHIP_FAMILY = samd51 + +QSPI_FLASH_FILESYSTEM = 1 +EXTERNAL_FLASH_DEVICE_COUNT = 2 +EXTERNAL_FLASH_DEVICES = "W25Q64JV_IQ, GD25Q64C" +LONGINT_IMPL = MPZ + +# No touch on SAMD51 yet +CIRCUITPY_TOUCHIO = 0 diff --git a/ports/atmel-samd/boards/pyportal_titano/pins.c b/ports/atmel-samd/boards/pyportal_titano/pins.c new file mode 100644 index 000000000..6bc0a504a --- /dev/null +++ b/ports/atmel-samd/boards/pyportal_titano/pins.c @@ -0,0 +1,94 @@ +#include "shared-bindings/board/__init__.h" + +#include "boards/board.h" +#include "shared-module/displayio/__init__.h" + +// This mapping only includes functional names because pins broken +// out on connectors are labeled with their MCU name available from +// microcontroller.pin. +STATIC const mp_rom_map_elem_t board_global_dict_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR_SPEAKER), MP_ROM_PTR(&pin_PA02) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_AUDIO_OUT), MP_ROM_PTR(&pin_PA02) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_PA02) }, // analog out/in + { MP_OBJ_NEW_QSTR(MP_QSTR_SPEAKER_ENABLE), MP_ROM_PTR(&pin_PA27) }, + + // Light sensor + { MP_OBJ_NEW_QSTR(MP_QSTR_LIGHT), MP_ROM_PTR(&pin_PA07) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_PA07) }, + + // STEMMA connectors + { MP_OBJ_NEW_QSTR(MP_QSTR_D3), MP_ROM_PTR(&pin_PA04) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_PA04) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_D4), MP_ROM_PTR(&pin_PA05) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_A4), MP_ROM_PTR(&pin_PA05) }, + + // Indicator LED + { MP_OBJ_NEW_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_PB23) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_L), MP_ROM_PTR(&pin_PB23) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_NEOPIXEL),MP_ROM_PTR(&pin_PB22) }, + + // LCD pins + { MP_OBJ_NEW_QSTR(MP_QSTR_TFT_RESET), MP_ROM_PTR(&pin_PA00) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_TFT_RD), MP_ROM_PTR(&pin_PB04) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_TFT_RS), MP_ROM_PTR(&pin_PB05) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_TFT_CS), MP_ROM_PTR(&pin_PB06) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_TFT_TE), MP_ROM_PTR(&pin_PB07) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_TFT_WR), MP_ROM_PTR(&pin_PB09) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_TFT_DC), MP_ROM_PTR(&pin_PB09) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_TFT_BACKLIGHT), MP_ROM_PTR(&pin_PB31) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_LCD_DATA0), MP_ROM_PTR(&pin_PA16) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_LCD_DATA1), MP_ROM_PTR(&pin_PA17) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_LCD_DATA2), MP_ROM_PTR(&pin_PA18) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_LCD_DATA3), MP_ROM_PTR(&pin_PA19) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_LCD_DATA4), MP_ROM_PTR(&pin_PA20) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_LCD_DATA5), MP_ROM_PTR(&pin_PA21) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_LCD_DATA6), MP_ROM_PTR(&pin_PA22) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_LCD_DATA7), MP_ROM_PTR(&pin_PA23) }, + + // Touch pins + { MP_OBJ_NEW_QSTR(MP_QSTR_TOUCH_YD), MP_ROM_PTR(&pin_PB00) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_TOUCH_XL), MP_ROM_PTR(&pin_PB01) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_TOUCH_YU), MP_ROM_PTR(&pin_PA06) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_TOUCH_XR), MP_ROM_PTR(&pin_PB08) }, + + // ESP control + { MP_OBJ_NEW_QSTR(MP_QSTR_ESP_CS), MP_ROM_PTR(&pin_PB14) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ESP_GPIO0), MP_ROM_PTR(&pin_PB15) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ESP_BUSY), MP_ROM_PTR(&pin_PB16) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ESP_RESET), MP_ROM_PTR(&pin_PB17) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ESP_RTS), MP_ROM_PTR(&pin_PA15) }, + + // UART + { MP_OBJ_NEW_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_PB12) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_PB13) }, + + // SPI + { MP_OBJ_NEW_QSTR(MP_QSTR_MOSI),MP_ROM_PTR(&pin_PA12) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_SCK),MP_ROM_PTR(&pin_PA13) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_MISO),MP_ROM_PTR(&pin_PA14) }, + + // I2C + { MP_OBJ_NEW_QSTR(MP_QSTR_SDA),MP_ROM_PTR(&pin_PB02) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_SCL),MP_ROM_PTR(&pin_PB03) }, + + // SD Card + { MP_OBJ_NEW_QSTR(MP_QSTR_SD_CS),MP_ROM_PTR(&pin_PB30) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_SD_CARD_DETECT),MP_ROM_PTR(&pin_PA01) }, + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, + + // TFT control pins + {MP_OBJ_NEW_QSTR(MP_QSTR_TFT_LITE), MP_ROM_PTR(&pin_PB31)}, + {MP_OBJ_NEW_QSTR(MP_QSTR_TFT_MOSI), MP_ROM_PTR(&pin_PA12)}, + {MP_OBJ_NEW_QSTR(MP_QSTR_TFT_SCK), MP_ROM_PTR(&pin_PA13)}, + {MP_OBJ_NEW_QSTR(MP_QSTR_TFT_MISO), MP_ROM_PTR(&pin_PA14)}, + {MP_OBJ_NEW_QSTR(MP_QSTR_TFT_RST), MP_ROM_PTR(&pin_PA00)}, + {MP_ROM_QSTR(MP_QSTR_TFT_CS), MP_ROM_PTR(&pin_PB06)}, + {MP_ROM_QSTR(MP_QSTR_TFT_DC), MP_ROM_PTR(&pin_PB05)}, + + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_global_dict_table); diff --git a/ports/atmel-samd/common-hal/audiobusio/PDMIn.c b/ports/atmel-samd/common-hal/audiobusio/PDMIn.c index b173181e0..76010a684 100644 --- a/ports/atmel-samd/common-hal/audiobusio/PDMIn.c +++ b/ports/atmel-samd/common-hal/audiobusio/PDMIn.c @@ -385,7 +385,7 @@ uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t* se init_event_channel_interrupt(event_channel, CORE_GCLK, EVSYS_ID_GEN_DMAC_CH_0 + dma_channel); // Turn on serializer now to get it in sync with DMA. i2s_set_serializer_enable(self->serializer, true); - dma_enable_channel(dma_channel); + audio_dma_enable_channel(dma_channel); // Record uint32_t buffers_processed = 0; @@ -464,7 +464,7 @@ uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t* se } disable_event_channel(event_channel); - dma_disable_channel(dma_channel); + audio_dma_disable_channel(dma_channel); // Turn off serializer, but leave clock on, to avoid mic startup delay. i2s_set_serializer_enable(self->serializer, false); diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index 9da2c498d..fad127514 100755 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -95,8 +95,7 @@ ifeq ($(DEBUG), 1) CFLAGS += -fno-inline -fno-ipa-sra else CFLAGS += -Os -DNDEBUG - # TODO: Test with -flto - ### CFLAGS += -flto + CFLAGS += -flto -flto-partition=none endif diff --git a/ports/nrf/boards/circuitplayground_bluefruit/board.c b/ports/nrf/boards/circuitplayground_bluefruit/board.c new file mode 100644 index 000000000..4421970ee --- /dev/null +++ b/ports/nrf/boards/circuitplayground_bluefruit/board.c @@ -0,0 +1,38 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * + * 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 "boards/board.h" + +void board_init(void) { +} + +bool board_requests_safe_mode(void) { + return false; +} + +void reset_board(void) { + +} diff --git a/ports/nrf/boards/circuitplayground_bluefruit/mpconfigboard.h b/ports/nrf/boards/circuitplayground_bluefruit/mpconfigboard.h new file mode 100644 index 000000000..04c7e8d60 --- /dev/null +++ b/ports/nrf/boards/circuitplayground_bluefruit/mpconfigboard.h @@ -0,0 +1,73 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Glenn Ruben Bakke + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * + * 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 "nrfx/hal/nrf_gpio.h" + +#define MICROPY_HW_BOARD_NAME "Adafruit Circuit Playground Bluefruit" +#define MICROPY_HW_MCU_NAME "nRF52840" +#define MICROPY_PY_SYS_PLATFORM "CircuitPlaygroundBluefruit" + +#define FLASH_SIZE (0x100000) +#define FLASH_PAGE_SIZE (4096) + +#define MICROPY_HW_NEOPIXEL (&pin_P0_13) + +#define MICROPY_HW_LED_STATUS (&pin_P1_14) + +#if QSPI_FLASH_FILESYSTEM +#define MICROPY_QSPI_DATA0 NRF_GPIO_PIN_MAP(0, 21) +#define MICROPY_QSPI_DATA1 NRF_GPIO_PIN_MAP(0, 23) +#define MICROPY_QSPI_DATA2 NRF_GPIO_PIN_MAP(1, 00) +#define MICROPY_QSPI_DATA3 NRF_GPIO_PIN_MAP(0, 22) +#define MICROPY_QSPI_SCK NRF_GPIO_PIN_MAP(0, 19) +#define MICROPY_QSPI_CS NRF_GPIO_PIN_MAP(0, 15) +#endif + +#if SPI_FLASH_FILESYSTEM +#define SPI_FLASH_MOSI_PIN &pin_P0_21 +#define SPI_FLASH_MISO_PIN &pin_P0_23 +#define SPI_FLASH_SCK_PIN &pin_P0_19 +#define SPI_FLASH_CS_PIN &pin_P0_15 +#endif + +#define CIRCUITPY_AUTORELOAD_DELAY_MS 500 + +#define CIRCUITPY_INTERNAL_NVM_SIZE (4096) + +#define BOARD_FLASH_SIZE (FLASH_SIZE - 0x4000 - CIRCUITPY_INTERNAL_NVM_SIZE) + +#define BOARD_HAS_CRYSTAL 1 + +#define DEFAULT_I2C_BUS_SCL (&pin_P0_04) +#define DEFAULT_I2C_BUS_SDA (&pin_P0_05) + +#define DEFAULT_SPI_BUS_SCK (&pin_P0_05) +#define DEFAULT_SPI_BUS_MOSI (&pin_P1_03) +#define DEFAULT_SPI_BUS_MISO (&pin_P0_29) + +#define DEFAULT_UART_BUS_RX (&pin_P0_30) +#define DEFAULT_UART_BUS_TX (&pin_P0_14) diff --git a/ports/nrf/boards/circuitplayground_bluefruit/mpconfigboard.mk b/ports/nrf/boards/circuitplayground_bluefruit/mpconfigboard.mk new file mode 100644 index 000000000..dbb32629c --- /dev/null +++ b/ports/nrf/boards/circuitplayground_bluefruit/mpconfigboard.mk @@ -0,0 +1,26 @@ +USB_VID = 0x239A +USB_PID = 0x8046 +USB_PRODUCT = "Circuit Playground Bluefruit" +USB_MANUFACTURER = "Adafruit Industries LLC" + +MCU_SERIES = m4 +MCU_VARIANT = nrf52 +MCU_SUB_VARIANT = nrf52840 +MCU_CHIP = nrf52840 +SD ?= s140 +SOFTDEV_VERSION ?= 6.1.0 + +BOOT_SETTING_ADDR = 0xFF000 + +ifeq ($(SD),) + LD_FILE = boards/nrf52840_1M_256k.ld +else + LD_FILE = boards/adafruit_$(MCU_SUB_VARIANT)_$(SD_LOWER)_v$(firstword $(subst ., ,$(SOFTDEV_VERSION))).ld + CIRCUITPY_BLEIO = 1 +endif + +NRF_DEFINES += -DNRF52840_XXAA -DNRF52840 + +QSPI_FLASH_FILESYSTEM = 1 +EXTERNAL_FLASH_DEVICE_COUNT = 1 +EXTERNAL_FLASH_DEVICES = "GD25Q16C" diff --git a/ports/nrf/boards/circuitplayground_bluefruit/pins.c b/ports/nrf/boards/circuitplayground_bluefruit/pins.c new file mode 100644 index 000000000..556651679 --- /dev/null +++ b/ports/nrf/boards/circuitplayground_bluefruit/pins.c @@ -0,0 +1,71 @@ +#include "shared-bindings/board/__init__.h" + +STATIC const mp_rom_map_elem_t board_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR_AUDIO), MP_ROM_PTR(&pin_P0_26) }, + { MP_ROM_QSTR(MP_QSTR_D12), MP_ROM_PTR(&pin_P0_26) }, + { MP_ROM_QSTR(MP_QSTR_SPEAKER), MP_ROM_PTR(&pin_P0_26) }, + + { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_P0_05) }, + { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_P0_05) }, + { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_P0_05) }, + + { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_P0_29) }, + { MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_P0_29) }, + { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_P0_29) }, + + { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_P0_03) }, + { MP_ROM_QSTR(MP_QSTR_D10), MP_ROM_PTR(&pin_P0_03) }, + { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_P0_03) }, + + { MP_ROM_QSTR(MP_QSTR_A4), MP_ROM_PTR(&pin_P0_04) }, + { MP_ROM_QSTR(MP_QSTR_D3), MP_ROM_PTR(&pin_P0_04) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_P0_04) }, + + { MP_ROM_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_P0_05) }, + { MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_P0_05) }, + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_P0_05) }, + + { MP_ROM_QSTR(MP_QSTR_A6), MP_ROM_PTR(&pin_P0_30) }, + { MP_ROM_QSTR(MP_QSTR_D0), MP_ROM_PTR(&pin_P0_30) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_P0_30) }, + + { MP_ROM_QSTR(MP_QSTR_A7), MP_ROM_PTR(&pin_P0_14) }, + { MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_P0_14) }, + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_P0_14) }, + + { MP_ROM_QSTR(MP_QSTR_LIGHT), MP_ROM_PTR(&pin_P0_28) }, + { MP_ROM_QSTR(MP_QSTR_A8), MP_ROM_PTR(&pin_P0_28) }, + + { MP_ROM_QSTR(MP_QSTR_TEMPERATURE), MP_ROM_PTR(&pin_P0_31) }, + { MP_ROM_QSTR(MP_QSTR_A9), MP_ROM_PTR(&pin_P0_31) }, + + { MP_ROM_QSTR(MP_QSTR_BUTTON_A), MP_ROM_PTR(&pin_P1_02) }, + { MP_ROM_QSTR(MP_QSTR_D4), MP_ROM_PTR(&pin_P1_02) }, + + { MP_ROM_QSTR(MP_QSTR_BUTTON_B), MP_ROM_PTR(&pin_P1_15) }, + { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_P1_15) }, + + { MP_ROM_QSTR(MP_QSTR_SLIDE_SWITCH), MP_ROM_PTR(&pin_P1_06) }, + { MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_P1_06) }, + + { MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_P1_14) }, + { MP_ROM_QSTR(MP_QSTR_L), MP_ROM_PTR(&pin_P1_14) }, + + { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_P0_13) }, + { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_P0_13) }, + + { MP_ROM_QSTR(MP_QSTR_MICROPHONE_CLOCK), MP_ROM_PTR(&pin_P0_17) }, + { MP_ROM_QSTR(MP_QSTR_MICROPHONE_DATA), MP_ROM_PTR(&pin_P0_16) }, + + { MP_ROM_QSTR(MP_QSTR_ACCELEROMETER_INTERRUPT), MP_ROM_PTR(&pin_P1_13) }, + { MP_ROM_QSTR(MP_QSTR_ACCELEROMETER_SDA), MP_ROM_PTR(&pin_P1_10) }, + { MP_ROM_QSTR(MP_QSTR_ACCELEROMETER_SCL), MP_ROM_PTR(&pin_P1_12) }, + + { MP_ROM_QSTR(MP_QSTR_SPEAKER_ENABLE), MP_ROM_PTR(&pin_P1_04) }, + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, +}; + +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/nrf/boards/make-pins.py b/ports/nrf/boards/make-pins.py deleted file mode 100644 index d1cfd5b70..000000000 --- a/ports/nrf/boards/make-pins.py +++ /dev/null @@ -1,208 +0,0 @@ -#!/usr/bin/env python -"""Creates the pin file for the nRF5.""" - -from __future__ import print_function - -import argparse -import sys -import csv - - -def parse_port_pin(name_str): - """Parses a string and returns a (port-num, pin-num) tuple.""" - if len(name_str) < 4: - raise ValueError("Expecting pin name to be at least 5 charcters.") - if name_str[0] != 'P': - raise ValueError("Expecting pin name to start with P") - if name_str[1] not in ('0', '1'): - raise ValueError("Expecting pin port to be in 0 or 1") - port = ord(name_str[1]) - ord('0') - pin_str = name_str[3:] - if not pin_str.isdigit(): - raise ValueError("Expecting numeric pin number.") - return (port, int(pin_str)) - - -class Pin(object): - """Holds the information associated with a pin.""" - - def __init__(self, port, pin): - self.port = port - self.pin = pin - self.adc_channel = '0' - self.board_pin = False - - def cpu_pin_name(self): - return 'P{:d}_{:02d}'.format(self.port, self.pin) - - def is_board_pin(self): - return self.board_pin - - def set_is_board_pin(self): - self.board_pin = True - - def parse_adc(self, adc_str): - if (adc_str[:3] != 'AIN'): - return - self.adc_channel = 'SAADC_CH_PSELP_PSELP_AnalogInput%d' % int(adc_str[3]) - - def print(self): - print('const pin_obj_t pin_{:s} = PIN({:s}, {:d}, {:d}, {:s});'.format( - self.cpu_pin_name(), self.cpu_pin_name(), - self.port, self.pin, self.adc_channel)) - - def print_header(self, hdr_file): - hdr_file.write('extern const pin_obj_t pin_{:s};\n'. - format(self.cpu_pin_name())) - - -class NamedPin(object): - - def __init__(self, name, pin): - self._name = name - self._pin = pin - - def pin(self): - return self._pin - - def name(self): - return self._name - - -class Pins(object): - - def __init__(self): - self.cpu_pins = [] # list of NamedPin objects - self.board_pins = [] # list of NamedPin objects - - def find_pin(self, port_num, pin_num): - for named_pin in self.cpu_pins: - pin = named_pin.pin() - if pin.port == port_num and pin.pin == pin_num: - return pin - - def parse_af_file(self, filename): - with open(filename, 'r') as csvfile: - rows = csv.reader(csvfile) - for row in rows: - try: - (port_num, pin_num) = parse_port_pin(row[0]) - except: - continue - pin = Pin(port_num, pin_num) - if len(row) > 1: - pin.parse_adc(row[1]) - self.cpu_pins.append(NamedPin(pin.cpu_pin_name(), pin)) - - def parse_board_file(self, filename): - with open(filename, 'r') as csvfile: - rows = csv.reader(csvfile) - for row in rows: - try: - (port_num, pin_num) = parse_port_pin(row[1]) - except: - continue - pin = self.find_pin(port_num, pin_num) - if pin: - pin.set_is_board_pin() - self.board_pins.append(NamedPin(row[0], pin)) - - def print_named(self, label, named_pins): - print('') - print('STATIC const mp_rom_map_elem_t {:s}_table[] = {{'.format(label)) - for named_pin in named_pins: - pin = named_pin.pin() - if pin.is_board_pin(): - print(' {{ MP_ROM_QSTR(MP_QSTR_{:s}), MP_ROM_PTR(&pin_{:s}) }},'.format(named_pin.name(), pin.cpu_pin_name())) - print('};') - print('MP_DEFINE_CONST_DICT({:s}, {:s}_table);'.format(label, label)) - - def print(self): - for named_pin in self.cpu_pins: - pin = named_pin.pin() - if pin.is_board_pin(): - pin.print() - self.print_named('mcu_pin_globals', self.cpu_pins) - self.print_named('board_module_globals', self.board_pins) - - def print_header(self, hdr_filename): - with open(hdr_filename, 'wt') as hdr_file: - for named_pin in self.cpu_pins: - pin = named_pin.pin() - if pin.is_board_pin(): - pin.print_header(hdr_file) - - def print_qstr(self, qstr_filename): - with open(qstr_filename, 'wt') as qstr_file: - qstr_set = set([]) - for named_pin in self.cpu_pins: - pin = named_pin.pin() - if pin.is_board_pin(): - qstr_set |= set([named_pin.name()]) - for named_pin in self.board_pins: - qstr_set |= set([named_pin.name()]) - for qstr in sorted(qstr_set): - print('Q({})'.format(qstr), file=qstr_file) - - -def main(): - parser = argparse.ArgumentParser( - prog="make-pins.py", - usage="%(prog)s [options] [command]", - description="Generate board specific pin file" - ) - parser.add_argument( - "-a", "--af", - dest="af_filename", - help="Specifies the alternate function file for the chip", - default="nrf_af.csv" - ) - parser.add_argument( - "-b", "--board", - dest="board_filename", - help="Specifies the board file", - ) - parser.add_argument( - "-p", "--prefix", - dest="prefix_filename", - help="Specifies beginning portion of generated pins file", - default="nrf52_prefix.c" - ) - parser.add_argument( - "-q", "--qstr", - dest="qstr_filename", - help="Specifies name of generated qstr header file", - default="build/pins_qstr.h" - ) - parser.add_argument( - "-r", "--hdr", - dest="hdr_filename", - help="Specifies name of generated pin header file", - default="build/pins.h" - ) - args = parser.parse_args(sys.argv[1:]) - - pins = Pins() - - print('// This file was automatically generated by make-pins.py') - print('//') - if args.af_filename: - print('// --af {:s}'.format(args.af_filename)) - pins.parse_af_file(args.af_filename) - - if args.board_filename: - print('// --board {:s}'.format(args.board_filename)) - pins.parse_board_file(args.board_filename) - - if args.prefix_filename: - print('// --prefix {:s}'.format(args.prefix_filename)) - print('') - with open(args.prefix_filename, 'r') as prefix_file: - print(prefix_file.read()) - pins.print() - pins.print_header(args.hdr_filename) - pins.print_qstr(args.qstr_filename) - - -if __name__ == "__main__": - main() diff --git a/ports/nrf/common-hal/audiobusio/I2SOut.c b/ports/nrf/common-hal/audiobusio/I2SOut.c new file mode 100644 index 000000000..8be1fb2f8 --- /dev/null +++ b/ports/nrf/common-hal/audiobusio/I2SOut.c @@ -0,0 +1,70 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Jeff Epler for Adafruit Industries + * + * 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 "common-hal/microcontroller/Pin.h" +#include "common-hal/audiobusio/I2SOut.h" + +#include "py/obj.h" +#include "py/runtime.h" + +void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t* self, + const mcu_pin_obj_t* bit_clock, const mcu_pin_obj_t* word_select, + const mcu_pin_obj_t* data, bool left_justified) { + mp_raise_NotImplementedError(NULL); +} + +bool common_hal_audiobusio_i2sout_deinited(audiobusio_i2sout_obj_t* self) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_audiobusio_i2sout_deinit(audiobusio_i2sout_obj_t* self) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_audiobusio_i2sout_play(audiobusio_i2sout_obj_t* self, + mp_obj_t sample, bool loop) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_audiobusio_i2sout_pause(audiobusio_i2sout_obj_t* self) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_audiobusio_i2sout_resume(audiobusio_i2sout_obj_t* self) { + mp_raise_NotImplementedError(NULL); +} + +bool common_hal_audiobusio_i2sout_get_paused(audiobusio_i2sout_obj_t* self) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_audiobusio_i2sout_stop(audiobusio_i2sout_obj_t* self) { + mp_raise_NotImplementedError(NULL); +} + +bool common_hal_audiobusio_i2sout_get_playing(audiobusio_i2sout_obj_t* self) { + mp_raise_NotImplementedError(NULL); +} diff --git a/ports/nrf/common-hal/audiobusio/I2SOut.h b/ports/nrf/common-hal/audiobusio/I2SOut.h new file mode 100644 index 000000000..01c944e72 --- /dev/null +++ b/ports/nrf/common-hal/audiobusio/I2SOut.h @@ -0,0 +1,36 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Jeff Epler for Adafruit Industries + * + * 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_NRF_COMMON_HAL_AUDIOBUSIO_I2SOUT_H +#define MICROPY_INCLUDED_NRF_COMMON_HAL_AUDIOBUSIO_I2SOUT_H + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; +} audiobusio_i2sout_obj_t; + +#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_AUDIOBUSIO_I2SOUT_H diff --git a/ports/nrf/common-hal/audiobusio/PDMIn.c b/ports/nrf/common-hal/audiobusio/PDMIn.c new file mode 100644 index 000000000..a7f9c9d74 --- /dev/null +++ b/ports/nrf/common-hal/audiobusio/PDMIn.c @@ -0,0 +1,130 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Jeff Epler for Adafruit Industries + * + * 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 "common-hal/audiobusio/PDMIn.h" +#include "shared-bindings/microcontroller/Pin.h" + +#include "py/runtime.h" + +__attribute__((used)) +NRF_PDM_Type *nrf_pdm = NRF_PDM; + +static uint32_t dummy_buffer[4]; + +void common_hal_audiobusio_pdmin_construct(audiobusio_pdmin_obj_t* self, + const mcu_pin_obj_t* clock_pin, + const mcu_pin_obj_t* data_pin, + uint32_t sample_rate, + uint8_t bit_depth, + bool mono, + uint8_t oversample) { + assert_pin_free(clock_pin); + assert_pin_free(data_pin); + claim_pin(clock_pin); + claim_pin(data_pin); + + self->mono = mono; + self->clock_pin_number = clock_pin->number; + self->data_pin_number = data_pin->number; + + if (sample_rate != 16000) { + mp_raise_ValueError(translate("only sample_rate=16000 is supported")); + } + if (bit_depth != 16) { + mp_raise_ValueError(translate("only bit_depth=16 is supported")); + } + nrf_pdm->PSEL.CLK = self->clock_pin_number; + nrf_pdm->PSEL.DIN = self->data_pin_number; + nrf_pdm->PDMCLKCTRL = PDM_PDMCLKCTRL_FREQ_Default; // For Ratio64 + nrf_pdm->RATIO = PDM_RATIO_RATIO_Ratio64; + nrf_pdm->GAINL = PDM_GAINL_GAINL_DefaultGain; + nrf_pdm->GAINR = PDM_GAINR_GAINR_DefaultGain; + nrf_pdm->ENABLE = 1; + + nrf_pdm->SAMPLE.PTR = (uintptr_t)&dummy_buffer; + nrf_pdm->SAMPLE.MAXCNT = 1; + nrf_pdm->TASKS_START = 1; +} + +bool common_hal_audiobusio_pdmin_deinited(audiobusio_pdmin_obj_t* self) { + return !self->clock_pin_number; +} + +void common_hal_audiobusio_pdmin_deinit(audiobusio_pdmin_obj_t* self) { + nrf_pdm->ENABLE = 0; + + reset_pin_number(self->clock_pin_number); + self->clock_pin_number = 0; + reset_pin_number(self->data_pin_number); + self->data_pin_number = 0; +} + +uint8_t common_hal_audiobusio_pdmin_get_bit_depth(audiobusio_pdmin_obj_t* self) { + return 16; +} + +uint32_t common_hal_audiobusio_pdmin_get_sample_rate(audiobusio_pdmin_obj_t* self) { + return 16000; +} + +uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t* self, + uint16_t* output_buffer, uint32_t output_buffer_length) { + // Note: Adafruit's module has SELECT pulled to GND, which makes the DATA + // valid when the CLK is low, therefore it must be sampled on the rising edge. + if (self->mono) { + nrf_pdm->MODE = PDM_MODE_OPERATION_Stereo | PDM_MODE_EDGE_LeftRising; + } else { + nrf_pdm->MODE = PDM_MODE_OPERATION_Mono | PDM_MODE_EDGE_LeftRising; + } + + // step 1. Redirect to real buffer + nrf_pdm->SAMPLE.PTR = (uintptr_t)output_buffer; + nrf_pdm->SAMPLE.MAXCNT = output_buffer_length; + + // a delay is the safest simple way to ensure that the above requested sample has started + mp_hal_delay_us(200); + nrf_pdm->EVENTS_END = 0; + + // step 2. Registers are double buffered, so pre-redirect back to dummy buffer + nrf_pdm->SAMPLE.PTR = (uintptr_t)&dummy_buffer; + nrf_pdm->SAMPLE.MAXCNT = 1; + + // Step 3. wait for PDM to end + while (!nrf_pdm->EVENTS_END) { + MICROPY_VM_HOOK_LOOP; + } + + // Step 4. They want unsigned + for (uint32_t i=0; i<output_buffer_length; i++) { + output_buffer[i] += 32768; + } + + if (self->mono) { + return (output_buffer_length / 2) * 2; + } else { + return (output_buffer_length / 4) * 4; + } +} diff --git a/ports/nrf/common-hal/audiobusio/PDMIn.h b/ports/nrf/common-hal/audiobusio/PDMIn.h new file mode 100644 index 000000000..d921e42ff --- /dev/null +++ b/ports/nrf/common-hal/audiobusio/PDMIn.h @@ -0,0 +1,40 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Jeff Epler for Adafruit Industries + * + * 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_NRF_COMMON_HAL_AUDIOBUSIO_AUDIOOUT_H +#define MICROPY_INCLUDED_NRF_COMMON_HAL_AUDIOBUSIO_AUDIOOUT_H + +#include "common-hal/microcontroller/Pin.h" + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + uint8_t clock_pin_number, data_pin_number; + bool mono; +} audiobusio_pdmin_obj_t; + +#endif diff --git a/ports/nrf/common-hal/audiobusio/__init__.c b/ports/nrf/common-hal/audiobusio/__init__.c new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ports/nrf/common-hal/audiobusio/__init__.c diff --git a/ports/nrf/common-hal/bleio/Adapter.c b/ports/nrf/common-hal/bleio/Adapter.c index 9d0912930..936dfcb9b 100644 --- a/ports/nrf/common-hal/bleio/Adapter.c +++ b/ports/nrf/common-hal/bleio/Adapter.c @@ -34,10 +34,11 @@ #include "nrfx_power.h" #include "nrf_nvic.h" #include "nrf_sdm.h" +#include "py/objstr.h" #include "py/runtime.h" -#include "shared-bindings/bleio/Adapter.h" - #include "supervisor/usb.h" +#include "shared-bindings/bleio/Adapter.h" +#include "shared-bindings/bleio/Address.h" STATIC void softdevice_assert_handler(uint32_t id, uint32_t pc, uint32_t info) { mp_raise_msg_varg(&mp_type_AssertionError, @@ -132,20 +133,42 @@ bool common_hal_bleio_adapter_get_enabled(void) { return is_enabled; } -void common_hal_bleio_adapter_get_address(bleio_address_obj_t *address) { - ble_gap_addr_t local_address; +void get_address(ble_gap_addr_t *address) { uint32_t err_code; common_hal_bleio_adapter_set_enabled(true); - err_code = sd_ble_gap_addr_get(&local_address); + err_code = sd_ble_gap_addr_get(address); if (err_code != NRF_SUCCESS) { mp_raise_OSError_msg(translate("Failed to get local address")); } +} + +bleio_address_obj_t *common_hal_bleio_adapter_get_address(void) { + common_hal_bleio_adapter_set_enabled(true); + + ble_gap_addr_t local_address; + get_address(&local_address); + + bleio_address_obj_t *address = m_new_obj(bleio_address_obj_t); + address->base.type = &bleio_address_type; + + common_hal_bleio_address_construct(address, local_address.addr, local_address.addr_type); + return address; +} + +mp_obj_t common_hal_bleio_adapter_get_default_name(void) { + common_hal_bleio_adapter_set_enabled(true); + + ble_gap_addr_t local_address; + get_address(&local_address); + + char name[] = { 'C', 'I', 'R', 'C', 'U', 'I', 'T', 'P', 'Y', 0, 0, 0, 0 }; - address->type = local_address.addr_type; + name[sizeof(name) - 4] = nibble_to_hex_lower[local_address.addr[1] >> 4 & 0xf]; + name[sizeof(name) - 3] = nibble_to_hex_lower[local_address.addr[1] & 0xf]; + name[sizeof(name) - 2] = nibble_to_hex_lower[local_address.addr[0] >> 4 & 0xf]; + name[sizeof(name) - 1] = nibble_to_hex_lower[local_address.addr[0] & 0xf]; - mp_buffer_info_t buf_info; - mp_get_buffer_raise(address, &buf_info, MP_BUFFER_READ); - memcpy(address->bytes, buf_info.buf, NUM_BLEIO_ADDRESS_BYTES); + return mp_obj_new_str(name, sizeof(name)); } diff --git a/ports/nrf/common-hal/bleio/Attribute.c b/ports/nrf/common-hal/bleio/Attribute.c new file mode 100644 index 000000000..fd1e7538d --- /dev/null +++ b/ports/nrf/common-hal/bleio/Attribute.c @@ -0,0 +1,60 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * + * 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 "shared-bindings/bleio/Attribute.h" + +// Convert a bleio security mode to a ble_gap_conn_sec_mode_t setting. +void bleio_attribute_gatts_set_security_mode(ble_gap_conn_sec_mode_t *perm, bleio_attribute_security_mode_t security_mode) { + switch (security_mode) { + case SECURITY_MODE_NO_ACCESS: + BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(perm); + break; + + case SECURITY_MODE_OPEN: + BLE_GAP_CONN_SEC_MODE_SET_OPEN(perm); + break; + + case SECURITY_MODE_ENC_NO_MITM: + BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(perm); + break; + + case SECURITY_MODE_ENC_WITH_MITM: + BLE_GAP_CONN_SEC_MODE_SET_ENC_WITH_MITM(perm); + break; + + case SECURITY_MODE_LESC_ENC_WITH_MITM: + BLE_GAP_CONN_SEC_MODE_SET_LESC_ENC_WITH_MITM(perm); + break; + + case SECURITY_MODE_SIGNED_NO_MITM: + BLE_GAP_CONN_SEC_MODE_SET_SIGNED_NO_MITM(perm); + break; + + case SECURITY_MODE_SIGNED_WITH_MITM: + BLE_GAP_CONN_SEC_MODE_SET_SIGNED_WITH_MITM(perm); + break; + } +} diff --git a/ports/nrf/common-hal/bleio/Attribute.h b/ports/nrf/common-hal/bleio/Attribute.h new file mode 100644 index 000000000..44c2fdfa1 --- /dev/null +++ b/ports/nrf/common-hal/bleio/Attribute.h @@ -0,0 +1,32 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * + * 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_NRF_COMMON_HAL_BLEIO_ATTRIBUTE_H +#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_ATTRIBUTE_H + +// Nothing yet. + +#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_ATTRIBUTE_H diff --git a/ports/nrf/common-hal/bleio/Central.c b/ports/nrf/common-hal/bleio/Central.c index 72657334c..3c5d97784 100644 --- a/ports/nrf/common-hal/bleio/Central.c +++ b/ports/nrf/common-hal/bleio/Central.c @@ -34,217 +34,9 @@ #include "nrf_soc.h" #include "py/objstr.h" #include "py/runtime.h" +#include "shared-bindings/bleio/__init__.h" #include "shared-bindings/bleio/Adapter.h" -#include "shared-bindings/bleio/Characteristic.h" #include "shared-bindings/bleio/Central.h" -#include "shared-bindings/bleio/Descriptor.h" -#include "shared-bindings/bleio/Service.h" -#include "shared-bindings/bleio/UUID.h" - -static bleio_service_obj_t *m_char_discovery_service; -static bleio_characteristic_obj_t *m_desc_discovery_characteristic; - -static volatile bool m_discovery_in_process; -static volatile bool m_discovery_successful; - -// service_uuid may be NULL, to discover all services. -STATIC bool discover_next_services(bleio_central_obj_t *self, uint16_t start_handle, ble_uuid_t *service_uuid) { - m_discovery_successful = false; - m_discovery_in_process = true; - - uint32_t err_code = sd_ble_gattc_primary_services_discover(self->conn_handle, start_handle, service_uuid); - - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg(translate("Failed to discover services")); - } - - // Wait for a discovery event. - while (m_discovery_in_process) { - RUN_BACKGROUND_TASKS; - } - return m_discovery_successful; -} - -STATIC bool discover_next_characteristics(bleio_central_obj_t *self, bleio_service_obj_t *service, uint16_t start_handle) { - m_char_discovery_service = service; - - ble_gattc_handle_range_t handle_range; - handle_range.start_handle = start_handle; - handle_range.end_handle = service->end_handle; - - m_discovery_successful = false; - m_discovery_in_process = true; - - uint32_t err_code = sd_ble_gattc_characteristics_discover(self->conn_handle, &handle_range); - if (err_code != NRF_SUCCESS) { - return false; - } - - // Wait for a discovery event. - while (m_discovery_in_process) { - RUN_BACKGROUND_TASKS; - } - return m_discovery_successful; -} - -STATIC bool discover_next_descriptors(bleio_central_obj_t *self, bleio_characteristic_obj_t *characteristic, uint16_t start_handle, uint16_t end_handle) { - m_desc_discovery_characteristic = characteristic; - - ble_gattc_handle_range_t handle_range; - handle_range.start_handle = start_handle; - handle_range.end_handle = end_handle; - - m_discovery_successful = false; - m_discovery_in_process = true; - - uint32_t err_code = sd_ble_gattc_descriptors_discover(self->conn_handle, &handle_range); - if (err_code != NRF_SUCCESS) { - return false; - } - - // Wait for a discovery event. - while (m_discovery_in_process) { - RUN_BACKGROUND_TASKS; - } - return m_discovery_successful; -} - -STATIC void on_primary_srv_discovery_rsp(ble_gattc_evt_prim_srvc_disc_rsp_t *response, bleio_central_obj_t *central) { - for (size_t i = 0; i < response->count; ++i) { - ble_gattc_service_t *gattc_service = &response->services[i]; - - bleio_service_obj_t *service = m_new_obj(bleio_service_obj_t); - service->base.type = &bleio_service_type; - - // Initialize several fields at once. - common_hal_bleio_service_construct(service, NULL, mp_obj_new_list(0, NULL), false); - - service->device = MP_OBJ_FROM_PTR(central); - service->start_handle = gattc_service->handle_range.start_handle; - service->end_handle = gattc_service->handle_range.end_handle; - service->handle = gattc_service->handle_range.start_handle; - - if (gattc_service->uuid.type != BLE_UUID_TYPE_UNKNOWN) { - // Known service UUID. - bleio_uuid_obj_t *uuid = m_new_obj(bleio_uuid_obj_t); - uuid->base.type = &bleio_uuid_type; - bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_service->uuid); - service->uuid = uuid; - service->device = MP_OBJ_FROM_PTR(central); - } else { - // The discovery response contained a 128-bit UUID that has not yet been registered with the - // softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it. - // For now, just set the UUID to NULL. - service->uuid = NULL; - } - - mp_obj_list_append(central->service_list, service); - } - - if (response->count > 0) { - m_discovery_successful = true; - } - m_discovery_in_process = false; -} - -STATIC void on_char_discovery_rsp(ble_gattc_evt_char_disc_rsp_t *response, bleio_central_obj_t *central) { - for (size_t i = 0; i < response->count; ++i) { - ble_gattc_char_t *gattc_char = &response->chars[i]; - - bleio_characteristic_obj_t *characteristic = m_new_obj(bleio_characteristic_obj_t); - characteristic->base.type = &bleio_characteristic_type; - - characteristic->descriptor_list = mp_obj_new_list(0, NULL); - - bleio_uuid_obj_t *uuid = NULL; - - if (gattc_char->uuid.type != BLE_UUID_TYPE_UNKNOWN) { - // Known characteristic UUID. - uuid = m_new_obj(bleio_uuid_obj_t); - uuid->base.type = &bleio_uuid_type; - bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_char->uuid); - } else { - // The discovery response contained a 128-bit UUID that has not yet been registered with the - // softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it. - // For now, just leave the UUID as NULL. - } - - bleio_characteristic_properties_t props; - - props.broadcast = gattc_char->char_props.broadcast; - props.indicate = gattc_char->char_props.indicate; - props.notify = gattc_char->char_props.notify; - props.read = gattc_char->char_props.read; - props.write = gattc_char->char_props.write; - props.write_no_response = gattc_char->char_props.write_wo_resp; - - // Call common_hal_bleio_characteristic_construct() to initalize some fields and set up evt handler. - common_hal_bleio_characteristic_construct(characteristic, uuid, props, mp_obj_new_list(0, NULL)); - characteristic->handle = gattc_char->handle_value; - characteristic->service = m_char_discovery_service; - - mp_obj_list_append(m_char_discovery_service->characteristic_list, MP_OBJ_FROM_PTR(characteristic)); - } - - if (response->count > 0) { - m_discovery_successful = true; - } - m_discovery_in_process = false; -} - -STATIC void on_desc_discovery_rsp(ble_gattc_evt_desc_disc_rsp_t *response, bleio_central_obj_t *central) { - for (size_t i = 0; i < response->count; ++i) { - ble_gattc_desc_t *gattc_desc = &response->descs[i]; - - // Remember handles for certain well-known descriptors. - switch (gattc_desc->uuid.uuid) { - case DESCRIPTOR_UUID_CLIENT_CHARACTERISTIC_CONFIGURATION: - m_desc_discovery_characteristic->cccd_handle = gattc_desc->handle; - break; - - case DESCRIPTOR_UUID_SERVER_CHARACTERISTIC_CONFIGURATION: - m_desc_discovery_characteristic->sccd_handle = gattc_desc->handle; - break; - - case DESCRIPTOR_UUID_CHARACTERISTIC_USER_DESCRIPTION: - m_desc_discovery_characteristic->user_desc_handle = gattc_desc->handle; - break; - - default: - // TODO: sd_ble_gattc_descriptors_discover() can return things that are not descriptors, - // so ignore those. - // https://devzone.nordicsemi.com/f/nordic-q-a/49500/sd_ble_gattc_descriptors_discover-is-returning-attributes-that-are-not-descriptors - break; - } - - bleio_descriptor_obj_t *descriptor = m_new_obj(bleio_descriptor_obj_t); - descriptor->base.type = &bleio_descriptor_type; - - bleio_uuid_obj_t *uuid = NULL; - - if (gattc_desc->uuid.type != BLE_UUID_TYPE_UNKNOWN) { - // Known descriptor UUID. - uuid = m_new_obj(bleio_uuid_obj_t); - uuid->base.type = &bleio_uuid_type; - bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_desc->uuid); - } else { - // The discovery response contained a 128-bit UUID that has not yet been registered with the - // softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it. - // For now, just leave the UUID as NULL. - } - - common_hal_bleio_descriptor_construct(descriptor, uuid); - descriptor->handle = gattc_desc->handle; - descriptor->characteristic = m_desc_discovery_characteristic; - - mp_obj_list_append(m_desc_discovery_characteristic->descriptor_list, MP_OBJ_FROM_PTR(descriptor)); - } - - if (response->count > 0) { - m_discovery_successful = true; - } - m_discovery_in_process = false; -} STATIC void central_on_ble_evt(ble_evt_t *ble_evt, void *central_in) { bleio_central_obj_t *central = (bleio_central_obj_t*)central_in; @@ -262,20 +54,6 @@ STATIC void central_on_ble_evt(ble_evt_t *ble_evt, void *central_in) { case BLE_GAP_EVT_DISCONNECTED: central->conn_handle = BLE_CONN_HANDLE_INVALID; - m_discovery_successful = false; - m_discovery_in_process = false; - break; - - case BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP: - on_primary_srv_discovery_rsp(&ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp, central); - break; - - case BLE_GATTC_EVT_CHAR_DISC_RSP: - on_char_discovery_rsp(&ble_evt->evt.gattc_evt.params.char_disc_rsp, central); - break; - - case BLE_GATTC_EVT_DESC_DISC_RSP: - on_desc_discovery_rsp(&ble_evt->evt.gattc_evt.params.desc_disc_rsp, central); break; case BLE_GAP_EVT_SEC_PARAMS_REQUEST: @@ -288,7 +66,6 @@ STATIC void central_on_ble_evt(ble_evt_t *ble_evt, void *central_in) { sd_ble_gap_conn_param_update(central->conn_handle, &request->conn_params); break; } - default: // For debugging. // mp_printf(&mp_plat_print, "Unhandled central event: 0x%04x\n", ble_evt->header.evt_id); @@ -299,12 +76,11 @@ STATIC void central_on_ble_evt(ble_evt_t *ble_evt, void *central_in) { void common_hal_bleio_central_construct(bleio_central_obj_t *self) { common_hal_bleio_adapter_set_enabled(true); - self->service_list = mp_obj_new_list(0, NULL); - self->gatt_role = GATT_ROLE_CLIENT; + self->remote_services_list = mp_obj_new_list(0, NULL); self->conn_handle = BLE_CONN_HANDLE_INVALID; } -void common_hal_bleio_central_connect(bleio_central_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout, mp_obj_t service_uuids) { +void common_hal_bleio_central_connect(bleio_central_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout) { common_hal_bleio_adapter_set_enabled(true); ble_drv_add_event_handler(central_on_ble_evt, self); @@ -345,109 +121,6 @@ void common_hal_bleio_central_connect(bleio_central_obj_t *self, bleio_address_o if (self->conn_handle == BLE_CONN_HANDLE_INVALID) { mp_raise_OSError_msg(translate("Failed to connect: timeout")); } - - // Connection successful. - // Now discover services on the remote peripheral. - - if (service_uuids == mp_const_none) { - - // List of service UUID's not given, so discover all available services. - - uint16_t next_service_start_handle = BLE_GATT_HANDLE_START; - - while (discover_next_services(self, next_service_start_handle, MP_OBJ_NULL)) { - // discover_next_services() appends to service_list. - - // Get the most recently discovered service, and then ask for services - // whose handles start after the last attribute handle inside that service. - const bleio_service_obj_t *service = - MP_OBJ_TO_PTR(self->service_list->items[self->service_list->len - 1]); - next_service_start_handle = service->end_handle + 1; - } - } else { - mp_obj_iter_buf_t iter_buf; - mp_obj_t iterable = mp_getiter(service_uuids, &iter_buf); - mp_obj_t uuid_obj; - while ((uuid_obj = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { - if (!MP_OBJ_IS_TYPE(uuid_obj, &bleio_uuid_type)) { - mp_raise_ValueError(translate("non-UUID found in service_uuids")); - } - bleio_uuid_obj_t *uuid = MP_OBJ_TO_PTR(uuid_obj); - - ble_uuid_t nrf_uuid; - bleio_uuid_convert_to_nrf_ble_uuid(uuid, &nrf_uuid); - - // Service might or might not be discovered; that's ok. Caller has to check - // Central.remote_services to find out. - // We only need to call this once for each service to discover. - discover_next_services(self, BLE_GATT_HANDLE_START, &nrf_uuid); - } - } - - - for (size_t service_idx = 0; service_idx < self->service_list->len; ++service_idx) { - bleio_service_obj_t *service = MP_OBJ_TO_PTR(self->service_list->items[service_idx]); - - // Skip the service if it had an unknown (unregistered) UUID. - if (service->uuid == NULL) { - continue; - } - - uint16_t next_char_start_handle = service->start_handle; - - // Stop when we go past the end of the range of handles for this service or - // discovery call returns nothing. - // discover_next_characteristics() appends to the characteristic_list. - while (next_char_start_handle <= service->end_handle && - discover_next_characteristics(self, service, next_char_start_handle)) { - - - // Get the most recently discovered characteristic, and then ask for characteristics - // whose handles start after the last attribute handle inside that characteristic. - const bleio_characteristic_obj_t *characteristic = - MP_OBJ_TO_PTR(service->characteristic_list->items[service->characteristic_list->len - 1]); - next_char_start_handle = characteristic->handle + 1; - } - - // Got characteristics for this service. Now discover descriptors for each characteristic. - size_t char_list_len = service->characteristic_list->len; - for (size_t char_idx = 0; char_idx < char_list_len; ++char_idx) { - bleio_characteristic_obj_t *characteristic = - MP_OBJ_TO_PTR(service->characteristic_list->items[char_idx]); - const bool last_characteristic = char_idx == char_list_len - 1; - bleio_characteristic_obj_t *next_characteristic = last_characteristic - ? NULL - : MP_OBJ_TO_PTR(service->characteristic_list->items[char_idx + 1]); - - // Skip the characteristic if it had an unknown (unregistered) UUID. - if (characteristic->uuid == NULL) { - continue; - } - - uint16_t next_desc_start_handle = characteristic->handle + 1; - - // Don't run past the end of this service or the beginning of the next characteristic. - uint16_t next_desc_end_handle = next_characteristic == NULL - ? service->end_handle - : next_characteristic->handle - 1; - - // Stop when we go past the end of the range of handles for this service or - // discovery call returns nothing. - // discover_next_descriptors() appends to the descriptor_list. - while (next_desc_start_handle <= service->end_handle && - next_desc_start_handle < next_desc_end_handle && - discover_next_descriptors(self, characteristic, - next_desc_start_handle, next_desc_end_handle)) { - - // Get the most recently discovered descriptor, and then ask for descriptors - // whose handles start after that descriptor's handle. - const bleio_descriptor_obj_t *descriptor = - MP_OBJ_TO_PTR(characteristic->descriptor_list->items[characteristic->descriptor_list->len - 1]); - next_desc_start_handle = descriptor->handle + 1; - } - } - - } } void common_hal_bleio_central_disconnect(bleio_central_obj_t *self) { @@ -458,6 +131,15 @@ bool common_hal_bleio_central_get_connected(bleio_central_obj_t *self) { return self->conn_handle != BLE_CONN_HANDLE_INVALID; } +mp_obj_tuple_t *common_hal_bleio_central_discover_remote_services(bleio_central_obj_t *self, mp_obj_t service_uuids_whitelist) { + common_hal_bleio_device_discover_remote_services(MP_OBJ_FROM_PTR(self), service_uuids_whitelist); + // Convert to a tuple and then clear the list so the callee will take ownership. + mp_obj_tuple_t *services_tuple = mp_obj_new_tuple(self->remote_services_list->len, + self->remote_services_list->items); + mp_obj_list_clear(self->remote_services_list); + return services_tuple; +} + mp_obj_list_t *common_hal_bleio_central_get_remote_services(bleio_central_obj_t *self) { - return self->service_list; + return self->remote_services_list; } diff --git a/ports/nrf/common-hal/bleio/Central.h b/ports/nrf/common-hal/bleio/Central.h index ac1670088..b7a9050b8 100644 --- a/ports/nrf/common-hal/bleio/Central.h +++ b/ports/nrf/common-hal/bleio/Central.h @@ -31,15 +31,14 @@ #include <stdbool.h> #include "py/objlist.h" -#include "shared-module/bleio/__init__.h" #include "shared-module/bleio/Address.h" typedef struct { mp_obj_base_t base; - gatt_role_t gatt_role; volatile bool waiting_to_connect; volatile uint16_t conn_handle; - mp_obj_list_t *service_list; + // Services discovered after connecting to a remote peripheral. + mp_obj_list_t *remote_services_list; } bleio_central_obj_t; #endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CENTRAL_H diff --git a/ports/nrf/common-hal/bleio/Characteristic.c b/ports/nrf/common-hal/bleio/Characteristic.c index 8168f4671..45e8d2495 100644 --- a/ports/nrf/common-hal/bleio/Characteristic.c +++ b/ports/nrf/common-hal/bleio/Characteristic.c @@ -25,29 +25,23 @@ * THE SOFTWARE. */ -#include <string.h> -#include <stdio.h> - -#include "ble_drv.h" -#include "ble_gatts.h" -#include "nrf_soc.h" - #include "py/runtime.h" -#include "common-hal/bleio/__init__.h" -#include "common-hal/bleio/Characteristic.h" -STATIC volatile bleio_characteristic_obj_t *m_read_characteristic; +#include "shared-bindings/bleio/__init__.h" +#include "shared-bindings/bleio/Characteristic.h" +#include "shared-bindings/bleio/Descriptor.h" +#include "shared-bindings/bleio/Service.h" -STATIC uint16_t get_cccd(bleio_characteristic_obj_t *characteristic) { - const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device); +static volatile bleio_characteristic_obj_t *m_read_characteristic; + +STATIC uint16_t characteristic_get_cccd(uint16_t cccd_handle, uint16_t conn_handle) { uint16_t cccd; ble_gatts_value_t value = { .p_value = (uint8_t*) &cccd, .len = 2, }; - const uint32_t err_code = sd_ble_gatts_value_get(conn_handle, characteristic->cccd_handle, &value); - + const uint32_t err_code = sd_ble_gatts_value_get(conn_handle, cccd_handle, &value); if (err_code == BLE_ERROR_GATTS_SYS_ATTR_MISSING) { // CCCD is not set, so say that neither Notify nor Indicate is enabled. @@ -59,64 +53,39 @@ STATIC uint16_t get_cccd(bleio_characteristic_obj_t *characteristic) { return cccd; } -STATIC void gatts_read(bleio_characteristic_obj_t *characteristic) { - // This might be BLE_CONN_HANDLE_INVALID if we're not connected, but that's OK, because - // we can still read and write the local value. - const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device); - - mp_buffer_info_t bufinfo; - ble_gatts_value_t gatts_value = { - .p_value = NULL, - .len = 0, - }; - - // Read once to find out what size buffer we need, then read again to fill buffer. - - uint32_t err_code = sd_ble_gatts_value_get(conn_handle, characteristic->handle, &gatts_value); - if (err_code == NRF_SUCCESS) { - characteristic->value_data = mp_obj_new_bytearray_of_zeros(gatts_value.len); - mp_get_buffer_raise(characteristic->value_data, &bufinfo, MP_BUFFER_WRITE); - gatts_value.p_value = bufinfo.buf; - - // Read again, with the correct size of buffer. - err_code = sd_ble_gatts_value_get(conn_handle, characteristic->handle, &gatts_value); - } - - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg_varg(translate("Failed to read gatts value, err 0x%04x"), err_code); - } -} - +STATIC void characteristic_on_gattc_read_rsp_evt(ble_evt_t *ble_evt, void *param) { + switch (ble_evt->header.evt_id) { -STATIC void gatts_write(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) { - // This might be BLE_CONN_HANDLE_INVALID if we're not conected, but that's OK, because - // we can still read and write the local value. - const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device); + // More events may be handled later, so keep this as a switch. - ble_gatts_value_t gatts_value = { - .p_value = bufinfo->buf, - .len = bufinfo->len, - }; + case BLE_GATTC_EVT_READ_RSP: { + ble_gattc_evt_read_rsp_t *response = &ble_evt->evt.gattc_evt.params.read_rsp; + if (m_read_characteristic) { + m_read_characteristic->value = mp_obj_new_bytearray(response->len, response->data); + } + // Indicate to busy-wait loop that we've read the attribute value. + m_read_characteristic = NULL; + break; + } - const uint32_t err_code = sd_ble_gatts_value_set(conn_handle, characteristic->handle, &gatts_value); - if (err_code != NRF_SUCCESS) { - mp_raise_OSError_msg_varg(translate("Failed to write gatts value, err 0x%04x"), err_code); + default: + // For debugging. + // mp_printf(&mp_plat_print, "Unhandled characteristic event: 0x%04x\n", ble_evt->header.evt_id); + break; } } -STATIC void gatts_notify_indicate(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo, uint16_t hvx_type) { +STATIC void characteristic_gatts_notify_indicate(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, uint16_t hvx_type) { uint16_t hvx_len = bufinfo->len; ble_gatts_hvx_params_t hvx_params = { - .handle = characteristic->handle, + .handle = handle, .type = hvx_type, .offset = 0, .p_len = &hvx_len, .p_data = bufinfo->buf, }; - const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device); - while (1) { const uint32_t err_code = sd_ble_gatts_hvx(conn_handle, &hvx_params); if (err_code == NRF_SUCCESS) { @@ -132,21 +101,17 @@ STATIC void gatts_notify_indicate(bleio_characteristic_obj_t *characteristic, mp // Some real error has occurred. mp_raise_OSError_msg_varg(translate("Failed to notify or indicate attribute value, err 0x%04x"), err_code); } - -} - -STATIC void check_connected(uint16_t conn_handle) { - if (conn_handle == BLE_CONN_HANDLE_INVALID) { - mp_raise_OSError_msg(translate("Not connected")); - } } -STATIC void gattc_read(bleio_characteristic_obj_t *characteristic) { +STATIC void characteristic_gattc_read(bleio_characteristic_obj_t *characteristic) { const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device); - check_connected(conn_handle); + common_hal_bleio_check_connected(conn_handle); + // Set to NULL in event loop after event. m_read_characteristic = characteristic; + ble_drv_add_event_handler(characteristic_on_gattc_read_rsp_evt, characteristic); + const uint32_t err_code = sd_ble_gattc_read(conn_handle, characteristic->handle, 0); if (err_code != NRF_SUCCESS) { mp_raise_OSError_msg_varg(translate("Failed to read attribute value, err 0x%04x"), err_code); @@ -155,126 +120,102 @@ STATIC void gattc_read(bleio_characteristic_obj_t *characteristic) { while (m_read_characteristic != NULL) { RUN_BACKGROUND_TASKS; } -} - -STATIC void gattc_write(bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *bufinfo) { - const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device); - check_connected(conn_handle); - - ble_gattc_write_params_t write_params = { - .write_op = characteristic->props.write_no_response ? BLE_GATT_OP_WRITE_CMD : BLE_GATT_OP_WRITE_REQ, - .handle = characteristic->handle, - .p_value = bufinfo->buf, - .len = bufinfo->len, - }; - - while (1) { - uint32_t err_code = sd_ble_gattc_write(conn_handle, &write_params); - if (err_code == NRF_SUCCESS) { - break; - } - - // Write with response will return NRF_ERROR_BUSY if the response has not been received. - // Write without reponse will return NRF_ERROR_RESOURCES if too many writes are pending. - if (err_code == NRF_ERROR_BUSY || err_code == NRF_ERROR_RESOURCES) { - // We could wait for an event indicating the write is complete, but just retrying is easier. - RUN_BACKGROUND_TASKS; - continue; - } - - // Some real error occurred. - mp_raise_OSError_msg_varg(translate("Failed to write attribute value, err 0x%04x"), err_code); - } + ble_drv_remove_event_handler(characteristic_on_gattc_read_rsp_evt, characteristic); } -STATIC void characteristic_on_ble_evt(ble_evt_t *ble_evt, void *param) { - switch (ble_evt->header.evt_id) { - - // More events may be handled later, so keep this as a switch. - - case BLE_GATTC_EVT_READ_RSP: { - ble_gattc_evt_read_rsp_t *response = &ble_evt->evt.gattc_evt.params.read_rsp; - m_read_characteristic->value_data = mp_obj_new_bytearray(response->len, response->data); - // Indicate to busy-wait loop that we've read the characteristic. - m_read_characteristic = NULL; - break; - } - - // For debugging. - default: - // mp_printf(&mp_plat_print, "Unhandled characteristic event: 0x%04x\n", ble_evt->header.evt_id); - break; - } - -} - -void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props, mp_obj_list_t *descriptor_list) { - self->service = mp_const_none; +void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_obj_list_t *descriptor_list) { + self->service = MP_OBJ_NULL; self->uuid = uuid; - self->value_data = mp_const_none; + self->value = mp_const_empty_bytes; + self->handle = BLE_GATT_HANDLE_INVALID; self->props = props; + self->read_perm = read_perm; + self->write_perm = write_perm; self->descriptor_list = descriptor_list; - self->handle = BLE_GATT_HANDLE_INVALID; - ble_drv_add_event_handler(characteristic_on_ble_evt, self); + const mp_int_t max_length_max = fixed_length ? BLE_GATTS_FIX_ATTR_LEN_MAX : BLE_GATTS_VAR_ATTR_LEN_MAX; + if (max_length < 0 || max_length > max_length_max) { + mp_raise_ValueError_varg(translate("max_length must be 0-%d when fixed_length is %s"), + max_length_max, fixed_length ? "True" : "False"); + } + self->max_length = max_length; + self->fixed_length = fixed_length; + + for (size_t descriptor_idx = 0; descriptor_idx < descriptor_list->len; ++descriptor_idx) { + bleio_descriptor_obj_t *descriptor = + MP_OBJ_TO_PTR(descriptor_list->items[descriptor_idx]); + descriptor->characteristic = self; + } } mp_obj_list_t *common_hal_bleio_characteristic_get_descriptor_list(bleio_characteristic_obj_t *self) { return self->descriptor_list; } +bleio_service_obj_t *common_hal_bleio_characteristic_get_service(bleio_characteristic_obj_t *self) { + return self->service; +} + mp_obj_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self) { - switch (common_hal_bleio_device_get_gatt_role(self->service->device)) { - case GATT_ROLE_CLIENT: - gattc_read(self); - break; - - case GATT_ROLE_SERVER: - gatts_read(self); - break; - - default: - mp_raise_RuntimeError(translate("bad GATT role")); - break; + // Do GATT operations only if this characteristic has been added to a registered service. + if (self->handle != BLE_GATT_HANDLE_INVALID) { + uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->service->device); + if (common_hal_bleio_service_get_is_remote(self->service)) { + // self->value is set by evt handler. + characteristic_gattc_read(self); + } else { + self->value = common_hal_bleio_gatts_read(self->handle, conn_handle); + } } - return self->value_data; + return self->value; } void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo) { - bool sent = false; - uint16_t cccd = 0; + // Do GATT operations only if this characteristic has been added to a registered service. + if (self->handle != BLE_GATT_HANDLE_INVALID) { + uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->service->device); + + if (common_hal_bleio_service_get_is_remote(self->service)) { + // Last argument is true if write-no-reponse desired. + common_hal_bleio_gattc_write(self->handle, conn_handle, bufinfo, + (self->props & CHAR_PROP_WRITE_NO_RESPONSE)); + } else { + if (self->fixed_length && bufinfo->len != self->max_length) { + mp_raise_ValueError(translate("Value length required fixed length")); + } + if (bufinfo->len > self->max_length) { + mp_raise_ValueError(translate("Value length > max_length")); + } + + bool sent = false; + uint16_t cccd = 0; - switch (common_hal_bleio_device_get_gatt_role(self->service->device)) { - case GATT_ROLE_SERVER: - if (self->props.notify || self->props.indicate) { - cccd = get_cccd(self); + const bool notify = self->props & CHAR_PROP_NOTIFY; + const bool indicate = self->props & CHAR_PROP_INDICATE; + if (notify | indicate) { + cccd = characteristic_get_cccd(self->cccd_handle, conn_handle); } + // It's possible that both notify and indicate are set. - if (self->props.notify && (cccd & BLE_GATT_HVX_NOTIFICATION)) { - gatts_notify_indicate(self, bufinfo, BLE_GATT_HVX_NOTIFICATION); + if (notify && (cccd & BLE_GATT_HVX_NOTIFICATION)) { + characteristic_gatts_notify_indicate(self->handle, conn_handle, bufinfo, BLE_GATT_HVX_NOTIFICATION); sent = true; } - if (self->props.indicate && (cccd & BLE_GATT_HVX_INDICATION)) { - gatts_notify_indicate(self, bufinfo, BLE_GATT_HVX_INDICATION); + if (indicate && (cccd & BLE_GATT_HVX_INDICATION)) { + characteristic_gatts_notify_indicate(self->handle, conn_handle, bufinfo, BLE_GATT_HVX_INDICATION); sent = true; } + if (!sent) { - gatts_write(self, bufinfo); + common_hal_bleio_gatts_write(self->handle, conn_handle, bufinfo); } - break; - - case GATT_ROLE_CLIENT: - gattc_write(self, bufinfo); - break; - - default: - mp_raise_RuntimeError(translate("bad GATT role")); - break; + } } -} + self->value = mp_obj_new_bytes(bufinfo->buf, bufinfo->len); +} bleio_uuid_obj_t *common_hal_bleio_characteristic_get_uuid(bleio_characteristic_obj_t *self) { return self->uuid; @@ -289,18 +230,17 @@ void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, mp_raise_ValueError(translate("No CCCD for this Characteristic")); } - if (common_hal_bleio_device_get_gatt_role(self->service->device) != GATT_ROLE_CLIENT) { - mp_raise_ValueError(translate("Can't set CCCD for local Characteristic")); + if (!common_hal_bleio_service_get_is_remote(self->service)) { + mp_raise_ValueError(translate("Can't set CCCD on local Characteristic")); } + const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->service->device); + common_hal_bleio_check_connected(conn_handle); + uint16_t cccd_value = (notify ? BLE_GATT_HVX_NOTIFICATION : 0) | (indicate ? BLE_GATT_HVX_INDICATION : 0); - const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->service->device); - check_connected(conn_handle); - - ble_gattc_write_params_t write_params = { .write_op = BLE_GATT_OP_WRITE_REQ, .handle = self->cccd_handle, diff --git a/ports/nrf/common-hal/bleio/Characteristic.h b/ports/nrf/common-hal/bleio/Characteristic.h index 7cb227596..fb6a4c6ce 100644 --- a/ports/nrf/common-hal/bleio/Characteristic.h +++ b/ports/nrf/common-hal/bleio/Characteristic.h @@ -28,17 +28,23 @@ #ifndef MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_CHARACTERISTIC_H #define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_CHARACTERISTIC_H +#include "shared-bindings/bleio/Attribute.h" +#include "shared-module/bleio/Characteristic.h" #include "common-hal/bleio/Service.h" #include "common-hal/bleio/UUID.h" -#include "shared-module/bleio/Characteristic.h" typedef struct { mp_obj_base_t base; + // Will be MP_OBJ_NULL before being assigned to a Service. bleio_service_obj_t *service; bleio_uuid_obj_t *uuid; - volatile mp_obj_t value_data; + mp_obj_t value; + uint16_t max_length; + bool fixed_length; uint16_t handle; bleio_characteristic_properties_t props; + bleio_attribute_security_mode_t read_perm; + bleio_attribute_security_mode_t write_perm; mp_obj_list_t *descriptor_list; uint16_t user_desc_handle; uint16_t cccd_handle; diff --git a/ports/nrf/common-hal/bleio/CharacteristicBuffer.c b/ports/nrf/common-hal/bleio/CharacteristicBuffer.c index 01e0d0c84..64ab5e8a7 100644 --- a/ports/nrf/common-hal/bleio/CharacteristicBuffer.c +++ b/ports/nrf/common-hal/bleio/CharacteristicBuffer.c @@ -37,7 +37,7 @@ #include "tick.h" -#include "common-hal/bleio/__init__.h" +#include "shared-bindings/bleio/__init__.h" #include "common-hal/bleio/CharacteristicBuffer.h" STATIC void write_to_ringbuf(bleio_characteristic_buffer_obj_t *self, uint8_t *data, uint16_t len) { diff --git a/ports/nrf/common-hal/bleio/Descriptor.c b/ports/nrf/common-hal/bleio/Descriptor.c index 005af6eaa..2a2019277 100644 --- a/ports/nrf/common-hal/bleio/Descriptor.c +++ b/ports/nrf/common-hal/bleio/Descriptor.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * Copyright (c) 2019 Dan Halbert for Adafruit Industries * Copyright (c) 2018 Artur Pacholec * Copyright (c) 2016 Glenn Ruben Bakke * @@ -26,17 +26,116 @@ * THE SOFTWARE. */ -#include "common-hal/bleio/Descriptor.h" +#include "py/runtime.h" + +#include "shared-bindings/bleio/__init__.h" +#include "shared-bindings/bleio/Descriptor.h" +#include "shared-bindings/bleio/Service.h" #include "shared-bindings/bleio/UUID.h" -void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, bleio_uuid_obj_t *uuid) { +static volatile bleio_descriptor_obj_t *m_read_descriptor; + +void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, bleio_uuid_obj_t *uuid, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length) { + self->characteristic = MP_OBJ_NULL; self->uuid = uuid; -} + self->value = mp_const_empty_bytes; + self->handle = BLE_GATT_HANDLE_INVALID; + self->read_perm = read_perm; + self->write_perm = write_perm; -mp_int_t common_hal_bleio_descriptor_get_handle(bleio_descriptor_obj_t *self) { - return self->handle; + const mp_int_t max_length_max = fixed_length ? BLE_GATTS_FIX_ATTR_LEN_MAX : BLE_GATTS_VAR_ATTR_LEN_MAX; + if (max_length < 0 || max_length > max_length_max) { + mp_raise_ValueError_varg(translate("max_length must be 0-%d when fixed_length is %s"), + max_length_max, fixed_length ? "True" : "False"); + } + self->max_length = max_length; + self->fixed_length = fixed_length; } bleio_uuid_obj_t *common_hal_bleio_descriptor_get_uuid(bleio_descriptor_obj_t *self) { return self->uuid; } + +bleio_characteristic_obj_t *common_hal_bleio_descriptor_get_characteristic(bleio_descriptor_obj_t *self) { + return self->characteristic; +} + +STATIC void descriptor_on_gattc_read_rsp_evt(ble_evt_t *ble_evt, void *param) { + switch (ble_evt->header.evt_id) { + + // More events may be handled later, so keep this as a switch. + + case BLE_GATTC_EVT_READ_RSP: { + ble_gattc_evt_read_rsp_t *response = &ble_evt->evt.gattc_evt.params.read_rsp; + if (m_read_descriptor) { + m_read_descriptor->value = mp_obj_new_bytearray(response->len, response->data); + } + // Indicate to busy-wait loop that we've read the attribute value. + m_read_descriptor = NULL; + break; + } + + default: + // For debugging. + // mp_printf(&mp_plat_print, "Unhandled descriptor event: 0x%04x\n", ble_evt->header.evt_id); + break; + } +} + +STATIC void descriptor_gattc_read(bleio_descriptor_obj_t *descriptor) { + const uint16_t conn_handle = + common_hal_bleio_device_get_conn_handle(descriptor->characteristic->service->device); + common_hal_bleio_check_connected(conn_handle); + + // Set to NULL in event loop after event. + m_read_descriptor = descriptor; + + ble_drv_add_event_handler(descriptor_on_gattc_read_rsp_evt, descriptor); + + const uint32_t err_code = sd_ble_gattc_read(conn_handle, descriptor->handle, 0); + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to read attribute value, err 0x%04x"), err_code); + } + + while (m_read_descriptor != NULL) { + MICROPY_VM_HOOK_LOOP; + } + + ble_drv_remove_event_handler(descriptor_on_gattc_read_rsp_evt, descriptor); +} + +mp_obj_t common_hal_bleio_descriptor_get_value(bleio_descriptor_obj_t *self) { + // Do GATT operations only if this descriptor has been registered + if (self->handle != BLE_GATT_HANDLE_INVALID) { + if (common_hal_bleio_service_get_is_remote(self->characteristic->service)) { + descriptor_gattc_read(self); + } else { + self->value = common_hal_bleio_gatts_read( + self->handle, common_hal_bleio_device_get_conn_handle(self->characteristic->service->device)); + } + } + + return self->value; +} + +void common_hal_bleio_descriptor_set_value(bleio_descriptor_obj_t *self, mp_buffer_info_t *bufinfo) { + // Do GATT operations only if this descriptor has been registered. + if (self->handle != BLE_GATT_HANDLE_INVALID) { + uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->characteristic->service->device); + if (common_hal_bleio_service_get_is_remote(self->characteristic->service)) { + // false means WRITE_REQ, not write-no-response + common_hal_bleio_gattc_write(self->handle, conn_handle, bufinfo, false); + } else { + if (self->fixed_length && bufinfo->len != self->max_length) { + mp_raise_ValueError(translate("Value length != required fixed length")); + } + if (bufinfo->len > self->max_length) { + mp_raise_ValueError(translate("Value length > max_length")); + } + + common_hal_bleio_gatts_write(self->handle, conn_handle, bufinfo); + } + } + + self->value = mp_obj_new_bytes(bufinfo->buf, bufinfo->len); +} diff --git a/ports/nrf/common-hal/bleio/Descriptor.h b/ports/nrf/common-hal/bleio/Descriptor.h index b7af6a42f..6db4d7be3 100644 --- a/ports/nrf/common-hal/bleio/Descriptor.h +++ b/ports/nrf/common-hal/bleio/Descriptor.h @@ -30,14 +30,21 @@ #define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_DESCRIPTOR_H #include "py/obj.h" -#include "common-hal/bleio/Characteristic.h" + +#include "shared-bindings/bleio/Characteristic.h" #include "common-hal/bleio/UUID.h" typedef struct { mp_obj_base_t base; - uint16_t handle; + // Will be MP_OBJ_NULL before being assigned to a Characteristic. bleio_characteristic_obj_t *characteristic; bleio_uuid_obj_t *uuid; + mp_obj_t value; + uint16_t max_length; + bool fixed_length; + uint16_t handle; + bleio_attribute_security_mode_t read_perm; + bleio_attribute_security_mode_t write_perm; } bleio_descriptor_obj_t; #endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_DESCRIPTOR_H diff --git a/ports/nrf/common-hal/bleio/Peripheral.c b/ports/nrf/common-hal/bleio/Peripheral.c index 608bd1342..ed35516fc 100644 --- a/ports/nrf/common-hal/bleio/Peripheral.c +++ b/ports/nrf/common-hal/bleio/Peripheral.c @@ -36,12 +36,12 @@ #include "py/objlist.h" #include "py/objstr.h" #include "py/runtime.h" +#include "shared-bindings/bleio/__init__.h" #include "shared-bindings/bleio/Adapter.h" #include "shared-bindings/bleio/Characteristic.h" #include "shared-bindings/bleio/Peripheral.h" #include "shared-bindings/bleio/Service.h" #include "shared-bindings/bleio/UUID.h" -#include "common-hal/bleio/Service.h" #define BLE_MIN_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_0_625_MS) #define BLE_MAX_CONN_INTERVAL MSEC_TO_UNITS(300, UNIT_0_625_MS) @@ -52,6 +52,19 @@ #define BLE_ADV_AD_TYPE_FIELD_SIZE 1 #define BLE_AD_TYPE_FLAGS_DATA_SIZE 1 +static const ble_gap_sec_params_t pairing_sec_params = { + .bond = 0, // TODO: add bonding + .mitm = 0, + .lesc = 0, + .keypress = 0, + .oob = 0, + .io_caps = BLE_GAP_IO_CAPS_NONE, + .min_key_size = 7, + .max_key_size = 16, + .kdist_own = { .enc = 1, .id = 1}, + .kdist_peer = { .enc = 1, .id = 1}, +}; + STATIC void check_data_fit(size_t data_len) { if (data_len > BLE_GAP_ADV_SET_DATA_SIZE_MAX) { mp_raise_ValueError(translate("Data too large for advertisement packet")); @@ -61,6 +74,9 @@ STATIC void check_data_fit(size_t data_len) { STATIC void peripheral_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { bleio_peripheral_obj_t *self = (bleio_peripheral_obj_t*)self_in; + // For debugging. + // mp_printf(&mp_plat_print, "Peripheral event: 0x%04x\n", ble_evt->header.evt_id); + switch (ble_evt->header.evt_id) { case BLE_GAP_EVT_CONNECTED: { // Central has connected. @@ -89,10 +105,6 @@ STATIC void peripheral_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { // Someday may handle timeouts or limit reached. break; - case BLE_GAP_EVT_SEC_PARAMS_REQUEST: - sd_ble_gap_sec_params_reply(self->conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL); - break; - case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: { ble_gap_evt_conn_param_update_request_t *request = &ble_evt->evt.gap_evt.params.conn_param_update_request; @@ -113,6 +125,47 @@ STATIC void peripheral_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0); break; + case BLE_GAP_EVT_SEC_PARAMS_REQUEST: + sd_ble_gap_sec_params_reply(self->conn_handle, BLE_GAP_SEC_STATUS_SUCCESS, &pairing_sec_params, NULL); + break; + + case BLE_GAP_EVT_LESC_DHKEY_REQUEST: + // TODO for LESC pairing: + // sd_ble_gap_lesc_dhkey_reply(...); + break; + + case BLE_GAP_EVT_AUTH_STATUS: { + // Pairing process completed + ble_gap_evt_auth_status_t* status = &ble_evt->evt.gap_evt.params.auth_status; + if (BLE_GAP_SEC_STATUS_SUCCESS == status->auth_status) { + // mp_printf(&mp_plat_print, "Pairing succeeded, status: 0x%04x\n", status->auth_status); + self->pair_status = PAIR_PAIRED; + } else { + // mp_printf(&mp_plat_print, "Pairing failed, status: 0x%04x\n", status->auth_status); + self->pair_status = PAIR_NOT_PAIRED; + } + break; + } + + case BLE_GAP_EVT_CONN_SEC_UPDATE: { + ble_gap_conn_sec_t* conn_sec = &ble_evt->evt.gap_evt.params.conn_sec_update.conn_sec; + if (conn_sec->sec_mode.sm <= 1 && conn_sec->sec_mode.lv <= 1) { + // Security setup did not succeed: + // mode 0, level 0 means no access + // mode 1, level 1 means open link + // mode >=1 and/or level >=1 means encryption is set up + self->pair_status = PAIR_NOT_PAIRED; + } else { + // TODO: see Bluefruit lib + // if ( !bond_load_cccd(_role, _conn_hdl, _ediv) ) { + // sd_ble_gatts_sys_attr_set(_conn_hdl, NULL, 0, 0); + // } + self->pair_status = PAIR_PAIRED; + } + break; + } + + default: // For debugging. // mp_printf(&mp_plat_print, "Unhandled peripheral event: 0x%04x\n", ble_evt->header.evt_id); @@ -120,20 +173,22 @@ STATIC void peripheral_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { } } -void common_hal_bleio_peripheral_construct(bleio_peripheral_obj_t *self, mp_obj_list_t *service_list, mp_obj_t name) { +void common_hal_bleio_peripheral_construct(bleio_peripheral_obj_t *self, mp_obj_list_t *services_list, mp_obj_t name) { common_hal_bleio_adapter_set_enabled(true); - self->service_list = service_list; + self->services_list = services_list; + // Used only for discovery when acting as a client. + self->remote_services_list = mp_obj_new_list(0, NULL); self->name = name; - self->gatt_role = GATT_ROLE_SERVER; self->conn_handle = BLE_CONN_HANDLE_INVALID; self->adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET; + self->pair_status = PAIR_NOT_PAIRED; // Add all the services. - for (size_t service_idx = 0; service_idx < service_list->len; ++service_idx) { - bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_list->items[service_idx]); + for (size_t service_idx = 0; service_idx < services_list->len; ++service_idx) { + bleio_service_obj_t *service = MP_OBJ_TO_PTR(services_list->items[service_idx]); service->device = MP_OBJ_FROM_PTR(self); @@ -156,8 +211,8 @@ void common_hal_bleio_peripheral_construct(bleio_peripheral_obj_t *self, mp_obj_ } -mp_obj_list_t *common_hal_bleio_peripheral_get_service_list(bleio_peripheral_obj_t *self) { - return self->service_list; +mp_obj_list_t *common_hal_bleio_peripheral_get_services(bleio_peripheral_obj_t *self) { + return self->services_list; } bool common_hal_bleio_peripheral_get_connected(bleio_peripheral_obj_t *self) { @@ -247,3 +302,30 @@ void common_hal_bleio_peripheral_stop_advertising(bleio_peripheral_obj_t *self) void common_hal_bleio_peripheral_disconnect(bleio_peripheral_obj_t *self) { sd_ble_gap_disconnect(self->conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); } + +mp_obj_tuple_t *common_hal_bleio_peripheral_discover_remote_services(bleio_peripheral_obj_t *self, mp_obj_t service_uuids_whitelist) { + common_hal_bleio_device_discover_remote_services(MP_OBJ_FROM_PTR(self), service_uuids_whitelist); + // Convert to a tuple and then clear the list so the callee will take ownership. + mp_obj_tuple_t *services_tuple = mp_obj_new_tuple(self->remote_services_list->len, + self->remote_services_list->items); + mp_obj_list_clear(self->remote_services_list); + return services_tuple; +} + +void common_hal_bleio_peripheral_pair(bleio_peripheral_obj_t *self) { + self->pair_status = PAIR_WAITING; + + uint32_t err_code = sd_ble_gap_authenticate(self->conn_handle, &pairing_sec_params); + + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to start pairing, error 0x%04x"), err_code); + } + + while (self->pair_status == PAIR_WAITING) { + MICROPY_VM_HOOK_LOOP; + } + + if (self->pair_status == PAIR_NOT_PAIRED) { + mp_raise_OSError_msg(translate("Failed to pair")); + } +} diff --git a/ports/nrf/common-hal/bleio/Peripheral.h b/ports/nrf/common-hal/bleio/Peripheral.h index 2a1251715..e75a1641a 100644 --- a/ports/nrf/common-hal/bleio/Peripheral.h +++ b/ports/nrf/common-hal/bleio/Peripheral.h @@ -35,22 +35,29 @@ #include "py/obj.h" #include "py/objlist.h" -#include "shared-module/bleio/__init__.h" #include "shared-module/bleio/Address.h" +typedef enum { + PAIR_NOT_PAIRED, + PAIR_WAITING, + PAIR_PAIRED, +} pair_status_t; + typedef struct { mp_obj_base_t base; mp_obj_t name; - gatt_role_t gatt_role; volatile uint16_t conn_handle; - mp_obj_list_t *service_list; + // Services provided by this peripheral. + mp_obj_list_t *services_list; + // Remote services discovered when this peripheral is acting as a client. + mp_obj_list_t *remote_services_list; // The advertising data and scan response buffers are held by us, not by the SD, so we must // maintain them and not change it. If we need to change the contents during advertising, // there are tricks to get the SD to notice (see DevZone - TBS). uint8_t* advertising_data; uint8_t* scan_response_data; uint8_t adv_handle; - + pair_status_t pair_status; } bleio_peripheral_obj_t; #endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_PERIPHERAL_H diff --git a/ports/nrf/common-hal/bleio/Service.c b/ports/nrf/common-hal/bleio/Service.c index b6fcde827..a7a3a6db0 100644 --- a/ports/nrf/common-hal/bleio/Service.c +++ b/ports/nrf/common-hal/bleio/Service.c @@ -29,8 +29,8 @@ #include "ble.h" #include "py/runtime.h" #include "common-hal/bleio/__init__.h" -#include "common-hal/bleio/Characteristic.h" #include "shared-bindings/bleio/Characteristic.h" +#include "shared-bindings/bleio/Descriptor.h" #include "shared-bindings/bleio/Service.h" #include "shared-bindings/bleio/Adapter.h" @@ -39,6 +39,7 @@ void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_ob self->handle = 0xFFFF; self->uuid = uuid; self->characteristic_list = characteristic_list; + self->is_remote = false; self->is_secondary = is_secondary; for (size_t characteristic_idx = 0; characteristic_idx < characteristic_list->len; ++characteristic_idx) { @@ -46,7 +47,6 @@ void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_ob MP_OBJ_TO_PTR(characteristic_list->items[characteristic_idx]); characteristic->service = self; } - } bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t *self) { @@ -57,6 +57,10 @@ mp_obj_list_t *common_hal_bleio_service_get_characteristic_list(bleio_service_ob return self->characteristic_list; } +bool common_hal_bleio_service_get_is_remote(bleio_service_obj_t *self) { + return self->is_remote; +} + bool common_hal_bleio_service_get_is_secondary(bleio_service_obj_t *self) { return self->is_secondary; } @@ -68,13 +72,17 @@ void common_hal_bleio_service_add_all_characteristics(bleio_service_obj_t *self) bleio_characteristic_obj_t *characteristic = MP_OBJ_TO_PTR(self->characteristic_list->items[characteristic_idx]); + if (characteristic->handle != BLE_GATT_HANDLE_INVALID) { + mp_raise_ValueError(translate("Characteristic already in use by another Service.")); + } + ble_gatts_char_md_t char_md = { - .char_props.broadcast = characteristic->props.broadcast, - .char_props.read = characteristic->props.read, - .char_props.write_wo_resp = characteristic->props.write_no_response, - .char_props.write = characteristic->props.write, - .char_props.notify = characteristic->props.notify, - .char_props.indicate = characteristic->props.indicate, + .char_props.broadcast = (characteristic->props & CHAR_PROP_BROADCAST) ? 1 : 0, + .char_props.read = (characteristic->props & CHAR_PROP_READ) ? 1 : 0, + .char_props.write_wo_resp = (characteristic->props & CHAR_PROP_WRITE_NO_RESPONSE) ? 1 : 0, + .char_props.write = (characteristic->props & CHAR_PROP_WRITE) ? 1 : 0, + .char_props.notify = (characteristic->props & CHAR_PROP_NOTIFY) ? 1 : 0, + .char_props.indicate = (characteristic->props & CHAR_PROP_INDICATE) ? 1 : 0, }; ble_gatts_attr_md_t cccd_md = { @@ -88,39 +96,74 @@ void common_hal_bleio_service_add_all_characteristics(bleio_service_obj_t *self) char_md.p_cccd_md = &cccd_md; } - ble_uuid_t uuid; - bleio_uuid_convert_to_nrf_ble_uuid(characteristic->uuid, &uuid); + ble_uuid_t char_uuid; + bleio_uuid_convert_to_nrf_ble_uuid(characteristic->uuid, &char_uuid); - ble_gatts_attr_md_t attr_md = { + ble_gatts_attr_md_t char_attr_md = { .vloc = BLE_GATTS_VLOC_STACK, - .vlen = 1, + .vlen = !characteristic->fixed_length, }; - BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm); - BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm); + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&char_attr_md.read_perm); + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&char_attr_md.write_perm); + + mp_buffer_info_t char_value_bufinfo; + mp_get_buffer_raise(characteristic->value, &char_value_bufinfo, MP_BUFFER_READ); - ble_gatts_attr_t attr_char_value = { - .p_uuid = &uuid, - .p_attr_md = &attr_md, - .init_len = sizeof(uint8_t), - .max_len = GATT_MAX_DATA_LENGTH, + ble_gatts_attr_t char_attr = { + .p_uuid = &char_uuid, + .p_attr_md = &char_attr_md, + .init_len = char_value_bufinfo.len, + .p_value = char_value_bufinfo.buf, + .init_offs = 0, + .max_len = characteristic->max_length, }; - ble_gatts_char_handles_t handles; + ble_gatts_char_handles_t char_handles; uint32_t err_code; - err_code = sd_ble_gatts_characteristic_add(self->handle, &char_md, &attr_char_value, &handles); + err_code = sd_ble_gatts_characteristic_add(self->handle, &char_md, &char_attr, &char_handles); if (err_code != NRF_SUCCESS) { mp_raise_OSError_msg_varg(translate("Failed to add characteristic, err 0x%04x"), err_code); } - if (characteristic->handle != BLE_GATT_HANDLE_INVALID) { - mp_raise_ValueError(translate("Characteristic already in use by another Service.")); - } + characteristic->user_desc_handle = char_handles.user_desc_handle; + characteristic->cccd_handle = char_handles.cccd_handle; + characteristic->sccd_handle = char_handles.sccd_handle; + characteristic->handle = char_handles.value_handle; - characteristic->user_desc_handle = handles.user_desc_handle; - characteristic->cccd_handle = handles.cccd_handle; - characteristic->sccd_handle = handles.sccd_handle; - characteristic->handle = handles.value_handle; - } + // Add the descriptors for this characteristic. + for (size_t descriptor_idx = 0; descriptor_idx < characteristic->descriptor_list->len; ++descriptor_idx) { + bleio_descriptor_obj_t *descriptor = + MP_OBJ_TO_PTR(characteristic->descriptor_list->items[descriptor_idx]); + + ble_uuid_t desc_uuid; + bleio_uuid_convert_to_nrf_ble_uuid(descriptor->uuid, &desc_uuid); + + ble_gatts_attr_md_t desc_attr_md = { + // Data passed is not in a permanent location and should be copied. + .vloc = BLE_GATTS_VLOC_STACK, + .vlen = !descriptor->fixed_length, + }; + + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&desc_attr_md.read_perm); + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&desc_attr_md.write_perm); + + mp_buffer_info_t desc_value_bufinfo; + mp_get_buffer_raise(descriptor->value, &desc_value_bufinfo, MP_BUFFER_READ); + + ble_gatts_attr_t desc_attr = { + .p_uuid = &desc_uuid, + .p_attr_md = &desc_attr_md, + .init_len = desc_value_bufinfo.len, + .p_value = desc_value_bufinfo.buf, + .init_offs = 0, + .max_len = descriptor->max_length, + }; + + err_code = sd_ble_gatts_descriptor_add(characteristic->handle, &desc_attr, &descriptor->handle); + + } // loop over descriptors + + } // loop over characteristics } diff --git a/ports/nrf/common-hal/bleio/Service.h b/ports/nrf/common-hal/bleio/Service.h index 583593fb8..03ac2bca8 100644 --- a/ports/nrf/common-hal/bleio/Service.h +++ b/ports/nrf/common-hal/bleio/Service.h @@ -35,6 +35,8 @@ typedef struct { mp_obj_base_t base; // Handle for this service. uint16_t handle; + // True if created during discovery. + bool is_remote; bool is_secondary; bleio_uuid_obj_t *uuid; // May be a Peripheral, Central, etc. diff --git a/ports/nrf/common-hal/bleio/__init__.c b/ports/nrf/common-hal/bleio/__init__.c index 2dba5780c..2e26ac07d 100644 --- a/ports/nrf/common-hal/bleio/__init__.c +++ b/ports/nrf/common-hal/bleio/__init__.c @@ -26,12 +26,24 @@ * THE SOFTWARE. */ +#include "py/runtime.h" #include "shared-bindings/bleio/__init__.h" #include "shared-bindings/bleio/Adapter.h" #include "shared-bindings/bleio/Central.h" +#include "shared-bindings/bleio/Characteristic.h" +#include "shared-bindings/bleio/Descriptor.h" #include "shared-bindings/bleio/Peripheral.h" +#include "shared-bindings/bleio/Service.h" +#include "shared-bindings/bleio/UUID.h" + #include "common-hal/bleio/__init__.h" +static volatile bool m_discovery_in_process; +static volatile bool m_discovery_successful; + +static bleio_service_obj_t *m_char_discovery_service; +static bleio_characteristic_obj_t *m_desc_discovery_characteristic; + // Turn off BLE on a reset or reload. void bleio_reset() { if (common_hal_bleio_adapter_get_enabled()) { @@ -47,13 +59,9 @@ const super_adapter_obj_t common_hal_bleio_adapter_obj = { }, }; -gatt_role_t common_hal_bleio_device_get_gatt_role(mp_obj_t device) { - if (MP_OBJ_IS_TYPE(device, &bleio_peripheral_type)) { - return ((bleio_peripheral_obj_t*) MP_OBJ_TO_PTR(device))->gatt_role; - } else if (MP_OBJ_IS_TYPE(device, &bleio_central_type)) { - return ((bleio_central_obj_t*) MP_OBJ_TO_PTR(device))->gatt_role; - } else { - return GATT_ROLE_NONE; +void common_hal_bleio_check_connected(uint16_t conn_handle) { + if (conn_handle == BLE_CONN_HANDLE_INVALID) { + mp_raise_OSError_msg(translate("Not connected")); } } @@ -63,6 +71,433 @@ uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device) { } else if (MP_OBJ_IS_TYPE(device, &bleio_central_type)) { return ((bleio_central_obj_t*) MP_OBJ_TO_PTR(device))->conn_handle; } else { - return 0; + return BLE_CONN_HANDLE_INVALID; + } +} + +mp_obj_list_t *common_hal_bleio_device_get_remote_services_list(mp_obj_t device) { + if (MP_OBJ_IS_TYPE(device, &bleio_peripheral_type)) { + return ((bleio_peripheral_obj_t*) MP_OBJ_TO_PTR(device))->remote_services_list; + } else if (MP_OBJ_IS_TYPE(device, &bleio_central_type)) { + return ((bleio_central_obj_t*) MP_OBJ_TO_PTR(device))->remote_services_list; + } else { + return NULL; + } +} + +// service_uuid may be NULL, to discover all services. +STATIC bool discover_next_services(mp_obj_t device, uint16_t start_handle, ble_uuid_t *service_uuid) { + m_discovery_successful = false; + m_discovery_in_process = true; + + uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(device); + uint32_t err_code = sd_ble_gattc_primary_services_discover(conn_handle, start_handle, service_uuid); + + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg(translate("Failed to discover services")); + } + + // Wait for a discovery event. + while (m_discovery_in_process) { + MICROPY_VM_HOOK_LOOP; + } + return m_discovery_successful; +} + +STATIC bool discover_next_characteristics(mp_obj_t device, bleio_service_obj_t *service, uint16_t start_handle) { + m_char_discovery_service = service; + + ble_gattc_handle_range_t handle_range; + handle_range.start_handle = start_handle; + handle_range.end_handle = service->end_handle; + + m_discovery_successful = false; + m_discovery_in_process = true; + + uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(device); + uint32_t err_code = sd_ble_gattc_characteristics_discover(conn_handle, &handle_range); + if (err_code != NRF_SUCCESS) { + return false; + } + + // Wait for a discovery event. + while (m_discovery_in_process) { + MICROPY_VM_HOOK_LOOP; + } + return m_discovery_successful; +} + +STATIC bool discover_next_descriptors(mp_obj_t device, bleio_characteristic_obj_t *characteristic, uint16_t start_handle, uint16_t end_handle) { + m_desc_discovery_characteristic = characteristic; + + ble_gattc_handle_range_t handle_range; + handle_range.start_handle = start_handle; + handle_range.end_handle = end_handle; + + m_discovery_successful = false; + m_discovery_in_process = true; + + uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(device); + uint32_t err_code = sd_ble_gattc_descriptors_discover(conn_handle, &handle_range); + if (err_code != NRF_SUCCESS) { + return false; } + + // Wait for a discovery event. + while (m_discovery_in_process) { + MICROPY_VM_HOOK_LOOP; + } + return m_discovery_successful; +} + +STATIC void on_primary_srv_discovery_rsp(ble_gattc_evt_prim_srvc_disc_rsp_t *response, mp_obj_t device) { + for (size_t i = 0; i < response->count; ++i) { + ble_gattc_service_t *gattc_service = &response->services[i]; + + bleio_service_obj_t *service = m_new_obj(bleio_service_obj_t); + service->base.type = &bleio_service_type; + + // Initialize several fields at once. + common_hal_bleio_service_construct(service, NULL, mp_obj_new_list(0, NULL), false); + + service->device = device; + service->is_remote = true; + service->start_handle = gattc_service->handle_range.start_handle; + service->end_handle = gattc_service->handle_range.end_handle; + service->handle = gattc_service->handle_range.start_handle; + + if (gattc_service->uuid.type != BLE_UUID_TYPE_UNKNOWN) { + // Known service UUID. + bleio_uuid_obj_t *uuid = m_new_obj(bleio_uuid_obj_t); + uuid->base.type = &bleio_uuid_type; + bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_service->uuid); + service->uuid = uuid; + } else { + // The discovery response contained a 128-bit UUID that has not yet been registered with the + // softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it. + // For now, just set the UUID to NULL. + service->uuid = NULL; + } + + mp_obj_list_append(common_hal_bleio_device_get_remote_services_list(device), service); + } + + if (response->count > 0) { + m_discovery_successful = true; + } + m_discovery_in_process = false; +} + +STATIC void on_char_discovery_rsp(ble_gattc_evt_char_disc_rsp_t *response, mp_obj_t device) { + for (size_t i = 0; i < response->count; ++i) { + ble_gattc_char_t *gattc_char = &response->chars[i]; + + bleio_characteristic_obj_t *characteristic = m_new_obj(bleio_characteristic_obj_t); + characteristic->base.type = &bleio_characteristic_type; + + bleio_uuid_obj_t *uuid = NULL; + + if (gattc_char->uuid.type != BLE_UUID_TYPE_UNKNOWN) { + // Known characteristic UUID. + uuid = m_new_obj(bleio_uuid_obj_t); + uuid->base.type = &bleio_uuid_type; + bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_char->uuid); + } else { + // The discovery response contained a 128-bit UUID that has not yet been registered with the + // softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it. + // For now, just leave the UUID as NULL. + } + + bleio_characteristic_properties_t props = + (gattc_char->char_props.broadcast ? CHAR_PROP_BROADCAST : 0) | + (gattc_char->char_props.indicate ? CHAR_PROP_INDICATE : 0) | + (gattc_char->char_props.notify ? CHAR_PROP_NOTIFY : 0) | + (gattc_char->char_props.read ? CHAR_PROP_READ : 0) | + (gattc_char->char_props.write ? CHAR_PROP_WRITE : 0) | + (gattc_char->char_props.write_wo_resp ? CHAR_PROP_WRITE_NO_RESPONSE : 0); + + // Call common_hal_bleio_characteristic_construct() to initalize some fields and set up evt handler. + common_hal_bleio_characteristic_construct( + characteristic, uuid, props, SECURITY_MODE_OPEN, SECURITY_MODE_OPEN, + GATT_MAX_DATA_LENGTH, false, // max_length, fixed_length: values may not matter for gattc + mp_obj_new_list(0, NULL)); + characteristic->handle = gattc_char->handle_value; + characteristic->service = m_char_discovery_service; + + mp_obj_list_append(m_char_discovery_service->characteristic_list, MP_OBJ_FROM_PTR(characteristic)); + } + + if (response->count > 0) { + m_discovery_successful = true; + } + m_discovery_in_process = false; +} + +STATIC void on_desc_discovery_rsp(ble_gattc_evt_desc_disc_rsp_t *response, mp_obj_t device) { + for (size_t i = 0; i < response->count; ++i) { + ble_gattc_desc_t *gattc_desc = &response->descs[i]; + + // Remember handles for certain well-known descriptors. + switch (gattc_desc->uuid.uuid) { + case BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG: + m_desc_discovery_characteristic->cccd_handle = gattc_desc->handle; + break; + + case BLE_UUID_DESCRIPTOR_SERVER_CHAR_CONFIG: + m_desc_discovery_characteristic->sccd_handle = gattc_desc->handle; + break; + + case BLE_UUID_DESCRIPTOR_CHAR_USER_DESC: + m_desc_discovery_characteristic->user_desc_handle = gattc_desc->handle; + break; + + default: + // TODO: sd_ble_gattc_descriptors_discover() can return things that are not descriptors, + // so ignore those. + // https://devzone.nordicsemi.com/f/nordic-q-a/49500/sd_ble_gattc_descriptors_discover-is-returning-attributes-that-are-not-descriptors + break; + } + + bleio_descriptor_obj_t *descriptor = m_new_obj(bleio_descriptor_obj_t); + descriptor->base.type = &bleio_descriptor_type; + + bleio_uuid_obj_t *uuid = NULL; + + if (gattc_desc->uuid.type != BLE_UUID_TYPE_UNKNOWN) { + // Known descriptor UUID. + uuid = m_new_obj(bleio_uuid_obj_t); + uuid->base.type = &bleio_uuid_type; + bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_desc->uuid); + } else { + // The discovery response contained a 128-bit UUID that has not yet been registered with the + // softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it. + // For now, just leave the UUID as NULL. + } + + common_hal_bleio_descriptor_construct(descriptor, uuid, SECURITY_MODE_OPEN, SECURITY_MODE_OPEN, + GATT_MAX_DATA_LENGTH, false); + descriptor->handle = gattc_desc->handle; + descriptor->characteristic = m_desc_discovery_characteristic; + + mp_obj_list_append(m_desc_discovery_characteristic->descriptor_list, MP_OBJ_FROM_PTR(descriptor)); + } + + if (response->count > 0) { + m_discovery_successful = true; + } + m_discovery_in_process = false; +} + +STATIC void discovery_on_ble_evt(ble_evt_t *ble_evt, mp_obj_t device) { + switch (ble_evt->header.evt_id) { + case BLE_GAP_EVT_DISCONNECTED: + m_discovery_successful = false; + m_discovery_in_process = false; + break; + + case BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP: + on_primary_srv_discovery_rsp(&ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp, device); + break; + + case BLE_GATTC_EVT_CHAR_DISC_RSP: + on_char_discovery_rsp(&ble_evt->evt.gattc_evt.params.char_disc_rsp, device); + break; + + case BLE_GATTC_EVT_DESC_DISC_RSP: + on_desc_discovery_rsp(&ble_evt->evt.gattc_evt.params.desc_disc_rsp, device); + break; + + default: + // For debugging. + // mp_printf(&mp_plat_print, "Unhandled discovery event: 0x%04x\n", ble_evt->header.evt_id); + break; + } +} + + +void common_hal_bleio_device_discover_remote_services(mp_obj_t device, mp_obj_t service_uuids_whitelist) { + mp_obj_list_t *remote_services_list = common_hal_bleio_device_get_remote_services_list(device); + + ble_drv_add_event_handler(discovery_on_ble_evt, device); + + // Start over with an empty list. + mp_obj_list_clear(MP_OBJ_FROM_PTR(common_hal_bleio_device_get_remote_services_list(device))); + + if (service_uuids_whitelist == mp_const_none) { + // List of service UUID's not given, so discover all available services. + + uint16_t next_service_start_handle = BLE_GATT_HANDLE_START; + + while (discover_next_services(device, next_service_start_handle, MP_OBJ_NULL)) { + // discover_next_services() appends to remote_services_list. + + // Get the most recently discovered service, and then ask for services + // whose handles start after the last attribute handle inside that service. + const bleio_service_obj_t *service = + MP_OBJ_TO_PTR(remote_services_list->items[remote_services_list->len - 1]); + next_service_start_handle = service->end_handle + 1; + } + } else { + mp_obj_iter_buf_t iter_buf; + mp_obj_t iterable = mp_getiter(service_uuids_whitelist, &iter_buf); + mp_obj_t uuid_obj; + while ((uuid_obj = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + if (!MP_OBJ_IS_TYPE(uuid_obj, &bleio_uuid_type)) { + mp_raise_ValueError(translate("non-UUID found in service_uuids_whitelist")); + } + bleio_uuid_obj_t *uuid = MP_OBJ_TO_PTR(uuid_obj); + + ble_uuid_t nrf_uuid; + bleio_uuid_convert_to_nrf_ble_uuid(uuid, &nrf_uuid); + + // Service might or might not be discovered; that's ok. Caller has to check + // Central.remote_services to find out. + // We only need to call this once for each service to discover. + discover_next_services(device, BLE_GATT_HANDLE_START, &nrf_uuid); + } + } + + + for (size_t service_idx = 0; service_idx < remote_services_list->len; ++service_idx) { + bleio_service_obj_t *service = MP_OBJ_TO_PTR(remote_services_list->items[service_idx]); + + // Skip the service if it had an unknown (unregistered) UUID. + if (service->uuid == NULL) { + continue; + } + + uint16_t next_char_start_handle = service->start_handle; + + // Stop when we go past the end of the range of handles for this service or + // discovery call returns nothing. + // discover_next_characteristics() appends to the characteristic_list. + while (next_char_start_handle <= service->end_handle && + discover_next_characteristics(device, service, next_char_start_handle)) { + + + // Get the most recently discovered characteristic, and then ask for characteristics + // whose handles start after the last attribute handle inside that characteristic. + const bleio_characteristic_obj_t *characteristic = + MP_OBJ_TO_PTR(service->characteristic_list->items[service->characteristic_list->len - 1]); + next_char_start_handle = characteristic->handle + 1; + } + + // Got characteristics for this service. Now discover descriptors for each characteristic. + size_t char_list_len = service->characteristic_list->len; + for (size_t char_idx = 0; char_idx < char_list_len; ++char_idx) { + bleio_characteristic_obj_t *characteristic = + MP_OBJ_TO_PTR(service->characteristic_list->items[char_idx]); + const bool last_characteristic = char_idx == char_list_len - 1; + bleio_characteristic_obj_t *next_characteristic = last_characteristic + ? NULL + : MP_OBJ_TO_PTR(service->characteristic_list->items[char_idx + 1]); + + // Skip the characteristic if it had an unknown (unregistered) UUID. + if (characteristic->uuid == NULL) { + continue; + } + + uint16_t next_desc_start_handle = characteristic->handle + 1; + + // Don't run past the end of this service or the beginning of the next characteristic. + uint16_t next_desc_end_handle = next_characteristic == NULL + ? service->end_handle + : next_characteristic->handle - 1; + + // Stop when we go past the end of the range of handles for this service or + // discovery call returns nothing. + // discover_next_descriptors() appends to the descriptor_list. + while (next_desc_start_handle <= service->end_handle && + next_desc_start_handle < next_desc_end_handle && + discover_next_descriptors(device, characteristic, + next_desc_start_handle, next_desc_end_handle)) { + + // Get the most recently discovered descriptor, and then ask for descriptors + // whose handles start after that descriptor's handle. + const bleio_descriptor_obj_t *descriptor = + MP_OBJ_TO_PTR(characteristic->descriptor_list->items[characteristic->descriptor_list->len - 1]); + next_desc_start_handle = descriptor->handle + 1; + } + } + } + + // This event handler is no longer needed. + ble_drv_remove_event_handler(discovery_on_ble_evt, device); + +} + +// GATTS read of a Characteristic or Descriptor. +mp_obj_t common_hal_bleio_gatts_read(uint16_t handle, uint16_t conn_handle) { + // conn_handle might be BLE_CONN_HANDLE_INVALID if we're not connected, but that's OK, because + // we can still read and write the local value. + + mp_buffer_info_t bufinfo; + ble_gatts_value_t gatts_value = { + .p_value = NULL, + .len = 0, + }; + + // Read once to find out what size buffer we need, then read again to fill buffer. + + mp_obj_t value = mp_const_none; + uint32_t err_code = sd_ble_gatts_value_get(conn_handle, handle, &gatts_value); + if (err_code == NRF_SUCCESS) { + value = mp_obj_new_bytearray_of_zeros(gatts_value.len); + mp_get_buffer_raise(value, &bufinfo, MP_BUFFER_WRITE); + gatts_value.p_value = bufinfo.buf; + + // Read again, with the correct size of buffer. + err_code = sd_ble_gatts_value_get(conn_handle, handle, &gatts_value); + } + + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to read gatts value, err 0x%04x"), err_code); + } + + return value; +} + +void common_hal_bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo) { + // conn_handle might be BLE_CONN_HANDLE_INVALID if we're not connected, but that's OK, because + // we can still read and write the local value. + + ble_gatts_value_t gatts_value = { + .p_value = bufinfo->buf, + .len = bufinfo->len, + }; + + const uint32_t err_code = sd_ble_gatts_value_set(conn_handle, handle, &gatts_value); + if (err_code != NRF_SUCCESS) { + mp_raise_OSError_msg_varg(translate("Failed to write gatts value, err 0x%04x"), err_code); + } +} + +void common_hal_bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response) { + common_hal_bleio_check_connected(conn_handle); + + ble_gattc_write_params_t write_params = { + .write_op = write_no_response ? BLE_GATT_OP_WRITE_CMD: BLE_GATT_OP_WRITE_REQ, + .handle = handle, + .p_value = bufinfo->buf, + .len = bufinfo->len, + }; + + while (1) { + uint32_t err_code = sd_ble_gattc_write(conn_handle, &write_params); + if (err_code == NRF_SUCCESS) { + break; + } + + // Write with response will return NRF_ERROR_BUSY if the response has not been received. + // Write without reponse will return NRF_ERROR_RESOURCES if too many writes are pending. + if (err_code == NRF_ERROR_BUSY || err_code == NRF_ERROR_RESOURCES) { + // We could wait for an event indicating the write is complete, but just retrying is easier. + MICROPY_VM_HOOK_LOOP; + continue; + } + + // Some real error occurred. + mp_raise_OSError_msg_varg(translate("Failed to write attribute value, err 0x%04x"), err_code); + } + } diff --git a/ports/nrf/common-hal/bleio/__init__.h b/ports/nrf/common-hal/bleio/__init__.h index 0c46b631f..c0bba3799 100644 --- a/ports/nrf/common-hal/bleio/__init__.h +++ b/ports/nrf/common-hal/bleio/__init__.h @@ -27,16 +27,10 @@ #ifndef MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_INIT_H #define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_INIT_H -#include "shared-bindings/bleio/__init__.h" -#include "shared-bindings/bleio/Adapter.h" - -#include "shared-module/bleio/__init__.h" +void bleio_reset(void); // We assume variable length data. // 20 bytes max (23 - 3). #define GATT_MAX_DATA_LENGTH (BLE_GATT_ATT_MTU_DEFAULT - 3) -gatt_role_t common_hal_bleio_device_get_gatt_role(mp_obj_t device); -uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device); - #endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_INIT_H diff --git a/ports/nrf/device/nrf52/startup_nrf52840.c b/ports/nrf/device/nrf52/startup_nrf52840.c index 30634a1b5..8e1c36012 100644 --- a/ports/nrf/device/nrf52/startup_nrf52840.c +++ b/ports/nrf/device/nrf52/startup_nrf52840.c @@ -116,7 +116,7 @@ void CRYPTOCELL_IRQHandler (void) __attribute__ ((weak, alias("Default_Han void SPIM3_IRQHandler (void) __attribute__ ((weak, alias("Default_Handler"))); void PWM3_IRQHandler (void) __attribute__ ((weak, alias("Default_Handler"))); -const func __Vectors[] __attribute__ ((section(".isr_vector"))) = { +const func __Vectors[] __attribute__ ((used, section(".isr_vector"))) = { (func)&_estack, Reset_Handler, NMI_Handler, diff --git a/ports/nrf/mpconfigport.mk b/ports/nrf/mpconfigport.mk index 57879bbaa..55920b3f4 100644 --- a/ports/nrf/mpconfigport.mk +++ b/ports/nrf/mpconfigport.mk @@ -14,9 +14,7 @@ LONGINT_IMPL = MPZ CIRCUITPY_AUDIOCORE = 1 CIRCUITPY_AUDIOIO = 0 CIRCUITPY_AUDIOPWMIO = 1 - -# No I2S yet. -CIRCUITPY_AUDIOBUSIO = 0 +CIRCUITPY_AUDIOBUSIO = 1 # No I2CSlave implementation CIRCUITPY_I2CSLAVE = 0 diff --git a/ports/nrf/nrfx_config.h b/ports/nrf/nrfx_config.h index a2b48065d..8fa6721e2 100644 --- a/ports/nrf/nrfx_config.h +++ b/ports/nrf/nrfx_config.h @@ -78,6 +78,7 @@ // TIMERS #define NRFX_TIMER_ENABLED 1 // Don't enable TIMER0: it's used by the SoftDevice. +#define NRFX_TIMER0_ENABLED 0 #define NRFX_TIMER1_ENABLED 1 #define NRFX_TIMER2_ENABLED 1 diff --git a/ports/nrf/peripherals/nrf/timers.c b/ports/nrf/peripherals/nrf/timers.c index 7f7a003d3..88f3dd468 100644 --- a/ports/nrf/peripherals/nrf/timers.c +++ b/ports/nrf/peripherals/nrf/timers.c @@ -36,7 +36,7 @@ STATIC nrfx_timer_t nrfx_timers[] = { #if NRFX_CHECK(NRFX_TIMER0_ENABLED) - // Note that TIMER0 is reserved for use by the SoftDevice, so it should not usually be enabled. + #error NRFX_TIMER0_ENABLED should not be on: TIMER0 is used by the SoftDevice NRFX_TIMER_INSTANCE(0), #endif #if NRFX_CHECK(NRFX_TIMER1_ENABLED) |
