From abef7881f83ad480dda2b10e4919512ec2d73aa8 Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Fri, 30 Jan 2026 17:55:14 +0900 Subject: [PATCH 1/3] 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 | 101 +++++++++++++++++++++++---------- 1 file changed, 70 insertions(+), 31 deletions(-) diff --git a/cores/arduino/zephyrCommon.cpp b/cores/arduino/zephyrCommon.cpp index 6b011c07..10c1201e 100644 --- a/cores/arduino/zephyrCommon.cpp +++ b/cores/arduino/zephyrCommon.cpp @@ -9,7 +9,7 @@ #include -static const struct gpio_dt_spec arduino_pins[] = {DT_FOREACH_PROP_ELEM_SEP( +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, (, ))}; namespace { @@ -58,6 +58,18 @@ constexpr const size_t is_first_appearance(const size_t &idx, const size_t &at, 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)) #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)), \ @@ -103,10 +115,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; } } @@ -192,26 +204,36 @@ void yield(void) { */ void pinMode(pin_size_t pinNumber, PinMode pinMode) { if (pinMode == INPUT) { // input mode - gpio_pin_configure_dt(&arduino_pins[pinNumber], + 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], + 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], + 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], + 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 @@ -264,18 +286,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--; @@ -289,6 +312,7 @@ void tone(pin_size_t pinNumber, unsigned int frequency, k_spinlock_key_t key; struct pin_timer *pt; k_timeout_t timeout; + const struct device *port; pt = find_pin_timer(pinNumber, false); @@ -296,6 +320,8 @@ void tone(pin_size_t pinNumber, unsigned int frequency, return; } + port = local_gpio_port(pt->pin); + pinMode(pinNumber, OUTPUT); k_timer_stop(&pt->timer); @@ -304,7 +330,9 @@ void tone(pin_size_t pinNumber, unsigned int frequency, 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; } @@ -321,13 +349,16 @@ void tone(pin_size_t pinNumber, unsigned int frequency, 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); @@ -335,12 +366,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) { @@ -442,6 +477,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); struct gpio_port_callback *pcb; gpio_flags_t intmode = 0; @@ -463,16 +499,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) @@ -500,29 +538,30 @@ 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; } @@ -536,18 +575,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; } } @@ -567,7 +606,7 @@ void noInterrupts(void) { int digitalPinToInterrupt(pin_size_t pin) { struct gpio_port_callback *pcb = - find_gpio_port_callback(arduino_pins[pin].port); + find_gpio_port_callback(local_gpio_port(pin)); return (pcb) ? pin : -1; } From 2cf6bc36e750e51d4e93c5c1f74f377e9ee8ca6c Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Fri, 30 Jan 2026 13:04:17 +0900 Subject: [PATCH 2/3] 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 | 156 ++++++++- cores/arduino/zephyrCommon.cpp | 246 +++++++++++++-- documentation/configuration-guide.md | 312 ++++++++++++++++++ documentation/configuration-reference.md | 382 +++++++++++++++++++++++ documentation/variants.md | 4 + 5 files changed, 1064 insertions(+), 36 deletions(-) create mode 100644 documentation/configuration-guide.md create mode 100644 documentation/configuration-reference.md diff --git a/cores/arduino/Arduino.h b/cores/arduino/Arduino.h index 9657a712..038ec5b3 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) + #define DIGITAL_PIN_EXISTS(n, p, i, dev, num) \ (((dev == DT_REG_ADDR(DT_PHANDLE_BY_IDX(n, p, i))) && \ (num == DT_PHA_BY_IDX(n, p, i, pin))) \ @@ -74,12 +77,149 @@ #define DN_ENUMS(n, p, i) D##i = i +#else + +#if DT_NODE_EXISTS(DT_NODELABEL(arduino_header)) +#if DT_NODE_HAS_COMPAT(DT_NODELABEL(arduino_header), arduino_header_r3) +#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 + +#else +#error "Only arduino-header-r3 connector is supported" +#endif +#endif + +#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 + +#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_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_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),), ()) + +#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 + /* * expand as * enum digitalPins { D0, D1, ... LED... NUM_OF_DIGITAL_PINS }; */ enum digitalPins { +#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, (, )), +#elif defined(ZARD_CONNECTOR) + DT_FOREACH_MAP_ENTRY(DT_NODELABEL(ZARD_CONNECTOR), gpio_map, ZARD_CONN_DN_ENUMS) +#endif NUM_OF_DIGITAL_PINS }; @@ -88,8 +228,20 @@ 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)), -enum analogPins { DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), - adc_pin_gpios, AN_ENUMS) }; +#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 { +#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) +#else + DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), adc_pin_gpios, ZARD_AN_ENUM_GLOBAL) +#endif +#elif defined(ZARD_ADC_CONNECTOR) + DT_FOREACH_MAP_ENTRY(DT_NODELABEL(ZARD_CONNECTOR), gpio_map, ZARD_CONN_AN_ENUMS) +#endif + NUM_OF_ANALOG_PINS +}; #endif diff --git a/cores/arduino/zephyrCommon.cpp b/cores/arduino/zephyrCommon.cpp index 10c1201e..eeb2d5d8 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,8 +10,24 @@ #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,))), \ + ()) + +#define GET_GPIO_NGPIOS(node_id) \ + COND_CODE_1(DT_NODE_HAS_PROP(node_id, gpio_controller), \ + (DT_PROP_OR(node_id, ngpios, 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 namespace { @@ -58,18 +75,87 @@ constexpr const size_t is_first_appearance(const size_t &idx, const size_t &at, 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 + 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)), \ @@ -81,6 +167,14 @@ const int port_num = #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 +#else +const int port_num = ARRAY_SIZE(gpio_ports); +const int max_ngpios = max_in_list(DT_FOREACH_NODE(GET_GPIO_NGPIOS) 0); +#endif /* * GPIO callback implementation @@ -100,6 +194,10 @@ struct gpio_port_callback { 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]; @@ -115,10 +213,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; } } @@ -140,13 +240,42 @@ void handleGpioCallback(const struct device *port, struct gpio_callback *cb, uin 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)), - -const struct pwm_dt_spec arduino_pwm[] = - { DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), pwms, PWM_DT_SPEC) }; +#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[] = { +#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) }; +const pin_size_t arduino_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; ipin = pin_size_t(-1); + pt->pin = invalid_pin_number; } else { if (port) { gpio_pin_toggle(port, local_gpio_pin(pt->pin)); @@ -320,14 +493,14 @@ void tone(pin_size_t pinNumber, unsigned int frequency, 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) { @@ -370,7 +543,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) { @@ -478,6 +651,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; @@ -499,18 +673,20 @@ void attachInterrupt(pin_size_t pinNumber, voidFuncPtr callback, PinStatus pinSt return; } + if (port == nullptr || pin == pin_size_t(-1)) { + 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) @@ -539,11 +715,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 || pin == pin_size_t(-1) || !device_is_ready(port)) { return 0; } @@ -575,18 +751,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; } } diff --git a/documentation/configuration-guide.md b/documentation/configuration-guide.md new file mode 100644 index 00000000..62dfc437 --- /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** (global GPIO numbering). +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>; + }; + }; + ``` + +Each approach results in different numeric pin-numbering rules. +See the reference document for details. + + +### 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 00000000..517b838d --- /dev/null +++ b/documentation/configuration-reference.md @@ -0,0 +1,382 @@ +Board Configuration Reference +============================= + +A. Devicetree node: `/zephyr,user` +---------------------------------- + +### Pin mapping + +#### `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. Derived behavior switches + +### Numeric pin interpretation +- **Switch condition:** presence of `/zephyr,user/digital-pin-gpios` +- **If present:** numeric `pinNumber` = Arduino-index into `digital-pin-gpios` +- **If absent:** numeric `pinNumber` = global GPIO number +- **Notes:** + - ArduinoCore-Zephyr defines a global GPIO numbering over GPIO controllers: + - The global gpio number assigns sequential numbers to GPIO pins in the device tree. + For example, if you have gpio0, gpio1 which has 16 ports, + then pin0 of gpio0 will be 0, pin1 will be 1, ... + pin0 of gpio1 will be 16, pin1 will be 17, ... etc. + - Only GPIO controllers with `status = "okay"` are counted. + - The order of GPIOs depends on their appearance in the device tree. + +### 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 24ffd151..85df0d71 100644 --- a/documentation/variants.md +++ b/documentation/variants.md @@ -146,6 +146,10 @@ 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 number**, which matches the numeric pin +interpretation in that mode. + For example, in the case of the 13th digital pins connected to the onboard LED, define `builtin-led-gpios` as follows. From 421542f10e34b4e2426d23d14e3af2ce58e34923 Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Mon, 9 Feb 2026 18:56:18 +0900 Subject: [PATCH 3/3] 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 00000000..c420c49b --- /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>; + }; +};