From dcb3f17ac72981f661d09c9c700b3018a3cc78c2 Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Fri, 30 Jan 2026 17:55:14 +0900 Subject: [PATCH 1/6] cores: arduino: zephyrCommon Specify port/pin instead of gpio_dt_spec. In preparation for improvements to allow the use of GPIOs without device tree definitions, the interface will be changed to specify port and pin instead of `gpio_dt_spec`. Signed-off-by: TOKITA Hiroshi --- cores/arduino/zephyrCommon.cpp | 106 +++++++++++++++++++++------------ 1 file changed, 69 insertions(+), 37 deletions(-) diff --git a/cores/arduino/zephyrCommon.cpp b/cores/arduino/zephyrCommon.cpp index dd670fe54..cc23d98e6 100644 --- a/cores/arduino/zephyrCommon.cpp +++ b/cores/arduino/zephyrCommon.cpp @@ -9,7 +9,7 @@ #include -static const struct gpio_dt_spec arduino_pins[] = { +static constexpr struct gpio_dt_spec arduino_pins[] = { DT_FOREACH_PROP_ELEM_SEP( DT_PATH(zephyr_user), digital_pin_gpios, GPIO_DT_SPEC_GET_BY_IDX, (, ))}; @@ -51,6 +51,17 @@ constexpr size_t is_first_appearance(const size_t &idx, const size_t &at, const return ((found == ((size_t)-1)) && (query == head) && (idx == at)) ? 1 : is_first_appearance(idx + 1, at, (query == head ? idx : found), query, tail...); + +constexpr inline const struct device *local_gpio_port(pin_size_t gpin) { + return (gpin < ARRAY_SIZE(arduino_pins)) ? arduino_pins[gpin].port : nullptr; +} + +constexpr inline pin_size_t local_gpio_pin(pin_size_t gpin) { + return (gpin < ARRAY_SIZE(arduino_pins)) ? arduino_pins[gpin].pin : pin_size_t(-1); +} + +inline int global_gpio_pin_configure(pin_size_t pinNumber, int flags) { + return gpio_pin_configure_dt(&arduino_pins[pinNumber], flags); } #define GET_DEVICE_VARGS(n, p, i, _) DEVICE_DT_GET(DT_GPIO_CTLR_BY_IDX(n, p, i)) @@ -64,12 +75,9 @@ const int port_num = sum_of_list( #define GPIO_NGPIOS(n, p, i) DT_PROP(DT_GPIO_CTLR_BY_IDX(n, p, i), ngpios) const int max_ngpios = max_in_list( 0, DT_FOREACH_PROP_ELEM_SEP(DT_PATH(zephyr_user), digital_pin_gpios, GPIO_NGPIOS, (, ))); - #else - const int port_num = 1; const int max_ngpios = 0; - #endif /* @@ -103,10 +111,10 @@ struct gpio_port_callback *find_gpio_port_callback(const struct device *dev) { } void setInterruptHandler(pin_size_t pinNumber, voidFuncPtr func) { - struct gpio_port_callback *pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); + struct gpio_port_callback *pcb = find_gpio_port_callback(local_gpio_port(pinNumber)); if (pcb) { - pcb->handlers[arduino_pins[pinNumber].pin].handler = func; + pcb->handlers[local_gpio_pin(pinNumber)].handler = func; } } @@ -190,24 +198,32 @@ void yield(void) { */ void pinMode(pin_size_t pinNumber, PinMode pinMode) { if (pinMode == INPUT) { // input mode - gpio_pin_configure_dt(&arduino_pins[pinNumber], GPIO_INPUT | GPIO_ACTIVE_HIGH); + global_gpio_pin_configure(pinNumber, GPIO_INPUT | GPIO_ACTIVE_HIGH); } else if (pinMode == INPUT_PULLUP) { // input with internal pull-up - gpio_pin_configure_dt(&arduino_pins[pinNumber], - GPIO_INPUT | GPIO_PULL_UP | GPIO_ACTIVE_HIGH); + global_gpio_pin_configure(pinNumber, GPIO_INPUT | GPIO_PULL_UP | GPIO_ACTIVE_HIGH); } else if (pinMode == INPUT_PULLDOWN) { // input with internal pull-down - gpio_pin_configure_dt(&arduino_pins[pinNumber], - GPIO_INPUT | GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH); + global_gpio_pin_configure(pinNumber, GPIO_INPUT | GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH); } else if (pinMode == OUTPUT) { // output mode - gpio_pin_configure_dt(&arduino_pins[pinNumber], GPIO_OUTPUT_LOW | GPIO_ACTIVE_HIGH); + global_gpio_pin_configure(pinNumber, GPIO_OUTPUT_LOW | GPIO_ACTIVE_HIGH); } } void digitalWrite(pin_size_t pinNumber, PinStatus status) { - gpio_pin_set_dt(&arduino_pins[pinNumber], status); + const struct device *port = local_gpio_port(pinNumber); + + if (port) { + gpio_pin_set(port, local_gpio_pin(pinNumber), status); + } } PinStatus digitalRead(pin_size_t pinNumber) { - return (gpio_pin_get_dt(&arduino_pins[pinNumber]) == 1) ? HIGH : LOW; + const struct device *port = local_gpio_port(pinNumber); + + if (port) { + return (gpio_pin_get(port, local_gpio_pin(pinNumber)) == 1) ? HIGH : LOW; + } else { + return LOW; + } } #if CONFIG_ARDUINO_MAX_TONES < 0 @@ -260,18 +276,19 @@ static struct pin_timer *find_pin_timer(pin_size_t pinNumber, bool active_only) void tone_expiry_cb(struct k_timer *timer) { struct pin_timer *pt = CONTAINER_OF(timer, struct pin_timer, timer); k_spinlock_key_t key = k_spin_lock(&pt->lock); + const struct device *port = local_gpio_port(pt->pin); pin_size_t pin = pt->pin; if (pt->count == 0 && !pt->infinity) { - if (pin != pin_size_t(-1)) { - gpio_pin_set_dt(&arduino_pins[pin], 0); + if (port) { + gpio_pin_set(port, local_gpio_pin(pt->pin), 0); } k_timer_stop(timer); pt->pin = pin_size_t(-1); } else { - if (pin != pin_size_t(-1)) { - gpio_pin_toggle_dt(&arduino_pins[pin]); + if (port) { + gpio_pin_toggle(port, local_gpio_pin(pt->pin)); } pt->count--; @@ -284,6 +301,7 @@ void tone(pin_size_t pinNumber, unsigned int frequency, unsigned long duration) k_spinlock_key_t key; struct pin_timer *pt; k_timeout_t timeout; + const struct device *port; pt = find_pin_timer(pinNumber, false); @@ -291,6 +309,8 @@ void tone(pin_size_t pinNumber, unsigned int frequency, unsigned long duration) return; } + port = local_gpio_port(pt->pin); + pinMode(pinNumber, OUTPUT); k_timer_stop(&pt->timer); @@ -299,7 +319,9 @@ void tone(pin_size_t pinNumber, unsigned int frequency, unsigned long duration) pt->pin = pin_size_t(-1); k_spin_unlock(&pt->lock, key); - gpio_pin_set_dt(&arduino_pins[pinNumber], 0); + if (port) { + gpio_pin_set(port, local_gpio_pin(pinNumber), 0); + } return; } @@ -316,13 +338,16 @@ void tone(pin_size_t pinNumber, unsigned int frequency, unsigned long duration) k_timer_init(&pt->timer, tone_expiry_cb, NULL); - gpio_pin_set_dt(&arduino_pins[pinNumber], 0); + if (port) { + gpio_pin_set(port, local_gpio_pin(pinNumber), 0); + } k_timer_start(&pt->timer, timeout, timeout); } void noTone(pin_size_t pinNumber) { struct pin_timer *pt; k_spinlock_key_t key; + const struct device *port; pt = find_pin_timer(pinNumber, true); @@ -330,12 +355,16 @@ void noTone(pin_size_t pinNumber) { return; } + port = local_gpio_port(pt->pin); + key = k_spin_lock(&pt->lock); k_timer_stop(&pt->timer); pt->pin = pin_size_t(-1); k_spin_unlock(&pt->lock, key); - gpio_pin_set_dt(&arduino_pins[pinNumber], 0); + if (port) { + gpio_pin_set(port, local_gpio_pin(pinNumber), 0); + } } void delay(unsigned long ms) { @@ -433,6 +462,7 @@ int analogRead(pin_size_t pinNumber) { #endif void attachInterrupt(pin_size_t pinNumber, voidFuncPtr callback, PinStatus pinStatus) { + const struct device *port = local_gpio_port(pinNumber); struct gpio_port_callback *pcb; gpio_flags_t intmode = 0; @@ -454,17 +484,18 @@ void attachInterrupt(pin_size_t pinNumber, voidFuncPtr callback, PinStatus pinSt return; } - pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); + pcb = find_gpio_port_callback(port); __ASSERT(pcb != nullptr, "gpio_port_callback not found"); - pcb->pins |= BIT(arduino_pins[pinNumber].pin); + pcb->pins |= BIT(local_gpio_pin(pinNumber)); setInterruptHandler(pinNumber, callback); enableInterrupt(pinNumber); - gpio_pin_interrupt_configure(arduino_pins[pinNumber].port, arduino_pins[pinNumber].pin, - intmode); - gpio_init_callback(&pcb->callback, handleGpioCallback, pcb->pins); - gpio_add_callback(arduino_pins[pinNumber].port, &pcb->callback); + if (port) { + gpio_pin_interrupt_configure(port, local_gpio_pin(pinNumber), intmode); + gpio_init_callback(&pcb->callback, handleGpioCallback, pcb->pins); + gpio_add_callback(port, &pcb->callback); + } } void detachInterrupt(pin_size_t pinNumber) { @@ -491,31 +522,32 @@ long random(long max) { #endif unsigned long pulseIn(pin_size_t pinNumber, uint8_t state, unsigned long timeout) { + const struct device *port = local_gpio_port(pinNumber); + const size_t pin = local_gpio_pin(pinNumber); struct k_timer timer; int64_t start, end, delta = 0; - const struct gpio_dt_spec *spec = &arduino_pins[pinNumber]; - if (!gpio_is_ready_dt(spec)) { + if (!device_is_ready(port)) { return 0; } k_timer_init(&timer, NULL, NULL); k_timer_start(&timer, K_MSEC(timeout), K_NO_WAIT); - while (gpio_pin_get_dt(spec) == state && k_timer_status_get(&timer) == 0) + while (gpio_pin_get(port, pin) == state && k_timer_status_get(&timer) == 0) ; if (k_timer_status_get(&timer) > 0) { goto cleanup; } - while (gpio_pin_get_dt(spec) != state && k_timer_status_get(&timer) == 0) + while (gpio_pin_get(port, pin) != state && k_timer_status_get(&timer) == 0) ; if (k_timer_status_get(&timer) > 0) { goto cleanup; } start = k_uptime_ticks(); - while (gpio_pin_get_dt(spec) == state && k_timer_status_get(&timer) == 0) + while (gpio_pin_get(port, pin) == state && k_timer_status_get(&timer) == 0) ; if (k_timer_status_get(&timer) > 0) { goto cleanup; @@ -530,18 +562,18 @@ unsigned long pulseIn(pin_size_t pinNumber, uint8_t state, unsigned long timeout } void enableInterrupt(pin_size_t pinNumber) { - struct gpio_port_callback *pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); + struct gpio_port_callback *pcb = find_gpio_port_callback(local_gpio_port(pinNumber)); if (pcb) { - pcb->handlers[arduino_pins[pinNumber].pin].enabled = true; + pcb->handlers[local_gpio_pin(pinNumber)].enabled = true; } } void disableInterrupt(pin_size_t pinNumber) { - struct gpio_port_callback *pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); + struct gpio_port_callback *pcb = find_gpio_port_callback(local_gpio_port(pinNumber)); if (pcb) { - pcb->handlers[arduino_pins[pinNumber].pin].enabled = false; + pcb->handlers[local_gpio_pin(pinNumber)].enabled = false; } } @@ -560,7 +592,7 @@ void noInterrupts(void) { } int digitalPinToInterrupt(pin_size_t pin) { - struct gpio_port_callback *pcb = find_gpio_port_callback(arduino_pins[pin].port); + struct gpio_port_callback *pcb = find_gpio_port_callback(local_gpio_port(pin)); return (pcb) ? pin : -1; } From 4eb16fa02399fd71192e962bf7be1b62b2cd1d4a Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Fri, 30 Jan 2026 17:55:14 +0900 Subject: [PATCH 2/6] cores: arduino: zephyrCommon: Generate config from connector definition If digital-gpio-pins is not defined, the pin configuration will be generated using the connector definition. This allows ArduinoCore-Zephyr to be used on boards that have arduino-header defined, without requiring specific configuration in ArduinoCore-Zephyr. Signed-off-by: TOKITA Hiroshi --- cores/arduino/Arduino.h | 37 ++++- cores/arduino/connectors/arduino_header_r3.h | 64 ++++++++ cores/arduino/connectors/connector.h | 70 ++++++++ cores/arduino/zephyrCommon.cpp | 158 ++++++++++++++++--- 4 files changed, 301 insertions(+), 28 deletions(-) create mode 100644 cores/arduino/connectors/arduino_header_r3.h create mode 100644 cores/arduino/connectors/connector.h diff --git a/cores/arduino/Arduino.h b/cores/arduino/Arduino.h index 5b6972966..fd88bd7b8 100644 --- a/cores/arduino/Arduino.h +++ b/cores/arduino/Arduino.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2022 Dhruva Gole + * Copyright (c) 2026 TOKITA Hiroshi * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,6 +15,8 @@ #include #include +#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), digital_pin_gpios) + #if DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios) > 0 /* Note: DT_REG_ADDR needs an expanded argument or it will not work properly */ #define DIGITAL_PIN_MATCHES(dev_pha, pin, dev, num) \ @@ -82,15 +85,33 @@ #define DN_ENUMS(n, p, i) D##i = i +#else + +#include "connectors/connector.h" + +#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), builtin_led_gpios) && \ + (DT_PROP_LEN(DT_PATH(zephyr_user), builtin_led_gpios) > 0) +#define ZARD_LED_BUILTIN \ + ZARD_GLOBAL_GPIO_OFFSET(DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), builtin_led_gpios, 0)) + \ + DT_PHA_BY_IDX(DT_PATH(zephyr_user), builtin_led_gpios, 0, pin) +#elif DT_NODE_EXISTS(DT_ALIAS(led0)) +#define ZARD_LED_BUILTIN \ + ZARD_GLOBAL_GPIO_OFFSET(DT_PHANDLE_BY_IDX(DT_ALIAS(led0), gpios, 0)) + \ + DT_PHA_BY_IDX(DT_ALIAS(led0), gpios, 0, pin) +#endif +#endif // digital_pin_gpios + /* * expand as * enum digitalPins { D0, D1, ... LED... NUM_OF_DIGITAL_PINS }; */ enum digitalPins { -#if DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios) > 0 +#if DT_PROP_LEN_OR(DT_PATH(zephyr_user), digital_pin_gpios, 0) > 0 DT_FOREACH_PROP_ELEM_SEP(DT_PATH(zephyr_user), digital_pin_gpios, DN_ENUMS, (, )), -#endif NUM_OF_DIGITAL_PINS +#elif defined(ZARD_CONNECTOR) + DT_FOREACH_MAP_ENTRY(DT_NODELABEL(ZARD_CONNECTOR), gpio_map, ZARD_CONN_DN_ENUMS) +#endif }; #ifdef CONFIG_ADC @@ -98,9 +119,19 @@ enum digitalPins { #define AN_ENUMS(n, p, i) \ A##i = DIGITAL_PIN_GPIOS_FIND_PIN(DT_REG_ADDR(DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), p, i)), \ DT_PHA_BY_IDX(DT_PATH(zephyr_user), p, i, pin)), +#define ZARD_AN_ENUM_GLOBAL(n, p, i) \ + ZARD_GLOBAL_GPIO_OFFSET(DT_PHANDLE_BY_IDX(n, p, i)) + DT_PHA_BY_IDX(n, p, i, pin) enum analogPins { - DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), adc_pin_gpios, AN_ENUMS) +#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), adc_pin_gpios) +#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), digital_pin_gpios) + DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), adc_pin_gpios, AN_ENUMS) NUM_OF_ANALOG_PINS +#else + DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), adc_pin_gpios, ZARD_AN_ENUM_GLOBAL) +#endif +#elif defined(ZARD_CONNECTOR) + DT_FOREACH_MAP_ENTRY(DT_NODELABEL(ZARD_CONNECTOR), gpio_map, ZARD_CONN_AN_ENUMS) +#endif }; #endif diff --git a/cores/arduino/connectors/arduino_header_r3.h b/cores/arduino/connectors/arduino_header_r3.h new file mode 100644 index 000000000..4a4a3747e --- /dev/null +++ b/cores/arduino/connectors/arduino_header_r3.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2026 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#define ZARD_CONNECTOR arduino_header + +#define ZARD_ARDUINO_HEADER_R3_PIN_NAME_A_0 A0 +#define ZARD_ARDUINO_HEADER_R3_PIN_NAME_A_1 A1 +#define ZARD_ARDUINO_HEADER_R3_PIN_NAME_A_2 A2 +#define ZARD_ARDUINO_HEADER_R3_PIN_NAME_A_3 A3 +#define ZARD_ARDUINO_HEADER_R3_PIN_NAME_A_4 A4 +#define ZARD_ARDUINO_HEADER_R3_PIN_NAME_A_5 A5 + +#define ZARD_ARDUINO_HEADER_R3_PIN_NAME_D_6 D0 +#define ZARD_ARDUINO_HEADER_R3_PIN_NAME_D_7 D1 +#define ZARD_ARDUINO_HEADER_R3_PIN_NAME_D_8 D2 +#define ZARD_ARDUINO_HEADER_R3_PIN_NAME_D_9 D3 +#define ZARD_ARDUINO_HEADER_R3_PIN_NAME_D_10 D4 +#define ZARD_ARDUINO_HEADER_R3_PIN_NAME_D_11 D5 +#define ZARD_ARDUINO_HEADER_R3_PIN_NAME_D_12 D6 +#define ZARD_ARDUINO_HEADER_R3_PIN_NAME_D_13 D7 +#define ZARD_ARDUINO_HEADER_R3_PIN_NAME_D_14 D8 +#define ZARD_ARDUINO_HEADER_R3_PIN_NAME_D_15 D9 +#define ZARD_ARDUINO_HEADER_R3_PIN_NAME_D_16 D10 +#define ZARD_ARDUINO_HEADER_R3_PIN_NAME_D_17 D11 +#define ZARD_ARDUINO_HEADER_R3_PIN_NAME_D_18 D12 +#define ZARD_ARDUINO_HEADER_R3_PIN_NAME_D_19 D13 +#define ZARD_ARDUINO_HEADER_R3_PIN_NAME_D_20 D14 +#define ZARD_ARDUINO_HEADER_R3_PIN_NAME_D_21 D15 + +#define ZARD_ARDUINO_HEADER_R3_IS_ANALOG_0 1 +#define ZARD_ARDUINO_HEADER_R3_IS_ANALOG_1 1 +#define ZARD_ARDUINO_HEADER_R3_IS_ANALOG_2 1 +#define ZARD_ARDUINO_HEADER_R3_IS_ANALOG_3 1 +#define ZARD_ARDUINO_HEADER_R3_IS_ANALOG_4 1 +#define ZARD_ARDUINO_HEADER_R3_IS_ANALOG_5 1 +#define ZARD_ARDUINO_HEADER_R3_IS_DIGITAL_6 1 +#define ZARD_ARDUINO_HEADER_R3_IS_DIGITAL_7 1 +#define ZARD_ARDUINO_HEADER_R3_IS_DIGITAL_8 1 +#define ZARD_ARDUINO_HEADER_R3_IS_DIGITAL_9 1 +#define ZARD_ARDUINO_HEADER_R3_IS_DIGITAL_10 1 +#define ZARD_ARDUINO_HEADER_R3_IS_DIGITAL_11 1 +#define ZARD_ARDUINO_HEADER_R3_IS_DIGITAL_12 1 +#define ZARD_ARDUINO_HEADER_R3_IS_DIGITAL_13 1 +#define ZARD_ARDUINO_HEADER_R3_IS_DIGITAL_14 1 +#define ZARD_ARDUINO_HEADER_R3_IS_DIGITAL_15 1 +#define ZARD_ARDUINO_HEADER_R3_IS_DIGITAL_16 1 +#define ZARD_ARDUINO_HEADER_R3_IS_DIGITAL_17 1 +#define ZARD_ARDUINO_HEADER_R3_IS_DIGITAL_18 1 +#define ZARD_ARDUINO_HEADER_R3_IS_DIGITAL_19 1 +#define ZARD_ARDUINO_HEADER_R3_IS_DIGITAL_20 1 +#define ZARD_ARDUINO_HEADER_R3_IS_DIGITAL_21 1 + +#if DT_NODE_EXISTS(DT_NODELABEL(arduino_adc)) +#define ZARD_ADC_CONNECTOR arduino_adc +#endif + +#if DT_NODE_EXISTS(DT_NODELABEL(arduino_pwm)) +#define ZARD_PWM_CONNECTOR arduino_pwm +#endif diff --git a/cores/arduino/connectors/connector.h b/cores/arduino/connectors/connector.h new file mode 100644 index 000000000..12d02d39b --- /dev/null +++ b/cores/arduino/connectors/connector.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2026 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#if DT_NODE_EXISTS(DT_NODELABEL(arduino_header)) && \ + DT_NODE_HAS_COMPAT(DT_NODELABEL(arduino_header), arduino_header_r3) +#include "arduino_header_r3.h" +#else +#error "Only arduino-header-r3 connector is supported" +#endif + +#define ZARD_CHECK_GPIO_CTLR(node_id) \ + COND_CODE_1(DT_NODE_HAS_PROP(node_id, gpio_controller), (node_id,), ()) + +#define ZARD_ALL_GPIO_CTLR DT_FOREACH_NODE(ZARD_CHECK_GPIO_CTLR) + +#define ZARD_IDX_IF_MATCH(i, n) \ + COND_CODE_1(DT_SAME_NODE(n, GET_ARG_N(UTIL_INC(i), ZARD_ALL_GPIO_CTLR)), (i), ()) + +#define ZARD_MATCH_IDX(n) LISTIFY(NUM_VA_ARGS_LESS_1(ZARD_ALL_GPIO_CTLR), ZARD_IDX_IF_MATCH, (), n) + +#define ZARD_GET_NGPIOS(i, ...) DT_PROP(GET_ARG_N(UTIL_INC(i), __VA_ARGS__), ngpios) +#define ZARD_SUM_NGPIOS(...) LISTIFY(NUM_VA_ARGS(__VA_ARGS__), ZARD_GET_NGPIOS, (+), __VA_ARGS__) + +#define ZARD_GLOBAL_GPIO_OFFSET_(ph) \ + ZARD_SUM_NGPIOS(GET_ARGS_FIRST_N(ZARD_MATCH_IDX(ph), ZARD_ALL_GPIO_CTLR)) + +#define ZARD_GLOBAL_GPIO_OFFSET(ph) \ + COND_CODE_1(IS_EQ(NUM_VA_ARGS(ZARD_GLOBAL_GPIO_OFFSET_(ph)), 0), \ + (0), (ZARD_GLOBAL_GPIO_OFFSET_(ph))) + +#define ZARD_CONNECTOR_PIN_IS_DIGITAL(node, num) \ + UTIL_CAT(UTIL_CAT(ZARD_, \ + UTIL_CAT(DT_STRING_UPPER_TOKEN_BY_IDX(node, compatible, 0), _IS_DIGITAL_)), \ + num) + +#define ZARD_CONNECTOR_PIN_IS_ANALOG(node, num) \ + UTIL_CAT( \ + UTIL_CAT(ZARD_, UTIL_CAT(DT_STRING_UPPER_TOKEN_BY_IDX(node, compatible, 0), _IS_ANALOG_)), \ + num) + +#define ZARD_CONNECTOR_PIN_NAME_D(node, num) \ + UTIL_CAT(UTIL_CAT(ZARD_, \ + UTIL_CAT(DT_STRING_UPPER_TOKEN_BY_IDX(node, compatible, 0), _PIN_NAME_D_)), \ + num) + +#define ZARD_CONNECTOR_PIN_NAME_A(node, num) \ + UTIL_CAT(UTIL_CAT(ZARD_, \ + UTIL_CAT(DT_STRING_UPPER_TOKEN_BY_IDX(node, compatible, 0), _PIN_NAME_A_)), \ + num) + +#define ZARD_CONN_DN_ENUMS(n, p, i) \ + COND_CODE_1(ZARD_CONNECTOR_PIN_IS_DIGITAL(DT_NODELABEL(ZARD_CONNECTOR), \ + DT_MAP_ENTRY_CHILD_SPECIFIER_BY_IDX(n, p, i, 0)), \ + (ZARD_CONNECTOR_PIN_NAME_D(DT_NODELABEL(ZARD_CONNECTOR), \ + DT_MAP_ENTRY_CHILD_SPECIFIER_BY_IDX(n, p, i, 0)) = \ + ZARD_GLOBAL_GPIO_OFFSET(DT_MAP_ENTRY_PARENT_BY_IDX(n, p, i)) + \ + DT_MAP_ENTRY_PARENT_SPECIFIER_BY_IDX(n, p, i, 0),), ()) + +#define ZARD_CONN_AN_ENUMS(n, p, i) \ + COND_CODE_1(ZARD_CONNECTOR_PIN_IS_ANALOG(DT_NODELABEL(ZARD_CONNECTOR), \ + DT_MAP_ENTRY_CHILD_SPECIFIER_BY_IDX(n, p, i, 0)), \ + (ZARD_CONNECTOR_PIN_NAME_A(DT_NODELABEL(ZARD_CONNECTOR), \ + DT_MAP_ENTRY_CHILD_SPECIFIER_BY_IDX(n, p, i, 0)) = \ + ZARD_GLOBAL_GPIO_OFFSET(DT_MAP_ENTRY_PARENT_BY_IDX(n, p, i)) + \ + DT_MAP_ENTRY_PARENT_SPECIFIER_BY_IDX(n, p, i, 0),), ()) diff --git a/cores/arduino/zephyrCommon.cpp b/cores/arduino/zephyrCommon.cpp index cc23d98e6..daef0797e 100644 --- a/cores/arduino/zephyrCommon.cpp +++ b/cores/arduino/zephyrCommon.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2022 Dhruva Gole + * Copyright (c) 2026 TOKITA Hiroshi * * SPDX-License-Identifier: Apache-2.0 */ @@ -9,13 +10,27 @@ #include +#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), digital_pin_gpios) static constexpr struct gpio_dt_spec arduino_pins[] = { DT_FOREACH_PROP_ELEM_SEP( DT_PATH(zephyr_user), digital_pin_gpios, GPIO_DT_SPEC_GET_BY_IDX, (, ))}; +#else +#define GET_GPIO_DEVICES(node_id) \ + COND_CODE_1(DT_NODE_HAS_PROP(node_id, gpio_controller), \ + (COND_CODE_1(DT_NODE_HAS_STATUS_OKAY(node_id), \ + (DEVICE_DT_GET(node_id),), \ + (nullptr,))), \ + ()) -namespace { +#define GET_GPIO_NGPIOS(node_id) \ + COND_CODE_1(DT_NODE_HAS_PROP(node_id, gpio_controller), \ + (DT_PROP_OR(node_id, ngpios, 0),), ()) -#if DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios) > 0 +static constexpr const struct device *gpio_ports[] = {DT_FOREACH_NODE(GET_GPIO_DEVICES)}; +static constexpr uint32_t gpio_ngpios[] = {DT_FOREACH_NODE(GET_GPIO_NGPIOS)}; +#endif // digital_pin_gpios + +namespace { /* * Calculate GPIO ports/pins number statically from devicetree configuration @@ -51,19 +66,91 @@ constexpr size_t is_first_appearance(const size_t &idx, const size_t &at, const return ((found == ((size_t)-1)) && (query == head) && (idx == at)) ? 1 : is_first_appearance(idx + 1, at, (query == head ? idx : found), query, tail...); +} + +#if !DT_NODE_HAS_PROP(DT_PATH(zephyr_user), digital_pin_gpios) +constexpr inline const struct device *local_gpio_port(pin_size_t gpin); + +constexpr inline const struct device *local_gpio_port_r(pin_size_t pin, + const struct device *const *ctrl, + const uint32_t accum, const uint32_t *end, + size_t n) { + return (n == 0) ? nullptr : + (pin < accum + end[0]) ? + ctrl[0] : + local_gpio_port_r(pin, ctrl + 1, accum + end[0], end + 1, n - 1); +} + +constexpr inline size_t port_index_r(const struct device *target, const struct device *const *table, + pin_size_t idx, size_t n) { + return (n == 0) ? size_t(-1) : + (target == table[0]) ? idx : + port_index_r(target, table + 1, idx + 1, n - 1); +} + +constexpr inline pin_size_t port_idx(pin_size_t gpin) { + return port_index_r(local_gpio_port(gpin), gpio_ports, 0, ARRAY_SIZE(gpio_ports)); +} + +constexpr inline pin_size_t end_accum_r(const uint32_t accum, const uint32_t *end, size_t n) { + return (n == 0) ? accum : end_accum_r(accum + end[0], end + 1, n - 1); +} + +constexpr inline pin_size_t end_accum(size_t n) { + return end_accum_r(0, gpio_ngpios, n); +} + +constexpr inline pin_size_t global_gpio_pin_(size_t port_idx, pin_size_t lpin) { + return port_idx == size_t(-1) ? size_t(-1) : end_accum(port_idx) + lpin; +} + +constexpr inline pin_size_t global_gpio_pin(const struct device *lport, pin_size_t lpin) { + return global_gpio_pin_(port_index_r(lport, gpio_ports, 0, ARRAY_SIZE(gpio_ports)), lpin); +} +#endif // digital_pin_gpios constexpr inline const struct device *local_gpio_port(pin_size_t gpin) { +#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), digital_pin_gpios) return (gpin < ARRAY_SIZE(arduino_pins)) ? arduino_pins[gpin].port : nullptr; +#else + return local_gpio_port_r(gpin, gpio_ports, 0, gpio_ngpios, ARRAY_SIZE(gpio_ports)); +#endif +} + +constexpr pin_size_t invalid_pin_number = pin_size_t(-1); + +constexpr inline bool local_gpio_pin_is_valid(pin_size_t pin) { + return pin != invalid_pin_number; } constexpr inline pin_size_t local_gpio_pin(pin_size_t gpin) { - return (gpin < ARRAY_SIZE(arduino_pins)) ? arduino_pins[gpin].pin : pin_size_t(-1); +#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), digital_pin_gpios) + return (gpin < ARRAY_SIZE(arduino_pins)) ? arduino_pins[gpin].pin : invalid_pin_number; +#else + return port_idx(gpin) == invalid_pin_number ? invalid_pin_number : + gpin - end_accum(port_idx(gpin)); +#endif } inline int global_gpio_pin_configure(pin_size_t pinNumber, int flags) { +#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), digital_pin_gpios) + if (pinNumber >= ARRAY_SIZE(arduino_pins)) { + return -1; + } return gpio_pin_configure_dt(&arduino_pins[pinNumber], flags); +#else + const struct device *port = local_gpio_port(pinNumber); + + if (port) { + return gpio_pin_configure(port, local_gpio_pin(pinNumber), flags); + } else { + return -1; + } +#endif } +#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), digital_pin_gpios) +#if DT_PROP_LEN_OR(DT_PATH(zephyr_user), digital_pin_gpios, 0) > 0 #define GET_DEVICE_VARGS(n, p, i, _) DEVICE_DT_GET(DT_GPIO_CTLR_BY_IDX(n, p, i)) #define FIRST_APPEARANCE(n, p, i) \ is_first_appearance(0, i, ((size_t)-1), DEVICE_DT_GET(DT_GPIO_CTLR_BY_IDX(n, p, i)), \ @@ -78,7 +165,11 @@ const int max_ngpios = max_in_list( #else const int port_num = 1; const int max_ngpios = 0; -#endif +#endif // digital_pin_gpios > 0 +#else +const int port_num = ARRAY_SIZE(gpio_ports); +const int max_ngpios = max_in_list(0, DT_FOREACH_NODE(GET_GPIO_NGPIOS) 0); +#endif // digital_pin_gpios /* * GPIO callback implementation @@ -97,6 +188,10 @@ struct gpio_port_callback { } port_callback[port_num] = {0}; struct gpio_port_callback *find_gpio_port_callback(const struct device *dev) { + if (dev == nullptr) { + return nullptr; + } + for (size_t i = 0; i < ARRAY_SIZE(port_callback); i++) { if (port_callback[i].dev == dev) { return &port_callback[i]; @@ -111,10 +206,12 @@ struct gpio_port_callback *find_gpio_port_callback(const struct device *dev) { } void setInterruptHandler(pin_size_t pinNumber, voidFuncPtr func) { - struct gpio_port_callback *pcb = find_gpio_port_callback(local_gpio_port(pinNumber)); + const struct device *port = local_gpio_port(pinNumber); + const pin_size_t pin = local_gpio_pin(pinNumber); + struct gpio_port_callback *pcb = find_gpio_port_callback(port); - if (pcb) { - pcb->handlers[local_gpio_pin(pinNumber)].handler = func; + if (pcb && local_gpio_pin_is_valid(pin)) { + pcb->handlers[pin].handler = func; } } @@ -227,7 +324,13 @@ PinStatus digitalRead(pin_size_t pinNumber) { } #if CONFIG_ARDUINO_MAX_TONES < 0 +#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), digital_pin_gpios) #define MAX_TONE_PINS DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios) +#elif defined(ZARD_CONNECTOR) +#define MAX_TONE_PINS DT_PROP_LEN(DT_NODELABEL(ZARD_CONNECTOR), gpio_map) +#else +#define MAX_TONE_PINS 1 +#endif #else #define MAX_TONE_PINS CONFIG_ARDUINO_MAX_TONES #endif @@ -237,7 +340,7 @@ PinStatus digitalRead(pin_size_t pinNumber) { static struct pin_timer { struct k_timer timer; uint32_t count{0}; - pin_size_t pin{pin_size_t(-1)}; + pin_size_t pin{invalid_pin_number}; bool infinity{false}; struct k_spinlock lock; } arduino_pin_timers[MAX_TONE_PINS]; @@ -261,7 +364,7 @@ static struct pin_timer *find_pin_timer(pin_size_t pinNumber, bool active_only) for (size_t i = 0; i < ARRAY_SIZE(arduino_pin_timers); i++) { k_spinlock_key_t key = k_spin_lock(&arduino_pin_timers[i].lock); - if (arduino_pin_timers[i].pin == pin_size_t(-1)) { + if (arduino_pin_timers[i].pin == invalid_pin_number) { arduino_pin_timers[i].pin = pinNumber; k_spin_unlock(&arduino_pin_timers[i].lock, key); return &arduino_pin_timers[i]; @@ -285,7 +388,7 @@ void tone_expiry_cb(struct k_timer *timer) { } k_timer_stop(timer); - pt->pin = pin_size_t(-1); + pt->pin = invalid_pin_number; } else { if (port) { gpio_pin_toggle(port, local_gpio_pin(pt->pin)); @@ -309,14 +412,14 @@ void tone(pin_size_t pinNumber, unsigned int frequency, unsigned long duration) return; } - port = local_gpio_port(pt->pin); + port = local_gpio_port(pinNumber); pinMode(pinNumber, OUTPUT); k_timer_stop(&pt->timer); if (frequency == 0) { key = k_spin_lock(&pt->lock); - pt->pin = pin_size_t(-1); + pt->pin = invalid_pin_number; k_spin_unlock(&pt->lock, key); if (port) { @@ -359,7 +462,7 @@ void noTone(pin_size_t pinNumber) { key = k_spin_lock(&pt->lock); k_timer_stop(&pt->timer); - pt->pin = pin_size_t(-1); + pt->pin = invalid_pin_number; k_spin_unlock(&pt->lock, key); if (port) { @@ -463,6 +566,7 @@ int analogRead(pin_size_t pinNumber) { void attachInterrupt(pin_size_t pinNumber, voidFuncPtr callback, PinStatus pinStatus) { const struct device *port = local_gpio_port(pinNumber); + const pin_size_t pin = local_gpio_pin(pinNumber); struct gpio_port_callback *pcb; gpio_flags_t intmode = 0; @@ -484,18 +588,20 @@ void attachInterrupt(pin_size_t pinNumber, voidFuncPtr callback, PinStatus pinSt return; } + if (port == nullptr || !local_gpio_pin_is_valid(pin)) { + return; + } + pcb = find_gpio_port_callback(port); __ASSERT(pcb != nullptr, "gpio_port_callback not found"); - pcb->pins |= BIT(local_gpio_pin(pinNumber)); + pcb->pins |= BIT(pin); setInterruptHandler(pinNumber, callback); enableInterrupt(pinNumber); - if (port) { - gpio_pin_interrupt_configure(port, local_gpio_pin(pinNumber), intmode); - gpio_init_callback(&pcb->callback, handleGpioCallback, pcb->pins); - gpio_add_callback(port, &pcb->callback); - } + gpio_pin_interrupt_configure(port, pin, intmode); + gpio_init_callback(&pcb->callback, handleGpioCallback, pcb->pins); + gpio_add_callback(port, &pcb->callback); } void detachInterrupt(pin_size_t pinNumber) { @@ -523,11 +629,11 @@ long random(long max) { unsigned long pulseIn(pin_size_t pinNumber, uint8_t state, unsigned long timeout) { const struct device *port = local_gpio_port(pinNumber); - const size_t pin = local_gpio_pin(pinNumber); + const pin_size_t pin = local_gpio_pin(pinNumber); struct k_timer timer; int64_t start, end, delta = 0; - if (!device_is_ready(port)) { + if (port == nullptr || !local_gpio_pin_is_valid(pin) || !device_is_ready(port)) { return 0; } @@ -562,18 +668,20 @@ unsigned long pulseIn(pin_size_t pinNumber, uint8_t state, unsigned long timeout } void enableInterrupt(pin_size_t pinNumber) { + const pin_size_t pin = local_gpio_pin(pinNumber); struct gpio_port_callback *pcb = find_gpio_port_callback(local_gpio_port(pinNumber)); - if (pcb) { - pcb->handlers[local_gpio_pin(pinNumber)].enabled = true; + if (pcb && local_gpio_pin_is_valid(pin)) { + pcb->handlers[pin].enabled = true; } } void disableInterrupt(pin_size_t pinNumber) { + const pin_size_t pin = local_gpio_pin(pinNumber); struct gpio_port_callback *pcb = find_gpio_port_callback(local_gpio_port(pinNumber)); - if (pcb) { - pcb->handlers[local_gpio_pin(pinNumber)].enabled = false; + if (pcb && local_gpio_pin_is_valid(pin)) { + pcb->handlers[pin].enabled = false; } } From b2ba6d7b7c07ff4e56d743a24702951374efe420 Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Fri, 30 Jan 2026 17:55:14 +0900 Subject: [PATCH 3/6] cores: arduino: zephyrCommon: Generate PWM config from connector definition Like digital pins, PWMs are also defined from connectors. Signed-off-by: TOKITA Hiroshi --- cores/arduino/zephyrCommon.cpp | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/cores/arduino/zephyrCommon.cpp b/cores/arduino/zephyrCommon.cpp index daef0797e..ea1d9da35 100644 --- a/cores/arduino/zephyrCommon.cpp +++ b/cores/arduino/zephyrCommon.cpp @@ -232,13 +232,42 @@ void handleGpioCallback(const struct device *port, struct gpio_callback *cb, uin #define PWM_PINS(n, p, i) \ DIGITAL_PIN_GPIOS_FIND_PIN(DT_REG_ADDR(DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), p, i)), \ DT_PHA_BY_IDX(DT_PATH(zephyr_user), p, i, pin)), +#define PWM_PINS_GLOBAL(n, p, i) \ + ZARD_GLOBAL_GPIO_OFFSET(DT_PHANDLE_BY_IDX(n, p, i)) + DT_PHA_BY_IDX(n, p, i, pin), +#define PWM_CONN_CHANNEL_DT(n, p, i) \ + COND_CODE_1(DT_NODE_HAS_STATUS_OKAY(DT_MAP_ENTRY_PARENT_BY_IDX(n, p, i)), \ + ({ .dev = DEVICE_DT_GET(DT_MAP_ENTRY_PARENT_BY_IDX(n, p, i)), \ + .channel = DT_MAP_ENTRY_PARENT_SPECIFIER_BY_IDX(n, p, i, 0), \ + .period = 255, },), \ + ()) +#define PWM_CONN_PINNUM(n, p, i) \ + COND_CODE_1(DT_NODE_HAS_STATUS_OKAY(DT_MAP_ENTRY_PARENT_BY_IDX(n, p, i)), \ + (ZARD_CONNECTOR_PIN_NAME_D(DT_NODELABEL(ZARD_CONNECTOR), \ + DT_MAP_ENTRY_CHILD_SPECIFIER_BY_IDX(n, p, i, 0)),), \ + ()) const struct pwm_dt_spec arduino_pwm[] = { - DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), pwms, PWM_DT_SPEC)}; +#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), pwms) + DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), pwms, PWM_DT_SPEC) +#elif defined(ZARD_PWM_CONNECTOR) + DT_FOREACH_MAP_ENTRY(DT_NODELABEL(ZARD_PWM_CONNECTOR), pwm_map, PWM_CONN_CHANNEL_DT) +#endif +}; /* pwm-pins node provides a mapping digital pin numbers to pwm channels */ const pin_size_t arduino_pwm_pins[] = { - DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), pwm_pin_gpios, PWM_PINS)}; +#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), pwm_pin_gpios) +#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), digital_pin_gpios) + DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), pwm_pin_gpios, PWM_PINS) +#else + DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), pwm_pin_gpios, PWM_PINS_GLOBAL) +#endif +#elif defined(ZARD_PWM_CONNECTOR) + DT_FOREACH_MAP_ENTRY(DT_NODELABEL(ZARD_PWM_CONNECTOR), pwm_map, PWM_CONN_PINNUM) +#endif +}; + +BUILD_ASSERT(ARRAY_SIZE(arduino_pwm) == ARRAY_SIZE(arduino_pwm_pins)); size_t pwm_pin_index(pin_size_t pinNumber) { for (size_t i = 0; i < ARRAY_SIZE(arduino_pwm_pins); i++) { From a602b3c227735c6d1cb753223430041c0da7d6ff Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Fri, 30 Jan 2026 17:55:14 +0900 Subject: [PATCH 4/6] cores: arduino: zephyrCommon: Generate ADC config from connector definition Like digital pins, ADCs are also defined from connectors. Signed-off-by: TOKITA Hiroshi --- cores/arduino/zephyrCommon.cpp | 46 +++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/cores/arduino/zephyrCommon.cpp b/cores/arduino/zephyrCommon.cpp index ea1d9da35..2daec4282 100644 --- a/cores/arduino/zephyrCommon.cpp +++ b/cores/arduino/zephyrCommon.cpp @@ -286,17 +286,55 @@ size_t pwm_pin_index(pin_size_t pinNumber) { #define ADC_PINS(n, p, i) \ DIGITAL_PIN_GPIOS_FIND_PIN(DT_REG_ADDR(DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), p, i)), \ DT_PHA_BY_IDX(DT_PATH(zephyr_user), p, i, pin)), +#define ADC_PINS_GLOBAL(n, p, i) \ + ZARD_GLOBAL_GPIO_OFFSET(DT_PHANDLE_BY_IDX(n, p, i)) + DT_PHA_BY_IDX(n, p, i, pin), #define ADC_CH_CFG(n, p, i) arduino_adc[i].channel_cfg, +#define ADC_CONN_CHANNEL_CFG(n, p, i) \ + COND_CODE_1(DT_NODE_HAS_STATUS_OKAY(DT_MAP_ENTRY_PARENT_BY_IDX(n, p, i)), \ + (ADC_CHANNEL_CFG_DT(ADC_CHANNEL_DT_NODE(DT_MAP_ENTRY_PARENT_BY_IDX(n, p, i), \ + DT_MAP_ENTRY_PARENT_SPECIFIER_BY_IDX(n, p, i, 0))),), \ + ()) +#define ADC_CONN_CHANNEL_DT(n, p, i) \ + COND_CODE_1(DT_NODE_HAS_STATUS_OKAY(DT_MAP_ENTRY_PARENT_BY_IDX(n, p, i)), \ + (ADC_DT_SPEC_STRUCT(DT_MAP_ENTRY_PARENT_BY_IDX(n, p, i), \ + DT_MAP_ENTRY_PARENT_SPECIFIER_BY_IDX(n, p, i, 0)),), \ + ()) +#define ADC_CONN_PINNUM(n, p, i) \ + COND_CODE_1(DT_NODE_HAS_STATUS_OKAY(DT_MAP_ENTRY_PARENT_BY_IDX(n, p, i)), \ + (ZARD_CONNECTOR_PIN_NAME_A(DT_NODELABEL(ZARD_CONNECTOR), \ + DT_MAP_ENTRY_CHILD_SPECIFIER_BY_IDX(n, p, i, 0)),), \ + ()) const struct adc_dt_spec arduino_adc[] = { - DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels, ADC_DT_SPEC)}; +#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), io_channels) + DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels, ADC_DT_SPEC) +#elif defined(ZARD_ADC_CONNECTOR) + DT_FOREACH_MAP_ENTRY(DT_NODELABEL(ZARD_ADC_CONNECTOR), io_channel_map, ADC_CONN_CHANNEL_DT) +#endif +}; -/* io-channel-pins node provides a mapping digital pin numbers to adc channels */ +/* adc-pin-gpios provides a mapping digital pin numbers to adc channels */ const pin_size_t arduino_analog_pins[] = { - DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), adc_pin_gpios, ADC_PINS)}; +#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), adc_pin_gpios) +#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), digital_pin_gpios) + DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), adc_pin_gpios, ADC_PINS) +#else + DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), adc_pin_gpios, ADC_PINS_GLOBAL) +#endif +#elif defined(ZARD_ADC_CONNECTOR) + DT_FOREACH_MAP_ENTRY(DT_NODELABEL(ZARD_ADC_CONNECTOR), io_channel_map, ADC_CONN_PINNUM) +#endif +}; struct adc_channel_cfg channel_cfg[ARRAY_SIZE(arduino_analog_pins)] = { - DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels, ADC_CH_CFG)}; +#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), io_channels) + DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels, ADC_CH_CFG) +#elif defined(ZARD_ADC_CONNECTOR) + DT_FOREACH_MAP_ENTRY(DT_NODELABEL(ZARD_ADC_CONNECTOR), io_channel_map, ADC_CONN_CHANNEL_CFG) +#endif +}; + +BUILD_ASSERT(ARRAY_SIZE(arduino_adc) == ARRAY_SIZE(arduino_analog_pins)); size_t analog_pin_index(pin_size_t pinNumber) { for (size_t i = 0; i < ARRAY_SIZE(arduino_analog_pins); i++) { From 98d8134d605ce9ffcabf45303a5ebb13e359c0d8 Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Mon, 16 Feb 2026 07:01:22 +0900 Subject: [PATCH 5/6] documentation: Add configuration guide and reference Add guide for adding new board configuration and configuration reference. Signed-off-by: TOKITA Hiroshi --- documentation/configuration-guide.md | 312 ++++++++++++++++ documentation/configuration-reference.md | 451 +++++++++++++++++++++++ documentation/variants.md | 18 +- 3 files changed, 776 insertions(+), 5 deletions(-) create mode 100644 documentation/configuration-guide.md create mode 100644 documentation/configuration-reference.md diff --git a/documentation/configuration-guide.md b/documentation/configuration-guide.md new file mode 100644 index 000000000..82c6c210f --- /dev/null +++ b/documentation/configuration-guide.md @@ -0,0 +1,312 @@ +Board Configuration Guide +========================= + +This document defines practical support levels for ArduinoCore-Zephyr and explains how to reach each level. + +The support level definitions +----------------------------- + +### Level 0 - Basic GPIO runtime + +Arduino GPIO APIs are usable with **numeric pins**. +All boards are expected to satisfy this level **once ArduinoCore-Zephyr is enabled**. + +### Level 1 — Blinky-ready + +The Blinky sample works if the board provides a built-in LED. + +### Level 2 — Digital pin definitions are available + +Arduino-style digital pin definitions `D0`, `D1`, … are available. + +### Level 3 — Common bus definitions are available + +Common buses (`Serial`, `Wire`, and `SPI`) are available for use. + +### Level 4 — PWM and ADC are available for use + +Channel provisioning and pin association are provided to make PWM and ADC available. + +### Level 5 — Supports board-specific features + +Board-specific peripherals and features are supported. + + + +Making a configuration for your board +------------------------------------- + +A well-configured board equipped with an Arduino connector provides **Level 3** support without additional configurations. + +**Level 4** support is required to cover most standard Arduino use cases. +Reaching **Level 4 and above** typically requires a board-/variant-specific overlay, typically delivered as snippets. + +This guide targets Level 4, which supports standard Arduino functions. + + +### Configuration sources + +#### Board-provided devicetree (Zephyr source tree) + +A well-configured board DTS typically includes: + +- An Arduino-compatible connector definition, + labeled as `arduino_header` (or other supported connectors) +- Optionally, connector mapping nodes for PWM/ADC pin association and channel derivation (commonly `arduino_pwm` and `arduino_adc`) +- Bus defaults via node labels such as `arduino_serial`, `arduino_i2c`, `arduino_spi` +- A built-in LED via the `led0` alias (recommended) + +These are the “works out of the box” ingredients. + +#### Overlays + +Define nodes/properties under `/zephyr,user` to configure GPIO, ADC, PWM, I2C, and SPI. +Use this when there is no connector definition or when you want to overwrite it. + + +Configuration point by level +---------------------------- + +### Level 1 + +If your board has an onboard LED, you can support it in one of the following ways (**lowest precedence rule first**): + +1. Define the `led0` alias for the LED node. + + You can usually find a definition like this in the board devicetree. + If a `led0` alias exists, no additional configuration is needed. + + ```dts + / { + leds { + compatible = "gpio-leds"; + + led: led { + gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>; + }; + }; + + aliases { + led0 = &led; + }; + }; + ``` + +2. Define `/zephyr,user/builtin-led-gpios` + + Define the `builtin-led-gpios` property to explicitly select the built-in LED GPIO. + + ```dts + / { + zephyr,user { + builtin-led-gpios = <&gpio0 25 0>; + }; + }; + ``` + +3. Manually define `LED_BUILTIN` in the board-specific `variant.h` + + As a last resort, you can define `LED_BUILTIN` directly in variant.h. + + ```c + #define LED_BUILTIN 13 + ``` + + +### Level 2 + +Two ways to reach it (**lowest precedence rule first**): + +1. **Define an Arduino connector** + + If you have an Arduino header definition like the one below, + `gpio-map` can be used to generate Arduino-style digital pin names (`D0`, `D1`, …). + The leftmost value in each map entry is the connector pin identifier (connector-dependent). + + ```dts + / { + arduino_header: connector { + compatible = "arduino-header-r3"; + #gpio-cells = <2>; + gpio-map-mask = <0xffffffff 0xffffffc0>; + gpio-map-pass-thru = <0 0x3f>; + gpio-map = , + /* -- snip -- */ + ; + }; + }; + ``` + +2. **Define `/zephyr,user/digital-pin-gpios`** + + Set the list of GPIOs to use in `/zephyr,user/digital-pin-gpios`. + They will be assigned in the order you set them: `D0`, `D1`, `D2`, ... + + ```dts + / { + zephyr,user { + digital-pin-gpios = <&gpio0 0 0>, + <&gpio0 1 0>, + /* -- snip -- */ + <&gpio1 5 0>; + }; + }; + ``` + +Pin number behavior differs between `/zephyr,user/digital-pin-gpios` and connector-derived (`gpio-map`) configuration. +For detailed rules, examples, and precedence between the two methods, see `documentation/configuration-reference.md`, Section D (`Pin mapping`). + + +### Level 3 + +Two ways to reach it (**lowest precedence rule first**): + +1. **Use board default buses via connector** + + If the board provides node labels such as `arduino_serial`, `arduino_i2c`, and `arduino_spi`, + they will be used as defaults for `Serial`, `Wire`, and `SPI`. + Use the conventional labels `arduino_serial`, `arduino_i2c`, and `arduino_spi`. + + ```dts + arduino_i2c: &iic1 {}; + arduino_spi: &spi1 {}; + arduino_serial: &uart2 {}; + ``` + + +2. **Define `/zephyr,user/serials`, `/zephyr,user/i2cs`, `/zephyr,user/spis`** + + Set the list of UART devices to use in `/zephyr,user/serials`. + This will create Arduino `Serial`, `Serial1`, ... objects. + The same applies to `i2cs` and `spis` for `Wire/Wire1...` and `SPI/SPI1...`. + + ```dts + / { + zephyr,user { + serials = <&uart2>; + i2cs = <&iic1>; + spis = <&spi1>; + }; + }; + ``` + +You can also add settings under `/zephyr,user` to fill in missing items in the devicetree. + + +### Level 4 + +To correctly configure PWM and ADC, two independent items must be addressed: + +1. **Pin association**: mapping PWM/ADC input/output pins to GPIO numbers +2. **Channel provisioning**: PWM and ADC channel configuration + +#### 1) Pin association + +This is similar to configuring GPIO: + +1) **Derive from connector maps** + + Provide connector mappings via the board’s connector definitions (e.g., `pwm-map` and `io-channel-map`) + + ```dts + / { + arduino_pwm: connector-pwm { + compatible = "arduino-header-pwm"; + #pwm-cells = <3>; + pwm-map = , + /* -- snip -- */ + ; + pwm-map-mask = <0xffffffff 0x0 0x0>; + pwm-map-pass-thru = <0x0 0xffffffff 0xffffffff>; + }; + + arduino_adc: analog-connector { + compatible = "arduino,uno-adc"; + #io-channel-cells = <1>; + io-channel-map = , + /* -- snip -- */ + ; + }; + }; + ``` + +2) **Explicit lists under `/zephyr,user`** + + Set GPIO pin lists in `/zephyr,user/pwm-pin-gpios` and `/zephyr,user/adc-pin-gpios`. + + ```dts + / { + zephyr,user { + pwm-pin-gpios = <&gpio0 3 0>, + /* -- snip -- */ + <&gpio0 11 0>; + + adc-pin-gpios = <&gpio0 14 0>, + /* -- snip -- */ + <&gpio0 21 0>; + }; + }; + ``` + + +#### 2) Channel provisioning + +ArduinoCore-Zephyr needs a list of available PWM/ADC channels. +You can provide it in either of the following ways (**lowest precedence rule first**): + +1) **Derive from connector maps** + + If the board defines `arduino_pwm` with `pwm-map` and/or `arduino_adc` with `io-channel-map`, + ArduinoCore-Zephyr can derive the channel lists from those maps. + In that case, the additional `/zephyr,user` properties are not required. + In other words, all you need to do is set the ADC channel settings described below. + +2) **Explicit lists under `/zephyr,user`** + + Set the PWM/ADC channels corresponding to the pins specified by `pwm-pin-gpios` and `adc-pin-gpios`. + - PWM channels: `/zephyr,user/pwms` + - ADC channels: `/zephyr,user/io-channels` + + ```dts + / { + zephyr,user { + pwms = <&pwm1 1 255 PWM_POLARITY_NORMAL>, + /* -- snip -- */ + <&pwm2 3 255 PWM_POLARITY_NORMAL>; + + io-channels = <&adc 2>, + /* -- snip -- */ + <&adc 1>; + }; + }; + ``` + +For ADC, you still need to define per-channel settings under each ADC device node. +See also the examples in `samples/drivers/adc/adc_dt`. + + ```dts + &adc { + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + zephyr,gain = "ADC_GAIN_1_6"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,input-positive = ; /* P0.02 */ + zephyr,resolution = <10>; + }; + /* -- snip -- */ + }; + ``` + + +### Level 5 + +Level 5 supports special features of each board, and the way to achieve this varies by board. + +It may require: +- adding definitions to `variant.h` or overlays, +- adding libraries to support the features, or +- adding features to Zephyr itself when necessary. diff --git a/documentation/configuration-reference.md b/documentation/configuration-reference.md new file mode 100644 index 000000000..1d36af43c --- /dev/null +++ b/documentation/configuration-reference.md @@ -0,0 +1,451 @@ +Board Configuration Reference +============================= + +A. Devicetree node: `/zephyr,user` +---------------------------------- + + +### Pin mapping under `/zephyr,user` + +#### `digital-pin-gpios` +- **Location:** `/zephyr,user` +- **Type:** `phandle-array` (GPIO specifiers) +- **Meaning:** Ordered Arduino digital pin list. +- **Affects:** Defines `D0`, `D1`, … from the list order. +- **Precedence:** higher than connector-derived digital namespace (`gpio-map`). +- **Example** + ```dts + / { + zephyr,user { + digital-pin-gpios = <&gpio0 0 0>, + <&gpio0 1 0>, + /* -- snip -- */ + <&gpio1 5 0>; + }; + }; + ``` + +#### `adc-pin-gpios` +- **Location:** `/zephyr,user` +- **Type:** `phandle-array` (GPIO specifiers) +- **Meaning:** Ordered list of GPIO pins treated as “ADC-capable pins” for Arduino mapping. +- **Affects:** ADC pin association (Arduino-visible analog pins ↔ ADC channels). +- **Precedence:** overrides connector `io-channel-map` (if present). +- **Notes:** + - Association only; does not provision ADC channels (see `io-channels`) + - The list index defines the Arduino analog pin index (e.g., index 0 corresponds to A0) + and associates with the corresponding channel entry by index. +- **Example** + ```dts + / { + zephyr,user { + adc-pin-gpios = <&gpio0 14 0>, + /* -- snip -- */ + <&gpio0 21 0>; + }; + }; + ``` + +#### `pwm-pin-gpios` +- **Location:** `/zephyr,user` +- **Type:** `phandle-array` (GPIO specifiers) +- **Meaning:** Ordered list of GPIO pins treated as “PWM-capable pins” for Arduino mapping. +- **Affects:** PWM pin association (Arduino-visible PWM pins ↔ PWM channels). +- **Precedence:** overrides connector `pwm-map` (if present). +- **Notes:** + - Association only; does not provision PWM channels (see `pwms`) + - The list index defines the Arduino PWM-capable pin index and associates with the + corresponding channel entry by index. +- **Example** + ```dts + / { + zephyr,user { + pwm-pin-gpios = <&gpio0 3 0>, + /* -- snip -- */ + <&gpio0 11 0>; + }; + }; + ``` + +#### `builtin-led-gpios` +- **Location:** `/zephyr,user` +- **Type:** `phandle-array` (GPIO specifiers) +- **Meaning:** Explicit built-in LED GPIO. +- **Affects:** Derivation of `LED_BUILTIN` when `variant.h` does not override it. +- **Precedence:** higher than board `led0` alias, lower than `variant.h` `LED_BUILTIN`. +- **Notes:** + - If `LED_BUILTIN` is defined in `variant.h`, this property is ignored for that purpose. +- **Example** + ```dts + / { + zephyr,user { + builtin-led-gpios = <&gpio0 25 0>; + }; + }; + ``` + + +### Bus node lists + +#### `serials` +- **Location:** `/zephyr,user` +- **Type:** phandle list (UART devices) +- **Meaning:** List of UART devices backing Arduino Serial instances. +- **Affects:** `Serial`, `Serial1`, … +- **Precedence:** overrides board default bus label (commonly `arduino_serial`). +- **Example** + ```dts + / { + zephyr,user { + serials = <&uart0>, <&uart1>; + }; + }; + ``` + +#### `i2cs` +- **Location:** `/zephyr,user` +- **Type:** phandle list (I2C controller devices) +- **Meaning:** List of I2C devices backing Arduino Wire instances. +- **Affects:** `Wire`, `Wire1`, … +- **Precedence:** overrides board default bus label (commonly `arduino_i2c`). +- **Example** + ```dts + / { + zephyr,user { + i2cs = <&i2c0>, <&i2c1>; + }; + }; + ``` + +#### `spis` +- **Location:** `/zephyr,user` +- **Type:** phandle list (SPI controller devices) +- **Meaning:** List of SPI devices backing Arduino SPI instances. +- **Affects:** `SPI`, `SPI1`, … +- **Precedence:** overrides board default bus label (commonly `arduino_spi`). +- **Example** + ```dts + / { + zephyr,user { + spis = <&spi0>, <&spi1>; + }; + }; + ``` + + +### Channel provisioning + +#### `io-channels` +- **Location:** `/zephyr,user` +- **Type:** `io-channels` phandle-array specifiers +- **Meaning:** List of ADC controller/channel specifiers used by ArduinoCore-Zephyr. +- **Affects:** ADC channel provisioning (ADC availability and channel list). +- **Precedence:** higher than connector-derived ADC channel list (from `io-channel-map`). +- **Notes:** + - Without provisioning via `/zephyr,user/io-channels`, ADC channels may still be available + if they are derived from connector maps (see `io-channel-map`). + - **ADC still requires per-channel configuration under the ADC device node** + (gain/reference/acquisition time/etc.), regardless of how the channel list is obtained. +- **Example:** + ```dts + / { + zephyr,user { + io-channels = <&adc 2>, + /* -- snip -- */ + <&adc 1>; + }; + }; + ``` + + +#### `pwms` +- **Location:** `/zephyr,user` +- **Type:** PWM phandle-array specifiers +- **Meaning:** List of PWM controller/channel specifiers used by ArduinoCore-Zephyr. +- **Affects:** PWM channel provisioning (PWM availability and channel list) and per-channel + parameters (e.g., period/polarity). +- **Precedence:** higher than connector-derived PWM channel list (from `pwm-map`) and overrides + the core’s defaults. +- **Notes:** + - Without provisioning via `/zephyr,user/pwms`, PWM channels may still be available if they are + derived from connector maps (see `pwm-map`). +- **Example:** + ```dts + / { + zephyr,user { + pwms = <&pwm1 1 255 PWM_POLARITY_NORMAL>, + /* -- snip -- */ + <&pwm2 3 255 PWM_POLARITY_NORMAL>; + }; + }; + ``` + + +B. Board devicetree constructs (board-provided defaults) +-------------------------------------------------------- + +### Node label: `arduino_header` (Arduino connector node) + +- **Location:** board devicetree node label +- **Type:** label pointing to a connector node (`gpio-map` container) +- **Meaning:** Connector node that models the Arduino header pinout. +- **Affects:** Defines `D0`, `D1`, … when `/zephyr,user/digital-pin-gpios` is absent. +- **Precedence:** Used only if `/zephyr,user/digital-pin-gpios` is absent. +- **Notes:** + - `gpio-map`: left = connector pin id, right = target GPIO. + - Currently only `arduino_header` (compatible = "arduino-header-r3") is supported. +- **Example:** + ```dts + / { + arduino_header: connector { + compatible = "arduino-header-r3"; + #gpio-cells = <2>; + gpio-map-mask = <0xffffffff 0xffffffc0>; + gpio-map-pass-thru = <0 0x3f>; + gpio-map = , + /* -- snip -- */ + ; + }; + }; + ``` + + +### Node label: `arduino_adc` (ADC connector node) + +- **Location:** board devicetree node label +- **Type:** label pointing to a connector node (`io-channel-map` container) +- **Meaning:** Connector node that models ADC-capable Arduino pins. +- **Affects:** Provides ADC pin association and/or ADC channel provisioning via `io-channel-map` when `/zephyr,user/adc-pin-gpios` and `/zephyr,user/io-channels` are absent. +- **Precedence:** + - Lower than `/zephyr,user/adc-pin-gpios` (association) + - Lower than `/zephyr,user/io-channels` (provisioning) +- **Notes:** + - `io-channel-map`: left = connector pin id, right = target ADC controller/channel. + - ADC still requires per-channel configuration under the ADC device node, + regardless of how the channel list is obtained. +- **Example:** + ```dts + / { + arduino_adc: analog-connector { + compatible = "arduino,uno-adc"; + #io-channel-cells = <1>; + io-channel-map = , + /* -- snip -- */ + ; + }; + }; + ``` + + +### Node label: `arduino_pwm` (PWM connector node) + +- **Location:** board devicetree node label +- **Type:** label pointing to a connector node (`pwm-map` container) +- **Meaning:** Connector node that models PWM-capable Arduino pins. +- **Affects:** Provides PWM pin association and/or PWM channel provisioning via `pwm-map` when `/zephyr,user/pwm-pin-gpios` and `/zephyr,user/pwms` are absent. +- **Precedence:** + - Lower than `/zephyr,user/pwm-pin-gpios` (association) + - Lower than `/zephyr,user/pwms` (provisioning/parameters) +- **Notes:** `pwm-map`: left = connector pin id, right = target PWM controller/channel. +- **Example:** + ```dts + / { + arduino_pwm: connector-pwm { + compatible = "arduino-header-pwm"; + #pwm-cells = <3>; + pwm-map = , + /* -- snip -- */ + ; + pwm-map-mask = <0xffffffff 0x0 0x0>; + pwm-map-pass-thru = <0x0 0xffffffff 0xffffffff>; + }; + }; + ``` + + +### Node label: `arduino_serial` (default Serial bus) + +- **Location:** board devicetree node label +- **Type:** label pointing to a UART controller node +- **Meaning:** Board default UART device for Arduino `Serial`. +- **Affects:** Selects the backend device for `Serial`. +- **Precedence:** Used only if `/zephyr,user/serials` is absent. +- **Notes:** This is a convention-based default; use `/zephyr,user/serials` to override + or provide multiple UARTs (`Serial1`, …). +- **Example:** + ```dts + arduino_serial: &uart2 {}; + ``` + + +### Node label: `arduino_i2c` (default Wire bus) + +- **Location:** board devicetree node label +- **Type:** label pointing to an I2C controller node +- **Meaning:** Board default I2C controller for Arduino `Wire`. +- **Affects:** Selects the backend device for `Wire`. +- **Precedence:** Used only if `/zephyr,user/i2cs` is absent. +- **Notes:** This is a convention-based default; use `/zephyr,user/i2cs` to override + or provide multiple buses (`Wire1`, …). +- **Example:** + ```dts + arduino_i2c: &iic1 {}; + ``` + + +### Node label: `arduino_spi` (default SPI bus) + +- **Location:** board devicetree node label +- **Type:** label pointing to an SPI controller node +- **Meaning:** Board default SPI controller for Arduino `SPI`. +- **Affects:** Selects the backend device for `SPI`. +- **Precedence:** Used only if `/zephyr,user/spis` is absent. +- **Notes:** This is a convention-based default; use `/zephyr,user/spis` to override + or provide multiple SPI instances (`SPI1`, …). +- **Example:** + ```dts + arduino_spi: &spi1 {}; + ``` + + + +### Alias: `led0` +- **Location:** devicetree `/aliases` +- **Type:** alias pointing to an LED node +- **Meaning:** Board default built-in LED (Zephyr convention). +- **Affects:** Used to derive `LED_BUILTIN` if neither `variant.h` `LED_BUILTIN` nor + `/zephyr,user/builtin-led-gpios` is provided. +- **Precedence:** lowest in the built-in LED chain. +- **Example:** + ```dts + / { + leds { + compatible = "gpio-leds"; + + led: led { + gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>; + }; + }; + + aliases { + led0 = &led; + }; + }; + ``` + +C. `variant.h` symbols (highest-precedence C/C++ overrides) +------------------------------------------------------------ + +### `LED_BUILTIN` +- **Location:** board-specific `variant.h` +- **Type:** C/C++ macro (integer Arduino pin number) +- **Meaning:** Explicit Arduino-visible LED pin number. +- **Affects:** `LED_BUILTIN` behavior regardless of devicetree LED selection. +- **Precedence:** highest for built-in LED pin numbering. +- **Example:** + ```c + #define LED_BUILTIN 13 + ``` + +D. Pin mapping +-------------- + +### Terminology + +- **Pin symbol:** Arduino-visible symbolic name such as `D0`, `D1`, `A0`, and `A1`. +- **Pin number:** Numeric argument used by Arduino APIs such as `pinMode()` and `digitalWrite()`. +- **Port/pin:** Physical GPIO tuple represented as `(GPIO controller, local pin)` (for example `gpio1/pin3`). +- **Global GPIO numbering:** Linear numeric space computed from GPIO controller offsets plus local pin numbers. + +### Using `/zephyr,user/digital-pin-gpios` + +`D` symbols are assigned by list order. Pin number uses Arduino digital index (`D1=1`, `D2=2`, ...). + +```dts +/ { + zephyr,user { + digital-pin-gpios = <&gpio0 10 0>, /* D0 */ + <&gpio0 11 0>, /* D1 */ + <&gpio0 12 0>; /* D2 */ + }; +}; +``` + +| Pin symbol | Pin number | Port/pin | +| ---------- | ---------- | ------------- | +| `D0` | `0` | `gpio0/pin10` | +| `D1` | `1` | `gpio0/pin11` | +| `D2` | `2` | `gpio0/pin12` | + +### Using `arduino_header` connector definition (`gpio-map`) + +`D` symbols follow connector pin names. Pin number uses global GPIO numbering. + +Global GPIO numbering is a single linear pin-number space across all GPIO controllers. +For a mapped GPIO `(port, pin)`, the value is calculated as: + +`global_pin = port_offset + local_pin` + +- `local_pin`: pin number inside that GPIO controller +- `port_offset`: sum of `ngpios` of controllers that come before that controller + in the devicetree order used by the core + +With one controller, Pin number is the same as the local GPIO pin. +With multiple controllers, pins on later controllers include the offset. + +Example calculation in the fragment below: +- `D1 -> gpio0/pin5`: `0 + 5 = 5` +- `D2 -> gpio1/pin1`: `16 + 1 = 17` (because `gpio0` has `ngpios = 16`) + +```dts +/ { + gpio0: gpio@0 { + gpio-controller; + ngpios = <16>; + }; + + gpio1: gpio@1 { + gpio-controller; + ngpios = <32>; + }; + + arduino_header: connector { + compatible = "arduino-header-r3"; + #gpio-cells = <2>; + gpio-map-mask = <0xffffffff 0xffffffc0>; + gpio-map-pass-thru = <0 0x3f>; + gpio-map = , /* D1 */ + ; /* D2 */ + }; +}; +``` + +| Pin symbol | Pin number | Port/pin | +| ---------- | ---------- | ------------- | +| `D1` | `5` | `gpio0/pin5` | +| `D2` | `17` | `gpio1/pin1` | + + +E. Derived behavior switches +--------------------------- + +### Dx availability (`D0`, `D1`, …) +- **Defined from** `/zephyr,user/digital-pin-gpios` when present +- **Otherwise defined from** connector `gpio-map` when present +- **Otherwise** not defined (numeric global pins may still work) + +### ADC channel list source (provisioning) +- **Primary:** `/zephyr,user/io-channels` +- **Fallback:** derive from connector `io-channel-map` under `arduino_adc` + +### ADC pin association source +- **Primary:** `/zephyr,user/adc-pin-gpios` +- **Fallback:** connector `io-channel-map` under `arduino_adc` + +### PWM channel list source (provisioning) +- **Primary:** `/zephyr,user/pwms` +- **Fallback:** derive from connector `pwm-map` under `arduino_pwm` + +### PWM pin association source +- **Primary:** `/zephyr,user/pwm-pin-gpios` +- **Fallback:** connector `pwm-map` under `arduino_pwm` diff --git a/documentation/variants.md b/documentation/variants.md index 24ffd151c..0b9f25ffe 100644 --- a/documentation/variants.md +++ b/documentation/variants.md @@ -46,6 +46,8 @@ It is possible to apply the overlay outside of the `variants` folder. In that ca The Arduino API requires pin mapping definitions to use Arduino-style pin numbers (pin numbers printed on the board, not GPIO numbers). +When `digital-pin-gpios` is not defined and pin mapping is derived from connector `gpio-map`, +Arduino API `Pin number` uses global GPIO numbering (see `configuration-reference.md`, Section D). The pin-mapping node is under the `zephyr,user` node of DTS. `digital-pin-gpios` defines digital input/output pins that is D0, D1, .., `adc-pin-gpios` defines analog input pins that is A0, A1, ... . @@ -59,7 +61,7 @@ You can also use the Arduino header node definition here. When an Arduino header exists in a board's in-tree DTS file it can easily be used to create the necessary overlay file. Assign the relevant mapping using the Arduino header label (usually either `&arduino_header` or `&arduino_nano_header` -and the `gpio_map` number. The second number is used to add GPIO flags and may +and the connector pin identifier from `gpio-map`. The second number is used to add GPIO flags and may safely be left as zero. For example, creating an overlay file for the Nordic nRF52840 Development Kit @@ -91,8 +93,8 @@ It instantiate the `Serial` with the UART device that contained in the node. Also instantiate as `Serial1`, `Serial2`, .. `SerialN` with the devices that is after the second in the case of the array contains plural devices. -If the `serials` node is not defined, Use the node labeled `arduino-serial`. -Boards with Arduino-shield style connectors usually label `arduino-serial` for +If the `serials` node is not defined, use the node labeled `arduino_serial`. +Boards with Arduino-shield style connectors usually label `arduino_serial` for UART port exposed in header or frequently used UART port. If even 'arduino_serial' does not define, it uses the stub implementation @@ -115,8 +117,8 @@ It instantiate the `Wire` with the i2c device that contained in the node. Also instantiate as `Wire1`, `Wire2`, .. `WireN` with the devices that is after the second in the case of the array contains plural devices. -If the `i2cs` node is not defined, Use the node labeled `arduino-i2c`. -Boards with Arduino-shield style connectors usually label `arduino-i2c` +If the `i2cs` node is not defined, use the node labeled `arduino_i2c`. +Boards with Arduino-shield style connectors usually label `arduino_i2c` to i2c exposed in the connector. The following example instantiates `Wire` and `Wire2` with each `i2c0` and `i2c1`. @@ -146,6 +148,12 @@ to define `LED_BUILTIN`. The `LED_BUILTIN` does not define here if it has not found both nodes or defined `LED_BUILTIN` already. +When `/zephyr,user/digital-pin-gpios` is absent, the `led0` alias sets +`LED_BUILTIN` to the **global GPIO-based Pin number**, which matches the +numeric pin interpretation in that mode. +See `configuration-reference.md`, Section B (`Alias: led0`) and +Section D (`Pin mapping` / `Global GPIO numbering`). + For example, in the case of the 13th digital pins connected to the onboard LED, define `builtin-led-gpios` as follows. From 33935c2e94d2d1097d53ec16331699c535841a3e Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Mon, 9 Feb 2026 18:56:18 +0900 Subject: [PATCH 6/6] variant: Adds support for Arduino Uno R4. A new configuration method has been adopted, allowing support to be added with less configuration than before. Signed-off-by: TOKITA Hiroshi --- .../arduino_uno_r4_r7fa4m1ab3cfm.overlay | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 variants/arduino_uno_r4_r7fa4m1ab3cfm/arduino_uno_r4_r7fa4m1ab3cfm.overlay diff --git a/variants/arduino_uno_r4_r7fa4m1ab3cfm/arduino_uno_r4_r7fa4m1ab3cfm.overlay b/variants/arduino_uno_r4_r7fa4m1ab3cfm/arduino_uno_r4_r7fa4m1ab3cfm.overlay new file mode 100644 index 000000000..c420c49b3 --- /dev/null +++ b/variants/arduino_uno_r4_r7fa4m1ab3cfm/arduino_uno_r4_r7fa4m1ab3cfm.overlay @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2026 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +&adc0 { + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0x0>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_VDD_1"; + zephyr,resolution = <12>; + zephyr,acquisition-time = ; + zephyr,vref-mv = <5000>; + }; + + channel@1 { + reg = <0x1>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_VDD_1"; + zephyr,resolution = <12>; + zephyr,acquisition-time = ; + zephyr,vref-mv = <5000>; + }; + + channel@2 { + reg = <0x2>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_VDD_1"; + zephyr,resolution = <12>; + zephyr,acquisition-time = ; + zephyr,vref-mv = <5000>; + }; + + channel@9 { + reg = <0x9>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_VDD_1"; + zephyr,resolution = <12>; + zephyr,acquisition-time = ; + zephyr,vref-mv = <5000>; + }; + + channel@15 { + reg = <0x15>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_VDD_1"; + zephyr,resolution = <12>; + zephyr,acquisition-time = ; + zephyr,vref-mv = <5000>; + }; + + channel@16 { + reg = <0x16>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_VDD_1"; + zephyr,resolution = <12>; + zephyr,acquisition-time = ; + zephyr,vref-mv = <5000>; + }; +};