Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .codespell/ignore-words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ ftbs
ftb
curren
mabey
rsource
11 changes: 8 additions & 3 deletions ports/zephyr-cp/.gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
# West manages these folders.
# West manages these folders. modules/* are west clones, except iobroker,
# which is an in-tree Zephyr module (see modules/iobroker/README.md); its
# datasheets/ PDFs are licensed by Nordic and not redistributed here.
bootloader
build
modules
tools
modules/*
!modules/iobroker
modules/iobroker/datasheets/*
!modules/iobroker/datasheets/README.md
/tools
zephyr
.west
6 changes: 6 additions & 0 deletions ports/zephyr-cp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
cmake_minimum_required(VERSION 3.20.0)

# The dynamic peripheral allocation / runtime pin routing code lives in a
# Zephyr module kept in-tree for now (modules/iobroker, see its README.md).
# It must be set before find_package(Zephyr). Repoint this at the module's new
# home to consume an externalized copy.
set(ZEPHYR_EXTRA_MODULES ${CMAKE_CURRENT_SOURCE_DIR}/modules/iobroker)

find_package(Zephyr REQUIRED HINTS lib/zephyr)
project(circuitpython)

Expand Down
11 changes: 11 additions & 0 deletions ports/zephyr-cp/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ config UART_LINE_CTRL
config ENTROPY_GENERATOR
default y

# ===== Dynamic pin routing (nRF) =====
# CircuitPython routes peripherals to pins at runtime on nRF SoCs. That needs
# pinctrl states to be swappable (PINCTRL_DYNAMIC) and devices to be
# de-initializable/re-initializable (DEVICE_DEINIT_SUPPORT). Both cost a little
# RAM (pinctrl configs move out of flash) and one function pointer per device.
config PINCTRL_DYNAMIC
default y if SOC_FAMILY_NORDIC_NRF

config DEVICE_DEINIT_SUPPORT
default y if SOC_FAMILY_NORDIC_NRF

# ===== Bluetooth defaults =====

# Use a variable for the chosen name so the comma isn't parsed as an argument separator
Expand Down
2 changes: 1 addition & 1 deletion ports/zephyr-cp/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ CP_BOARD_CONF := $(DEBUG_CONF_FILE)
endif
endif
ifneq ($(CP_BOARD_CONF),)
WEST_CMAKE_ARGS += -Dzephyr-cp_EXTRA_CONF_FILE=$(CP_BOARD_CONF)
WEST_CMAKE_ARGS += -Dzephyr-cp_EXTRA_CONF_FILE="$(CP_BOARD_CONF)"
endif

.PHONY: $(BUILD)/zephyr-cp/zephyr/zephyr.elf flash recover debug debug-jlink debugserver attach run run-sim clean menuconfig all clean-all sim clean-sim test fetch-port-submodules
Expand Down
43 changes: 43 additions & 0 deletions ports/zephyr-cp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,49 @@ Behavior and precedence:
- If neither is provided, defaults from `circuitpython.toml` are used.
- Use `SHIELD=` (empty) to disable a board default shield for one build.

## Pin names

Human readable pin names (the `board` module) come from the devicetree by
default: `gpio-leds` and `gpio-keys` labels, node aliases, and connector
`gpio-map`s. Boards can add names without any devicetree involvement by
listing them in `boards/<vendor>/<board>/circuitpython.toml` under `[pins]`.
Each entry maps a board module name to the pin number exposed by the board's
hardware: the SoC package pin (or, for ball grid array packages, the
datasheet's ball id, e.g. `"B2"`), or the castellated module pin when the
board uses a module like the Raytac MDBT50Q:

```toml
[pins]
LED = 17 # QFN package pin number
SDA = "B2" # ball id for BGA/CSP packages
D13 = 8 # MDBT50Q-1MV2 module pin number
```

The build resolves each package pin to a SoC pad using the iobroker package
pin map selected by `CONFIG_IOBROKER_PACKAGE_<PACKAGE>`
(`modules/iobroker/packages/<package>.toml`) and exposes the name on the
matching pad in the `board` module. A name that already maps to the same pin
(from the devicetree or an earlier entry) is deduplicated; a name that maps
to two different pins is a build error. The package map must not be `CUSTOM`
or missing, and the pad must be on an enabled GPIO controller; otherwise the
build fails with an error naming the offending entry.

## Connector names

Devicetree connector nodes (`gpio-map`) get their names from a generic
per-compatible list in `cptools/zephyr2cp.py`. A board whose silkscreen
differs can override them per position in `circuitpython.toml` under
`[connectors.<node label>]`, keying the gpio-map position (the header pin
number, as a string) to a name:

```toml
[connectors.nordic_expansion_header]
0 = "EXP_00" # header GPIO 00
21 = "EXP_21" # header GPIO 21 (QSPI CS)
```

Positions left out of the table get no name.

## Testing other boards

[Any Zephyr board](https://docs.zephyrproject.org/latest/boards/index.html#) can
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ CONFIG_BOARD_SERIAL_BACKEND_CDC_ACM=n

# Enable the ST7789V TFT via the Zephyr display subsystem
CONFIG_DISPLAY=y

# Pin numbers are module pins (MDBT50Q-1MV2 carries the aQFN73 inside).
# The CLUE uses the MDBT50Q-1MV2 module, so pin numbers are module pins.
CONFIG_IOBROKER_PACKAGE_MDBT50Q_1MV2=y
94 changes: 88 additions & 6 deletions ports/zephyr-cp/boards/adafruit/clue_nrf52840_zephyr/board.overlay
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
// The UF2 board definition points the Zephyr console/shell/mcumgr/BT chosen
// nodes at board_cdc_acm_uart, which is deleted above: CircuitPython talks USB
// CDC ACM through its own usb_cdc bindings instead, and no serial pins are
// claimed at boot.
/ {
chosen {
zephyr,console = &uart0;
zephyr,shell-uart = &uart0;
zephyr,uart-mcumgr = &uart0;
zephyr,bt-mon-uart = &uart0;
zephyr,bt-c2h-uart = &uart0;
/delete-property/ zephyr,console;
/delete-property/ zephyr,shell-uart;
/delete-property/ zephyr,uart-mcumgr;
/delete-property/ zephyr,bt-mon-uart;
/delete-property/ zephyr,bt-c2h-uart;
};
};

Expand Down Expand Up @@ -42,8 +46,86 @@
};
};

// UART0, I2C1 and SPI3 are enabled with all pins disconnected and marked
// zephyr,deferred-init so nothing is claimed at boot: the iobroker
// initializes and routes a device to arbitrary pins when a busio object is
// constructed, and de-initializes it on release. (I2C0 is the fixed sensor
// bus, SPI2 the fixed display bus, and QSPI the fixed external flash.)
//
// I2C1 and SPI1 are the same peripheral instance (0x40004000), so only one
// mode can be enabled: I2C is the more useful dynamic bus on the CLUE.
// SPI1 is left disabled; UART1 is enabled by neither overlay because UART0
// already provides a dynamic serial instance.

&pinctrl {
uart0_dyn_default: uart0_dyn_default {
group1 {
psels = <NRF_PSEL_DISCONNECTED(UART_TX)>,
<NRF_PSEL_DISCONNECTED(UART_RX)>;
};
};

uart0_dyn_sleep: uart0_dyn_sleep {
group1 {
psels = <NRF_PSEL_DISCONNECTED(UART_TX)>,
<NRF_PSEL_DISCONNECTED(UART_RX)>;
};
};

i2c1_dyn_default: i2c1_dyn_default {
group1 {
psels = <NRF_PSEL_DISCONNECTED(TWIM_SDA)>,
<NRF_PSEL_DISCONNECTED(TWIM_SCL)>;
};
};

i2c1_dyn_sleep: i2c1_dyn_sleep {
group1 {
psels = <NRF_PSEL_DISCONNECTED(TWIM_SDA)>,
<NRF_PSEL_DISCONNECTED(TWIM_SCL)>;
};
};

spi3_dyn_default: spi3_dyn_default {
group1 {
psels = <NRF_PSEL_DISCONNECTED(SPIM_SCK)>,
<NRF_PSEL_DISCONNECTED(SPIM_MOSI)>,
<NRF_PSEL_DISCONNECTED(SPIM_MISO)>;
};
};

spi3_dyn_sleep: spi3_dyn_sleep {
group1 {
psels = <NRF_PSEL_DISCONNECTED(SPIM_SCK)>,
<NRF_PSEL_DISCONNECTED(SPIM_MOSI)>,
<NRF_PSEL_DISCONNECTED(SPIM_MISO)>;
};
};
};

&uart0 {
status = "okay";
status = "okay";
zephyr,deferred-init;
current-speed = <115200>;
pinctrl-0 = <&uart0_dyn_default>;
pinctrl-1 = <&uart0_dyn_sleep>;
pinctrl-names = "default", "sleep";
};

&i2c1 {
status = "okay";
zephyr,deferred-init;
pinctrl-0 = <&i2c1_dyn_default>;
pinctrl-1 = <&i2c1_dyn_sleep>;
pinctrl-names = "default", "sleep";
};

&spi3 {
status = "okay";
zephyr,deferred-init;
pinctrl-0 = <&spi3_dyn_default>;
pinctrl-1 = <&spi3_dyn_sleep>;
pinctrl-names = "default", "sleep";
};

#include "../../../app.overlay"
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,86 @@ NAME="CLUE nRF52840 Express"

# Non-Zephyr build of the same board; nvm and CIRCUITPY must sit where it puts them.
counterpart = "nordic/clue_nrf52840_express"


# Pin names copied from the ports/ counterpart board's pins.c and resolved
# through the package pin map selected in board.conf.

[pins]
# Pin names copied from the ports/ counterpart board's pins.c and resolved
# to MDBT50Q-1MV2 module pin numbers through the package pin map selected
# in board.conf.
P0 = 20
D0 = 20
A2 = 20
RX = 20
P1 = 21
D1 = 21
A3 = 21
TX = 21
P2 = 9
D2 = 9
A4 = 9
P3 = 13
D3 = 13
A5 = 13
P4 = 11
D4 = 11
A6 = 11
P5 = 50
D5 = 50
BUTTON_A = 50
P6 = 26
D6 = 26
P7 = 23
D7 = 23
P8 = 58
D8 = 58
P9 = 16
D9 = 16
P10 = 14
D10 = 14
A7 = 14
P11 = 3
D11 = 3
BUTTON_B = 3
P12 = 12
D12 = 12
A0 = 12
P13 = 24
D13 = 24
SCK = 24
P14 = 22
D14 = 22
MISO = 22
P15 = 19
D15 = 19
MOSI = 19
P16 = 10
D16 = 10
A1 = 10
P17 = 61
D17 = 61
L = 61
LED = 61
P18 = 38
D18 = 38
NEOPIXEL = 38
P19 = 49
D19 = 49
SCL = 49
P20 = 48
D20 = 48
SDA = 48
MICROPHONE_CLOCK = 18
MICROPHONE_DATA = 17
SPEAKER = 47
PROXIMITY_LIGHT_INTERRUPT = 52
ACCELEROMETER_GYRO_INTERRUPT = 57
WHITE_LEDS = 54
TFT_RESET = 60
TFT_BACKLIGHT = 59
TFT_CS = 29
TFT_DC = 37
TFT_SCK = 36
TFT_MOSI = 39
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
CONFIG_USE_DT_CODE_PARTITION=y

CONFIG_BOARD_SERIAL_BACKEND_CDC_ACM=n

# Pin numbers are module pins (MDBT50Q-1MV2 carries the aQFN73 inside).
# The feathers use the MDBT50Q-1MV2 module, so pin numbers are module pins.
CONFIG_IOBROKER_PACKAGE_MDBT50Q_1MV2=y
Loading
Loading