From 6806d96d4ca01b26063589b0374ea3fe19c21c9e Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Mon, 17 Nov 2025 17:19:29 +0100 Subject: [PATCH 1/4] core: add dynamic peripheral init for pinmux switching Allow Arduino APIs to switch a pin between GPIO and peripheral functions by re-running the selected device init path when the requested owner changes. This is an interim pinmux handoff mechanism for boards where sketches may move a pin back and forth between serial/GPIO/PWM-style use. Co-Authored-by: Martino Facchin Co-Authored-by: pennam Signed-off-by: TOKITA Hiroshi --- cores/arduino/zephyrCommon.cpp | 20 ++++++++++++++++++++ cores/arduino/zephyrInternal.h | 1 + cores/arduino/zephyrSerial.cpp | 2 ++ cores/arduino/zephyrSerial.h | 5 +++++ libraries/SPI/SPI.cpp | 6 ++++++ libraries/Wire/Wire.cpp | 6 ++++++ 6 files changed, 40 insertions(+) diff --git a/cores/arduino/zephyrCommon.cpp b/cores/arduino/zephyrCommon.cpp index 8e8aa5052..bdb7dafc1 100644 --- a/cores/arduino/zephyrCommon.cpp +++ b/cores/arduino/zephyrCommon.cpp @@ -9,6 +9,19 @@ #include +// create an array of arduino_pins with functions to reinitialize pins if needed +static const struct device *pinmux_array[DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios)] = { + nullptr}; + +void _reinit_peripheral_if_needed(pin_size_t pin, const struct device *dev) { + if (pinmux_array[pin] != dev) { + pinmux_array[pin] = dev; + if (dev != NULL) { + dev->ops.init(dev); + } + } +} + static const struct gpio_dt_spec arduino_pins[] = { DT_FOREACH_PROP_ELEM_SEP( DT_PATH(zephyr_user), digital_pin_gpios, GPIO_DT_SPEC_GET_BY_IDX, (, ))}; @@ -238,6 +251,7 @@ int digitalPinToPinIndex(pin_size_t pinNumber) { void pinMode(pin_size_t pinNumber, PinMode pinMode) { RETURN_ON_INVALID_PIN(pinNumber); + _reinit_peripheral_if_needed(pinNumber, NULL); if (pinMode == INPUT) { // input mode gpio_pin_configure_dt(&arduino_pins[pinNumber], GPIO_INPUT | GPIO_ACTIVE_HIGH); } else if (pinMode == INPUT_PULLUP) { // input with internal pull-up @@ -478,6 +492,7 @@ void analogWrite(pin_size_t pinNumber, int value) { return; } + _reinit_peripheral_if_needed(pinNumber, arduino_pwm[idx].dev); value = CLAMP(value, 0, maxInput); const uint32_t pulse = map64(value, 0, maxInput, 0, arduino_pwm[idx].period); @@ -506,6 +521,9 @@ void analogWrite(enum dacPins dacName, int value) { return; } + // TODO: add reverse map to find pin name from DAC* define + // In the meantime, consider A0 == DAC0 + _reinit_peripheral_if_needed((pin_size_t)(dacName + A0), dac_dev); ret = dac_channel_setup(dac_dev, &dac_ch_cfg[dacName]); if (ret != 0) { return; @@ -568,6 +586,8 @@ int analogRead(pin_size_t pinNumber) { return -ENOTSUP; } + _reinit_peripheral_if_needed(pinNumber, arduino_adc[idx].dev); + err = adc_channel_setup(arduino_adc[idx].dev, &arduino_adc[idx].channel_cfg); if (err < 0) { return err; diff --git a/cores/arduino/zephyrInternal.h b/cores/arduino/zephyrInternal.h index b07e1bd9c..e3938e603 100644 --- a/cores/arduino/zephyrInternal.h +++ b/cores/arduino/zephyrInternal.h @@ -14,6 +14,7 @@ extern "C" { void enableInterrupt(pin_size_t); void disableInterrupt(pin_size_t); +void _reinit_peripheral_if_needed(pin_size_t pin, const struct device *dev); #ifdef __cplusplus } // extern "C" diff --git a/cores/arduino/zephyrSerial.cpp b/cores/arduino/zephyrSerial.cpp index 104bed73b..0a9b2e6f2 100644 --- a/cores/arduino/zephyrSerial.cpp +++ b/cores/arduino/zephyrSerial.cpp @@ -58,6 +58,8 @@ void arduino::ZephyrSerial::begin(unsigned long baud, uint16_t conf) { .flow_ctrl = UART_CFG_FLOW_CTRL_NONE, }; + uart->ops.init(uart); + uart_configure(uart, &config); uart_irq_callback_user_data_set(uart, arduino::ZephyrSerial::IrqDispatch, this); uart_irq_rx_enable(uart); diff --git a/cores/arduino/zephyrSerial.h b/cores/arduino/zephyrSerial.h index fd1e3c1a1..57cc44ac6 100644 --- a/cores/arduino/zephyrSerial.h +++ b/cores/arduino/zephyrSerial.h @@ -76,6 +76,11 @@ class ZephyrSerial : public HardwareSerial { } void end() { +#ifdef CONFIG_DEVICE_DEINIT_SUPPORT + if (uart->ops.deinit) { + uart->ops.deinit(uart); + } +#endif } size_t write(const uint8_t *buffer, size_t size); diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index 3f4738b50..ab355142f 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -118,9 +118,15 @@ void arduino::ZephyrSPI::detachInterrupt() { } void arduino::ZephyrSPI::begin() { + spi_dev->ops.init(spi_dev); } void arduino::ZephyrSPI::end() { +#ifdef CONFIG_DEVICE_DEINIT_SUPPORT + if (spi_dev->ops.deinit) { + spi_dev->ops.deinit(spi_dev); + } +#endif } #if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), spis) diff --git a/libraries/Wire/Wire.cpp b/libraries/Wire/Wire.cpp index f53780f11..cb572acd4 100644 --- a/libraries/Wire/Wire.cpp +++ b/libraries/Wire/Wire.cpp @@ -15,12 +15,18 @@ arduino::ZephyrI2C::ZephyrI2C(const struct device *i2c) : i2c_dev(i2c) { void arduino::ZephyrI2C::begin() { ring_buf_init(&rxRingBuffer.rb, sizeof(rxRingBuffer.buffer), rxRingBuffer.buffer); + i2c_dev->ops.init(i2c_dev); } void arduino::ZephyrI2C::begin(uint8_t slaveAddr) { } void arduino::ZephyrI2C::end() { +#ifdef CONFIG_DEVICE_DEINIT_SUPPORT + if (i2c_dev->ops.deinit) { + i2c_dev->ops.deinit(i2c_dev); + } +#endif } void arduino::ZephyrI2C::setClock(uint32_t freq) { From e4ba9af5d16c3c6d5fcc6452f81faa2cce51c526 Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Sat, 18 Apr 2026 14:57:08 +0900 Subject: [PATCH 2/4] cores: arduino: zephyrCommon.cpp: fix struct initialization warnings Replace {0} with {} for port_callback, set dac_channel_cfg::internal explicitly, and expand the adc_sequence initialization to make default values explicit and avoid C++ initializer warnings. Signed-off-by: TOKITA Hiroshi --- cores/arduino/zephyrCommon.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/cores/arduino/zephyrCommon.cpp b/cores/arduino/zephyrCommon.cpp index bdb7dafc1..30df01304 100644 --- a/cores/arduino/zephyrCommon.cpp +++ b/cores/arduino/zephyrCommon.cpp @@ -106,7 +106,7 @@ struct gpio_port_callback { struct arduino_callback handlers[max_ngpios]; gpio_port_pins_t pins; const struct device *dev; -} port_callback[port_num] = {0}; +} port_callback[port_num] = {}; struct gpio_port_callback *find_gpio_port_callback(const struct device *dev) { for (size_t i = 0; i < ARRAY_SIZE(port_callback); i++) { @@ -210,6 +210,7 @@ static const struct device *const dac_dev = DEVICE_DT_GET(DAC_NODE); .channel_id = DT_PROP_BY_IDX(n, p, i), \ .resolution = DAC_RESOLUTION, \ .buffered = true, \ + .internal = false, \ }, #if DT_PROP_LEN_OR(DT_PATH(zephyr_user), dac_channels, 0) > 0 @@ -571,7 +572,16 @@ int analogReadResolution() { int analogRead(pin_size_t pinNumber) { int err; uint16_t buf; - struct adc_sequence seq = {.buffer = &buf, .buffer_size = sizeof(buf)}; + struct adc_sequence seq = {.options = nullptr, + .channels = 0, + .buffer = &buf, + .buffer_size = sizeof(buf), +#if defined(CONFIG_ADC_SEQUENCE_PRIORITY) + .priority = 0, +#endif + .resolution = 0, + .oversampling = 0, + .calibrate = false}; size_t idx = analog_pin_index(pinNumber); if (idx >= ARRAY_SIZE(arduino_adc)) { From 145445832a8c1c2fd7c5c368ecbaf4ae70d6e921 Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Sat, 18 Apr 2026 08:27:28 +0900 Subject: [PATCH 3/4] cores: arduino: Extract definitions used for compile-time calculations Move constexpr-specified constants and functions used for compile-time calculations from zephyrCommon.cpp to wiring_private.h. Signed-off-by: TOKITA Hiroshi --- cores/arduino/wiring_private.h | 81 ++++++++++++++++++++++++++++++++++ cores/arduino/zephyrCommon.cpp | 63 +------------------------- 2 files changed, 82 insertions(+), 62 deletions(-) create mode 100644 cores/arduino/wiring_private.h diff --git a/cores/arduino/wiring_private.h b/cores/arduino/wiring_private.h new file mode 100644 index 000000000..04b95c393 --- /dev/null +++ b/cores/arduino/wiring_private.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) Arduino s.r.l. and/or its affiliated companies + * Copyright (c) 2026 KurtE + * Copyright (c) 2026 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus + +namespace zephyr { +namespace arduino { + +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, (, ))}; + +/* + * Calculate GPIO ports/pins number statically from devicetree configuration + */ + +template constexpr N sum_of_list(const N sum, const Head &head) { + return sum + head; +} + +template +constexpr N sum_of_list(const N sum, const Head &head, const Tail &...tail) { + return sum_of_list(sum + head, tail...); +} + +template constexpr N max_in_list(const N max, const Head &head) { + return (max >= head) ? max : head; +} + +template +constexpr N max_in_list(const N max, const Head &head, const Tail &...tail) { + return max_in_list((max >= head) ? max : head, tail...); +} + +template +constexpr size_t is_first_appearance(const size_t &idx, const size_t &at, const size_t &found, + const Query &query, const Head &head) { + return ((found == ((size_t)-1)) && (query == head) && (idx == at)) ? 1 : 0; +} + +template +constexpr size_t is_first_appearance(const size_t &idx, const size_t &at, const size_t &found, + const Query &query, const Head &head, const Tail &...tail) { + return ((found == ((size_t)-1)) && (query == head) && (idx == at)) ? + 1 : + is_first_appearance(idx + 1, at, (query == head ? idx : found), query, tail...); +} + +#define ZARD_GET_DEVICE_VARGS(n, p, i, _) DEVICE_DT_GET(DT_GPIO_CTLR_BY_IDX(n, p, i)) +#define ZARD_FIRST_APPEARANCE(n, p, i) \ + is_first_appearance(0, i, ((size_t)-1), DEVICE_DT_GET(DT_GPIO_CTLR_BY_IDX(n, p, i)), \ + DT_FOREACH_PROP_ELEM_SEP_VARGS(n, p, ZARD_GET_DEVICE_VARGS, (, ), 0)) + +#if DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios) > 0 + +constexpr int port_num = sum_of_list( + 0, DT_FOREACH_PROP_ELEM_SEP(DT_PATH(zephyr_user), digital_pin_gpios, + ZARD_FIRST_APPEARANCE, (, ))); + +#define ZARD_GPIO_NGPIOS(n, p, i) DT_PROP(DT_GPIO_CTLR_BY_IDX(n, p, i), ngpios) +constexpr int max_ngpios = max_in_list( + 0, DT_FOREACH_PROP_ELEM_SEP(DT_PATH(zephyr_user), digital_pin_gpios, ZARD_GPIO_NGPIOS, (, ))); + +#else + +constexpr int port_num = 1; +constexpr int max_ngpios = 0; + +#endif + +} // namespace arduino +} // namespace zephyr + +#endif // __cplusplus diff --git a/cores/arduino/zephyrCommon.cpp b/cores/arduino/zephyrCommon.cpp index 30df01304..94cb4907a 100644 --- a/cores/arduino/zephyrCommon.cpp +++ b/cores/arduino/zephyrCommon.cpp @@ -5,7 +5,7 @@ */ #include -#include "zephyrInternal.h" +#include "wiring_private.h" #include @@ -22,10 +22,6 @@ void _reinit_peripheral_if_needed(pin_size_t pin, const struct device *dev) { } } -static const struct gpio_dt_spec arduino_pins[] = { - DT_FOREACH_PROP_ELEM_SEP( - DT_PATH(zephyr_user), digital_pin_gpios, GPIO_DT_SPEC_GET_BY_IDX, (, ))}; - #define RETURN_ON_INVALID_PIN(pinNumber, ...) \ do { \ if ((pin_size_t)(pinNumber) >= ARRAY_SIZE(arduino_pins)) { \ @@ -35,63 +31,6 @@ static const struct gpio_dt_spec arduino_pins[] = { namespace { -#if DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios) > 0 - -/* - * Calculate GPIO ports/pins number statically from devicetree configuration - */ - -template constexpr N sum_of_list(const N sum, const Head &head) { - return sum + head; -} - -template -constexpr N sum_of_list(const N sum, const Head &head, const Tail &...tail) { - return sum_of_list(sum + head, tail...); -} - -template constexpr N max_in_list(const N max, const Head &head) { - return (max >= head) ? max : head; -} - -template -constexpr N max_in_list(const N max, const Head &head, const Tail &...tail) { - return max_in_list((max >= head) ? max : head, tail...); -} - -template -constexpr size_t is_first_appearance(const size_t &idx, const size_t &at, const size_t &found, - const Query &query, const Head &head) { - return ((found == ((size_t)-1)) && (query == head) && (idx == at)) ? 1 : 0; -} - -template -constexpr size_t is_first_appearance(const size_t &idx, const size_t &at, const size_t &found, - const Query &query, const Head &head, const Tail &...tail) { - return ((found == ((size_t)-1)) && (query == head) && (idx == at)) ? - 1 : - is_first_appearance(idx + 1, at, (query == head ? idx : found), query, tail...); -} - -#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)), \ - DT_FOREACH_PROP_ELEM_SEP_VARGS(n, p, GET_DEVICE_VARGS, (, ), 0)) -const int port_num = sum_of_list( - 0, DT_FOREACH_PROP_ELEM_SEP(DT_PATH(zephyr_user), digital_pin_gpios, - FIRST_APPEARANCE, (, ))); - -#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 - /* * GPIO callback implementation */ From 649366a90f15dc40afd33e3860f6ba6d066960fe Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Sat, 18 Apr 2026 08:38:51 +0900 Subject: [PATCH 4/4] cores: arduino: Split zephyrCommon.cpp Split zephyrCommon.cpp into smaller source files. wiring_digital.cpp: - pinMode - digitalWrite - digitalRead wiring_analog.cpp: - analogRead - analogWrite - analogReference - analogReadResolution - analogWriteResolution wiring_pulse.cpp: - pulseIn WInterrupts.cpp: - attachInterrupt - detachInterrupt - interrupts - noInterrupts - digitalPinToInterrupt WMath.cpp: - randomSeed - random Tone.cpp: - tone - noTone Signed-off-by: TOKITA Hiroshi --- cores/arduino/CMakeLists.txt | 7 + cores/arduino/Tone.cpp | 184 +++++++++ cores/arduino/WInterrupts.cpp | 155 +++++++ cores/arduino/WMath.cpp | 27 ++ cores/arduino/wiring_analog.cpp | 266 ++++++++++++ cores/arduino/wiring_digital.cpp | 46 +++ cores/arduino/wiring_private.h | 9 + cores/arduino/wiring_pulse.cpp | 53 +++ cores/arduino/zephyrCommon.cpp | 676 +------------------------------ cores/arduino/zephyrInternal.h | 21 - 10 files changed, 758 insertions(+), 686 deletions(-) create mode 100644 cores/arduino/Tone.cpp create mode 100644 cores/arduino/WInterrupts.cpp create mode 100644 cores/arduino/WMath.cpp create mode 100644 cores/arduino/wiring_analog.cpp create mode 100644 cores/arduino/wiring_digital.cpp create mode 100644 cores/arduino/wiring_pulse.cpp delete mode 100644 cores/arduino/zephyrInternal.h diff --git a/cores/arduino/CMakeLists.txt b/cores/arduino/CMakeLists.txt index 36e337b90..c3ab81da0 100644 --- a/cores/arduino/CMakeLists.txt +++ b/cores/arduino/CMakeLists.txt @@ -19,6 +19,13 @@ if(CONFIG_USE_ARDUINO_API_RUST_IMPLEMENTATION) zephyr_sources(apiCommon.cpp) endif() +zephyr_sources(wiring_analog.cpp) +zephyr_sources(wiring_digital.cpp) +zephyr_sources(wiring_pulse.cpp) +zephyr_sources(Tone.cpp) +zephyr_sources(WMath.cpp) +zephyr_sources(WInterrupts.cpp) + if(DEFINED CONFIG_ARDUINO_ENTRY) zephyr_sources(main.cpp) endif() diff --git a/cores/arduino/Tone.cpp b/cores/arduino/Tone.cpp new file mode 100644 index 000000000..ea5dda9b4 --- /dev/null +++ b/cores/arduino/Tone.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (c) Arduino s.r.l. and/or its affiliated companies + * Copyright (c) 2024 Ayush Singh + * Copyright (c) 2026 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "wiring_private.h" + +using namespace zephyr::arduino; + +namespace { + +#if CONFIG_ARDUINO_MAX_TONES < 0 +#define MAX_TONE_PINS DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios) +#else +#define MAX_TONE_PINS CONFIG_ARDUINO_MAX_TONES +#endif + +#define TOGGLES_PER_CYCLE 2ULL + +static struct pin_timer { + struct k_timer timer; + uint32_t count{0}; + pin_size_t pin{pin_size_t(-1)}; + bool infinity{false}; + bool timer_initialized{false}; + struct k_spinlock lock{}; +} arduino_pin_timers[MAX_TONE_PINS]; + +K_MUTEX_DEFINE(timer_cfg_lock); + +void tone_expiry_cb(struct k_timer *timer); + +/* Callers must hold timer_cfg_lock while using this helper. */ +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 == pinNumber) { + k_spin_unlock(&arduino_pin_timers[i].lock, key); + return &arduino_pin_timers[i]; + } + + k_spin_unlock(&arduino_pin_timers[i].lock, key); + } + + if (active_only) { + return nullptr; + } + + 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)) { + arduino_pin_timers[i].pin = pinNumber; + k_spin_unlock(&arduino_pin_timers[i].lock, key); + return &arduino_pin_timers[i]; + } + + k_spin_unlock(&arduino_pin_timers[i].lock, key); + } + + return nullptr; +} + +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); + 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); + } + + k_timer_stop(timer); + pt->count = 0; + pt->infinity = false; + pt->pin = pin_size_t(-1); + } else { + if (pin != pin_size_t(-1)) { + gpio_pin_toggle_dt(&arduino_pins[pin]); + } + pt->count--; + } + + k_spin_unlock(&pt->lock, key); +} + +} // anonymous namespace + +void tone(pin_size_t pinNumber, unsigned int frequency, unsigned long duration) { + RETURN_ON_INVALID_PIN(pinNumber); + + k_spinlock_key_t key; + uint64_t toggles_count; + struct pin_timer *pt; + k_timeout_t timeout; + + if (k_is_in_isr()) { + return; + } + + k_mutex_lock(&timer_cfg_lock, K_FOREVER); + + pt = find_pin_timer(pinNumber, false); + + if (pt == nullptr) { + k_mutex_unlock(&timer_cfg_lock); + return; + } + + if (!pt->timer_initialized) { + k_timer_init(&pt->timer, tone_expiry_cb, NULL); + pt->timer_initialized = true; + } + + pinMode(pinNumber, OUTPUT); + k_timer_stop(&pt->timer); + + toggles_count = ((uint64_t)duration * frequency / (MSEC_PER_SEC / TOGGLES_PER_CYCLE)); + if (frequency == 0 || (toggles_count == 0 && duration != 0)) { + key = k_spin_lock(&pt->lock); + pt->count = 0; + pt->infinity = false; + pt->pin = pin_size_t(-1); + k_spin_unlock(&pt->lock, key); + + gpio_pin_set_dt(&arduino_pins[pinNumber], 0); + + k_mutex_unlock(&timer_cfg_lock); + return; + } + + timeout = K_NSEC(NSEC_PER_SEC / (TOGGLES_PER_CYCLE * frequency)); + if (timeout.ticks == 0) { + timeout.ticks = 1; + } + + key = k_spin_lock(&pt->lock); + pt->infinity = (duration == 0); + pt->count = min(toggles_count, UINT32_MAX); + pt->pin = pinNumber; + k_spin_unlock(&pt->lock, key); + + gpio_pin_set_dt(&arduino_pins[pinNumber], 1); + k_timer_start(&pt->timer, timeout, timeout); + + k_mutex_unlock(&timer_cfg_lock); +} + +void noTone(pin_size_t pinNumber) { + RETURN_ON_INVALID_PIN(pinNumber); + + struct pin_timer *pt; + k_spinlock_key_t key; + + if (k_is_in_isr()) { + return; + } + + k_mutex_lock(&timer_cfg_lock, K_FOREVER); + + pt = find_pin_timer(pinNumber, true); + + if (pt == nullptr) { + k_mutex_unlock(&timer_cfg_lock); + return; + } + + key = k_spin_lock(&pt->lock); + k_timer_stop(&pt->timer); + pt->count = 0; + pt->infinity = false; + pt->pin = pin_size_t(-1); + k_spin_unlock(&pt->lock, key); + + gpio_pin_set_dt(&arduino_pins[pinNumber], 0); + + k_mutex_unlock(&timer_cfg_lock); +} diff --git a/cores/arduino/WInterrupts.cpp b/cores/arduino/WInterrupts.cpp new file mode 100644 index 000000000..a9d2229f6 --- /dev/null +++ b/cores/arduino/WInterrupts.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (c) Arduino s.r.l. and/or its affiliated companies + * Copyright (c) 2022 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "wiring_private.h" + +using namespace zephyr::arduino; + +namespace { + +/* + * GPIO callback implementation + */ + +struct arduino_callback { + voidFuncPtr handler; + bool enabled; +}; + +struct gpio_port_callback { + struct gpio_callback callback; + struct arduino_callback handlers[max_ngpios]; + gpio_port_pins_t pins; + const struct device *dev; +} port_callback[port_num] = {}; + +unsigned int irq_key; +bool interrupts_disabled = false; + +struct gpio_port_callback *find_gpio_port_callback(const struct device *dev) { + for (size_t i = 0; i < ARRAY_SIZE(port_callback); i++) { + if (port_callback[i].dev == dev) { + return &port_callback[i]; + } + if (port_callback[i].dev == nullptr) { + port_callback[i].dev = dev; + return &port_callback[i]; + } + } + + return nullptr; +} + +void setInterruptHandler(pin_size_t pinNumber, voidFuncPtr func) { + RETURN_ON_INVALID_PIN(pinNumber); + + struct gpio_port_callback *pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); + + if (pcb) { + pcb->handlers[arduino_pins[pinNumber].pin].handler = func; + } +} + +void handleGpioCallback(const struct device *port, struct gpio_callback *cb, uint32_t pins) { + (void)port; // unused + struct gpio_port_callback *pcb = (struct gpio_port_callback *)cb; + + for (uint32_t i = 0; i < max_ngpios; i++) { + if (pins & BIT(i) && pcb->handlers[i].enabled) { + pcb->handlers[i].handler(); + } + } +} + +void enableInterrupt(pin_size_t pinNumber) { + RETURN_ON_INVALID_PIN(pinNumber); + + struct gpio_port_callback *pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); + + if (pcb) { + pcb->handlers[arduino_pins[pinNumber].pin].enabled = true; + } +} + +void disableInterrupt(pin_size_t pinNumber) { + RETURN_ON_INVALID_PIN(pinNumber); + + struct gpio_port_callback *pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); + + if (pcb) { + pcb->handlers[arduino_pins[pinNumber].pin].enabled = false; + } +} + +} // anonymous namespace + +void attachInterrupt(pin_size_t pinNumber, voidFuncPtr callback, PinStatus pinStatus) { + RETURN_ON_INVALID_PIN(pinNumber); + + struct gpio_port_callback *pcb; + gpio_flags_t intmode = 0; + + if (!callback) { + return; + } + + if (pinStatus == LOW) { + intmode |= GPIO_INT_LEVEL_LOW; + } else if (pinStatus == HIGH) { + intmode |= GPIO_INT_LEVEL_HIGH; + } else if (pinStatus == CHANGE) { + intmode |= GPIO_INT_EDGE_BOTH; + } else if (pinStatus == FALLING) { + intmode |= GPIO_INT_EDGE_FALLING; + } else if (pinStatus == RISING) { + intmode |= GPIO_INT_EDGE_RISING; + } else { + return; + } + + pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); + __ASSERT(pcb != nullptr, "gpio_port_callback not found"); + + pcb->pins |= BIT(arduino_pins[pinNumber].pin); + 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); +} + +void detachInterrupt(pin_size_t pinNumber) { + RETURN_ON_INVALID_PIN(pinNumber); + + setInterruptHandler(pinNumber, nullptr); + disableInterrupt(pinNumber); +} + +void interrupts(void) { + if (interrupts_disabled) { + irq_unlock(irq_key); + interrupts_disabled = false; + } +} + +void noInterrupts(void) { + if (!interrupts_disabled) { + irq_key = irq_lock(); + interrupts_disabled = true; + } +} + +int digitalPinToInterrupt(pin_size_t pinNumber) { + RETURN_ON_INVALID_PIN(pinNumber, -1); + + struct gpio_port_callback *pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); + + return (pcb) ? pinNumber : -1; +} diff --git a/cores/arduino/WMath.cpp b/cores/arduino/WMath.cpp new file mode 100644 index 000000000..d8dcc835e --- /dev/null +++ b/cores/arduino/WMath.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) Arduino s.r.l. and/or its affiliated companies + * Copyright (c) 2024 Ayush Singh + * Copyright (c) 2026 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#ifndef CONFIG_MINIMAL_LIBC_RAND + +#include + +void randomSeed(unsigned long seed) { + srand(seed); +} + +long random(long min, long max) { + return rand() % (max - min) + min; +} + +long random(long max) { + return rand() % max; +} + +#endif diff --git a/cores/arduino/wiring_analog.cpp b/cores/arduino/wiring_analog.cpp new file mode 100644 index 000000000..ee597f2d3 --- /dev/null +++ b/cores/arduino/wiring_analog.cpp @@ -0,0 +1,266 @@ +/* + * Copyright (c) Arduino s.r.l. and/or its affiliated companies + * Copyright (c) 2022 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "wiring_private.h" + +using namespace zephyr::arduino; + +#define PWM_DT_SPEC(n, p, i) PWM_DT_SPEC_GET_BY_IDX(n, i), +#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 ADC_DT_SPEC(n, p, i) ADC_DT_SPEC_GET_BY_IDX(n, i), +#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_CH_CFG(n, p, i) arduino_adc[i].channel_cfg, + +#define DAC_NODE DT_PHANDLE(DT_PATH(zephyr_user), dac) +#define DAC_RESOLUTION DT_PROP(DT_PATH(zephyr_user), dac_resolution) +#define DAC_CHANNEL_DEFINE(n, p, i) \ + { \ + .channel_id = DT_PROP_BY_IDX(n, p, i), \ + .resolution = DAC_RESOLUTION, \ + .buffered = true, \ + .internal = false, \ + }, + +namespace { + +#ifdef CONFIG_PWM + +const struct pwm_dt_spec arduino_pwm[] = { + DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), pwms, PWM_DT_SPEC)}; + +/* 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)}; + +size_t pwm_pin_index(pin_size_t pinNumber) { + for (size_t i = 0; i < ARRAY_SIZE(arduino_pwm_pins); i++) { + if (arduino_pwm_pins[i] == pinNumber) { + return i; + } + } + return (size_t)-1; +} + +#endif // CONFIG_PWM + +#ifdef CONFIG_ADC + +const struct adc_dt_spec arduino_adc[] = { + DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels, ADC_DT_SPEC)}; + +/* io-channel-pins node 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)}; + +struct adc_channel_cfg channel_cfg[] = { + DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels, ADC_CH_CFG)}; + +size_t analog_pin_index(pin_size_t pinNumber) { + for (size_t i = 0; i < ARRAY_SIZE(arduino_analog_pins); i++) { + if (arduino_analog_pins[i] == pinNumber) { + return i; + } + } + return (size_t)-1; +} + +#endif // CONFIG_ADC + +#ifdef CONFIG_DAC + +#if (DT_NODE_HAS_PROP(DT_PATH(zephyr_user), dac)) + +const struct device *const dac_dev = DEVICE_DT_GET(DAC_NODE); + +#if DT_PROP_LEN_OR(DT_PATH(zephyr_user), dac_channels, 0) > 0 +static const struct dac_channel_cfg dac_ch_cfg[] = { + DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), dac_channels, DAC_CHANNEL_DEFINE)}; + +static bool dac_channel_initialized[NUM_OF_DACS]; +#endif + +#endif + +#endif // CONFIG_DAC + +#if defined(CONFIG_DAC) || defined(CONFIG_PWM) +int _analog_write_resolution = 8; +#endif + +// Note: We can not update the arduino_adc structure as it is read only... +static int read_resolution = 10; + +uint32_t map64(uint32_t x, uint32_t in_min, uint32_t in_max, uint32_t out_min, uint32_t out_max) { + return ((uint64_t)(x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min); +} + +} // anonymous namespace + +#if defined(CONFIG_DAC) || defined(CONFIG_PWM) +void analogWriteResolution(int bits) { + _analog_write_resolution = bits; +} + +int analogWriteResolution() { + return _analog_write_resolution; +} +#endif + +#ifdef CONFIG_PWM + +void analogWrite(pin_size_t pinNumber, int value) { + const int maxInput = BIT(_analog_write_resolution) - 1U; + size_t idx = pwm_pin_index(pinNumber); + + if (idx >= ARRAY_SIZE(arduino_pwm)) { + pinMode(pinNumber, OUTPUT); + digitalWrite(pinNumber, value > 127 ? HIGH : LOW); + return; + } + + if (!pwm_is_ready_dt(&arduino_pwm[idx])) { + pinMode(pinNumber, OUTPUT); + digitalWrite(pinNumber, value > 127 ? HIGH : LOW); + return; + } + + _reinit_peripheral_if_needed(pinNumber, arduino_pwm[idx].dev); + value = CLAMP(value, 0, maxInput); + + const uint32_t pulse = map64(value, 0, maxInput, 0, arduino_pwm[idx].period); + + /* + * A duty ratio determines by the period value defined in dts + * and the value arguments. So usually the period value sets as 255. + */ + (void)pwm_set_pulse_dt(&arduino_pwm[idx], pulse); +} + +#endif + +#ifdef CONFIG_DAC +void analogWrite(enum dacPins dacName, int value) { +#if DT_PROP_LEN_OR(DT_PATH(zephyr_user), dac_channels, 0) > 0 + const int maxInput = BIT(_analog_write_resolution) - 1U; + int ret = 0; + + if (dacName >= NUM_OF_DACS) { + return; + } + + if (!dac_channel_initialized[dacName]) { + if (!device_is_ready(dac_dev)) { + return; + } + + // TODO: add reverse map to find pin name from DAC* define + // In the meantime, consider A0 == DAC0 + _reinit_peripheral_if_needed((pin_size_t)(dacName + A0), dac_dev); + ret = dac_channel_setup(dac_dev, &dac_ch_cfg[dacName]); + if (ret != 0) { + return; + } + dac_channel_initialized[dacName] = true; + } + + value = CLAMP(value, 0, maxInput); + + const int max_dac_value = BIT(dac_ch_cfg[dacName].resolution) - 1; + const uint32_t output = map(value, 0, maxInput, 0, max_dac_value); + + (void)dac_write_value(dac_dev, dac_ch_cfg[dacName].channel_id, output); +#else + ARG_UNUSED(dacName); + ARG_UNUSED(value); +#endif +} +#endif + +#ifdef CONFIG_ADC + +void __attribute__((weak)) analogReference(uint8_t mode) { + /* + * The Arduino API not clearly defined what means of + * the mode argument of analogReference(). + * Treat the value as equivalent to zephyr's adc_reference. + */ + for (size_t i = 0; i < ARRAY_SIZE(channel_cfg); i++) { + channel_cfg[i].reference = static_cast(mode); + } +} + +void analogReadResolution(int bits) { + read_resolution = bits; +} + +int analogReadResolution() { + return read_resolution; +} + +int analogRead(pin_size_t pinNumber) { + int err; + uint16_t buf; + struct adc_sequence seq = {.options = nullptr, + .channels = 0, + .buffer = &buf, + .buffer_size = sizeof(buf), +#if defined(CONFIG_ADC_SEQUENCE_PRIORITY) + .priority = 0, +#endif + .resolution = 0, + .oversampling = 0, + .calibrate = false}; + size_t idx = analog_pin_index(pinNumber); + + if (idx >= ARRAY_SIZE(arduino_adc)) { + return -EINVAL; + } + + /* + * ADC that is on MCU supported by Zephyr exists + * only 16bit resolution, currently. + */ + if (arduino_adc[idx].resolution > 16) { + return -ENOTSUP; + } + + _reinit_peripheral_if_needed(pinNumber, arduino_adc[idx].dev); + + err = adc_channel_setup(arduino_adc[idx].dev, &arduino_adc[idx].channel_cfg); + if (err < 0) { + return err; + } + + seq.channels = BIT(arduino_adc[idx].channel_id); + seq.resolution = arduino_adc[idx].resolution; + seq.oversampling = arduino_adc[idx].oversampling; + + err = adc_read(arduino_adc[idx].dev, &seq); + if (err < 0) { + return err; + } + + /* + * If necessary map the return value to the + * number of bits the user has asked for + */ + if (read_resolution == seq.resolution) { + return buf; + } + if (read_resolution < seq.resolution) { + return buf >> (seq.resolution - read_resolution); + } + return buf << (read_resolution - seq.resolution); +} + +#endif diff --git a/cores/arduino/wiring_digital.cpp b/cores/arduino/wiring_digital.cpp new file mode 100644 index 000000000..e797801ae --- /dev/null +++ b/cores/arduino/wiring_digital.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) Arduino s.r.l. and/or its affiliated companies + * Copyright (c) 2022 Dhruva Gole + * Copyright (c) 2026 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "wiring_private.h" + +using namespace zephyr::arduino; + +/* + * The ACTIVE_HIGH flag is set so that A low physical + * level on the pin will be interpreted as value 0. + * A high physical level will be interpreted as value 1 + */ +void pinMode(pin_size_t pinNumber, PinMode pinMode) { + RETURN_ON_INVALID_PIN(pinNumber); + + _reinit_peripheral_if_needed(pinNumber, NULL); + if (pinMode == INPUT) { // input mode + gpio_pin_configure_dt(&arduino_pins[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); + } 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); + } else if (pinMode == OUTPUT) { // output mode + gpio_pin_configure_dt(&arduino_pins[pinNumber], GPIO_OUTPUT_LOW | GPIO_ACTIVE_HIGH); + } +} + +void digitalWrite(pin_size_t pinNumber, PinStatus status) { + RETURN_ON_INVALID_PIN(pinNumber); + + gpio_pin_set_dt(&arduino_pins[pinNumber], status); +} + +PinStatus digitalRead(pin_size_t pinNumber) { + RETURN_ON_INVALID_PIN(pinNumber, LOW); + + return (gpio_pin_get_dt(&arduino_pins[pinNumber]) == 1) ? HIGH : LOW; +} diff --git a/cores/arduino/wiring_private.h b/cores/arduino/wiring_private.h index 04b95c393..0949ca4f1 100644 --- a/cores/arduino/wiring_private.h +++ b/cores/arduino/wiring_private.h @@ -8,6 +8,13 @@ #pragma once +#define RETURN_ON_INVALID_PIN(pinNumber, ...) \ + do { \ + if ((pin_size_t)(pinNumber) >= ARRAY_SIZE(arduino_pins)) { \ + return __VA_ARGS__; \ + } \ + } while (0) + #ifdef __cplusplus namespace zephyr { @@ -75,6 +82,8 @@ constexpr int max_ngpios = 0; #endif +void _reinit_peripheral_if_needed(pin_size_t pin, const struct device *dev); + } // namespace arduino } // namespace zephyr diff --git a/cores/arduino/wiring_pulse.cpp b/cores/arduino/wiring_pulse.cpp new file mode 100644 index 000000000..eaa9171ae --- /dev/null +++ b/cores/arduino/wiring_pulse.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) Arduino s.r.l. and/or its affiliated companies + * Copyright (c) 2024 Ayush Singh + * Copyright (c) 2026 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "wiring_private.h" + +using namespace zephyr::arduino; + +unsigned long pulseIn(pin_size_t pinNumber, uint8_t state, unsigned long timeout) { + RETURN_ON_INVALID_PIN(pinNumber, LOW); + + 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)) { + 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) + ; + if (k_timer_status_get(&timer) > 0) { + goto cleanup; + } + + while (gpio_pin_get_dt(spec) != 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) + ; + if (k_timer_status_get(&timer) > 0) { + goto cleanup; + } + end = k_uptime_ticks(); + + delta = k_ticks_to_us_floor64(end - start); + +cleanup: + k_timer_stop(&timer); + return (unsigned long)delta; +} diff --git a/cores/arduino/zephyrCommon.cpp b/cores/arduino/zephyrCommon.cpp index 94cb4907a..b5bcb22f0 100644 --- a/cores/arduino/zephyrCommon.cpp +++ b/cores/arduino/zephyrCommon.cpp @@ -1,4 +1,5 @@ /* + * Copyright (c) Arduino s.r.l. and/or its affiliated companies * Copyright (c) 2022 Dhruva Gole * * SPDX-License-Identifier: Apache-2.0 @@ -7,7 +8,8 @@ #include #include "wiring_private.h" -#include +namespace zephyr { +namespace arduino { // create an array of arduino_pins with functions to reinitialize pins if needed static const struct device *pinmux_array[DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios)] = { @@ -22,369 +24,15 @@ void _reinit_peripheral_if_needed(pin_size_t pin, const struct device *dev) { } } -#define RETURN_ON_INVALID_PIN(pinNumber, ...) \ - do { \ - if ((pin_size_t)(pinNumber) >= ARRAY_SIZE(arduino_pins)) { \ - return __VA_ARGS__; \ - } \ - } while (0) +} // namespace arduino +} // namespace zephyr -namespace { - -/* - * GPIO callback implementation - */ - -struct arduino_callback { - voidFuncPtr handler; - bool enabled; -}; - -struct gpio_port_callback { - struct gpio_callback callback; - struct arduino_callback handlers[max_ngpios]; - gpio_port_pins_t pins; - const struct device *dev; -} port_callback[port_num] = {}; - -struct gpio_port_callback *find_gpio_port_callback(const struct device *dev) { - for (size_t i = 0; i < ARRAY_SIZE(port_callback); i++) { - if (port_callback[i].dev == dev) { - return &port_callback[i]; - } - if (port_callback[i].dev == nullptr) { - port_callback[i].dev = dev; - return &port_callback[i]; - } - } - - return nullptr; -} - -void setInterruptHandler(pin_size_t pinNumber, voidFuncPtr func) { - RETURN_ON_INVALID_PIN(pinNumber); - - struct gpio_port_callback *pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); - - if (pcb) { - pcb->handlers[arduino_pins[pinNumber].pin].handler = func; - } -} - -void handleGpioCallback(const struct device *port, struct gpio_callback *cb, uint32_t pins) { - (void)port; // unused - struct gpio_port_callback *pcb = (struct gpio_port_callback *)cb; - - for (uint32_t i = 0; i < max_ngpios; i++) { - if (pins & BIT(i) && pcb->handlers[i].enabled) { - pcb->handlers[i].handler(); - } - } -} - -#ifdef CONFIG_PWM - -#define PWM_DT_SPEC(n, p, i) PWM_DT_SPEC_GET_BY_IDX(n, i), -#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)), - -const struct pwm_dt_spec arduino_pwm[] = { - DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), pwms, PWM_DT_SPEC)}; - -/* 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)}; - -size_t pwm_pin_index(pin_size_t pinNumber) { - for (size_t i = 0; i < ARRAY_SIZE(arduino_pwm_pins); i++) { - if (arduino_pwm_pins[i] == pinNumber) { - return i; - } - } - return (size_t)-1; -} - -#endif // CONFIG_PWM - -#ifdef CONFIG_ADC - -#define ADC_DT_SPEC(n, p, i) ADC_DT_SPEC_GET_BY_IDX(n, i), -#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_CH_CFG(n, p, i) arduino_adc[i].channel_cfg, - -static const struct adc_dt_spec arduino_adc[] = { - DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels, ADC_DT_SPEC)}; - -/* io-channel-pins node 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)}; - -struct adc_channel_cfg channel_cfg[] = { - DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels, ADC_CH_CFG)}; - -size_t analog_pin_index(pin_size_t pinNumber) { - for (size_t i = 0; i < ARRAY_SIZE(arduino_analog_pins); i++) { - if (arduino_analog_pins[i] == pinNumber) { - return i; - } - } - return (size_t)-1; -} - -#endif // CONFIG_ADC - -#ifdef CONFIG_DAC - -#if (DT_NODE_HAS_PROP(DT_PATH(zephyr_user), dac)) - -#define DAC_NODE DT_PHANDLE(DT_PATH(zephyr_user), dac) -#define DAC_RESOLUTION DT_PROP(DT_PATH(zephyr_user), dac_resolution) -static const struct device *const dac_dev = DEVICE_DT_GET(DAC_NODE); - -#define DAC_CHANNEL_DEFINE(n, p, i) \ - { \ - .channel_id = DT_PROP_BY_IDX(n, p, i), \ - .resolution = DAC_RESOLUTION, \ - .buffered = true, \ - .internal = false, \ - }, - -#if DT_PROP_LEN_OR(DT_PATH(zephyr_user), dac_channels, 0) > 0 -static const struct dac_channel_cfg dac_ch_cfg[] = { - DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), dac_channels, DAC_CHANNEL_DEFINE)}; - -static bool dac_channel_initialized[NUM_OF_DACS]; -#endif - -#endif - -#endif // CONFIG_DAC - -static unsigned int irq_key; -static bool interrupts_disabled = false; -} // namespace +using namespace zephyr::arduino; void yield(void) { k_yield(); } -const struct device *digitalPinToPortDevice(pin_size_t pinNumber) { - RETURN_ON_INVALID_PIN(pinNumber, nullptr); - - return arduino_pins[pinNumber].port; -} - -int digitalPinToPinIndex(pin_size_t pinNumber) { - RETURN_ON_INVALID_PIN(pinNumber, -1); - - return arduino_pins[pinNumber].pin; -} - -/* - * The ACTIVE_HIGH flag is set so that A low physical - * level on the pin will be interpreted as value 0. - * A high physical level will be interpreted as value 1 - */ -void pinMode(pin_size_t pinNumber, PinMode pinMode) { - RETURN_ON_INVALID_PIN(pinNumber); - - _reinit_peripheral_if_needed(pinNumber, NULL); - if (pinMode == INPUT) { // input mode - gpio_pin_configure_dt(&arduino_pins[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); - } 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); - } else if (pinMode == OUTPUT) { // output mode - gpio_pin_configure_dt(&arduino_pins[pinNumber], GPIO_OUTPUT_LOW | GPIO_ACTIVE_HIGH); - } -} - -void digitalWrite(pin_size_t pinNumber, PinStatus status) { - RETURN_ON_INVALID_PIN(pinNumber); - - gpio_pin_set_dt(&arduino_pins[pinNumber], status); -} - -PinStatus digitalRead(pin_size_t pinNumber) { - RETURN_ON_INVALID_PIN(pinNumber, LOW); - - return (gpio_pin_get_dt(&arduino_pins[pinNumber]) == 1) ? HIGH : LOW; -} - -#if CONFIG_ARDUINO_MAX_TONES < 0 -#define MAX_TONE_PINS DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios) -#else -#define MAX_TONE_PINS CONFIG_ARDUINO_MAX_TONES -#endif - -#define TOGGLES_PER_CYCLE 2ULL - -static struct pin_timer { - struct k_timer timer; - uint32_t count{0}; - pin_size_t pin{pin_size_t(-1)}; - bool infinity{false}; - bool timer_initialized{false}; - struct k_spinlock lock{}; -} arduino_pin_timers[MAX_TONE_PINS]; - -K_MUTEX_DEFINE(timer_cfg_lock); - -void tone_expiry_cb(struct k_timer *timer); - -/* Callers must hold timer_cfg_lock while using this helper. */ -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 == pinNumber) { - k_spin_unlock(&arduino_pin_timers[i].lock, key); - return &arduino_pin_timers[i]; - } - - k_spin_unlock(&arduino_pin_timers[i].lock, key); - } - - if (active_only) { - return nullptr; - } - - 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)) { - arduino_pin_timers[i].pin = pinNumber; - k_spin_unlock(&arduino_pin_timers[i].lock, key); - return &arduino_pin_timers[i]; - } - - k_spin_unlock(&arduino_pin_timers[i].lock, key); - } - - return nullptr; -} - -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); - 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); - } - - k_timer_stop(timer); - pt->count = 0; - pt->infinity = false; - pt->pin = pin_size_t(-1); - } else { - if (pin != pin_size_t(-1)) { - gpio_pin_toggle_dt(&arduino_pins[pin]); - } - pt->count--; - } - - k_spin_unlock(&pt->lock, key); -} - -void tone(pin_size_t pinNumber, unsigned int frequency, unsigned long duration) { - RETURN_ON_INVALID_PIN(pinNumber); - - k_spinlock_key_t key; - uint64_t toggles_count; - struct pin_timer *pt; - k_timeout_t timeout; - - if (k_is_in_isr()) { - return; - } - - k_mutex_lock(&timer_cfg_lock, K_FOREVER); - - pt = find_pin_timer(pinNumber, false); - - if (pt == nullptr) { - k_mutex_unlock(&timer_cfg_lock); - return; - } - - if (!pt->timer_initialized) { - k_timer_init(&pt->timer, tone_expiry_cb, NULL); - pt->timer_initialized = true; - } - - pinMode(pinNumber, OUTPUT); - k_timer_stop(&pt->timer); - - toggles_count = ((uint64_t)duration * frequency / (MSEC_PER_SEC / TOGGLES_PER_CYCLE)); - if (frequency == 0 || (toggles_count == 0 && duration != 0)) { - key = k_spin_lock(&pt->lock); - pt->count = 0; - pt->infinity = false; - pt->pin = pin_size_t(-1); - k_spin_unlock(&pt->lock, key); - - gpio_pin_set_dt(&arduino_pins[pinNumber], 0); - - k_mutex_unlock(&timer_cfg_lock); - return; - } - - timeout = K_NSEC(NSEC_PER_SEC / (TOGGLES_PER_CYCLE * frequency)); - if (timeout.ticks == 0) { - timeout.ticks = 1; - } - - key = k_spin_lock(&pt->lock); - pt->infinity = (duration == 0); - pt->count = min(toggles_count, UINT32_MAX); - pt->pin = pinNumber; - k_spin_unlock(&pt->lock, key); - - gpio_pin_set_dt(&arduino_pins[pinNumber], 1); - k_timer_start(&pt->timer, timeout, timeout); - - k_mutex_unlock(&timer_cfg_lock); -} - -void noTone(pin_size_t pinNumber) { - RETURN_ON_INVALID_PIN(pinNumber); - - struct pin_timer *pt; - k_spinlock_key_t key; - - if (k_is_in_isr()) { - return; - } - - k_mutex_lock(&timer_cfg_lock, K_FOREVER); - - pt = find_pin_timer(pinNumber, true); - - if (pt == nullptr) { - k_mutex_unlock(&timer_cfg_lock); - return; - } - - key = k_spin_lock(&pt->lock); - k_timer_stop(&pt->timer); - pt->count = 0; - pt->infinity = false; - pt->pin = pin_size_t(-1); - k_spin_unlock(&pt->lock, key); - - gpio_pin_set_dt(&arduino_pins[pinNumber], 0); - - k_mutex_unlock(&timer_cfg_lock); -} - unsigned long micros(void) { #ifdef CONFIG_TIMER_HAS_64BIT_CYCLE_COUNTER return k_cyc_to_us_floor32(k_cycle_get_64()); @@ -397,316 +45,14 @@ unsigned long millis(void) { return k_uptime_get_32(); } -#if defined(CONFIG_DAC) || defined(CONFIG_PWM) -static int _analog_write_resolution = 8; - -void analogWriteResolution(int bits) { - _analog_write_resolution = bits; -} - -int analogWriteResolution() { - return _analog_write_resolution; -} -#endif - -#ifdef CONFIG_PWM - -static uint32_t map64(uint32_t x, uint32_t in_min, uint32_t in_max, uint32_t out_min, - uint32_t out_max) { - return ((uint64_t)(x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min); -} - -void analogWrite(pin_size_t pinNumber, int value) { - const int maxInput = BIT(_analog_write_resolution) - 1U; - size_t idx = pwm_pin_index(pinNumber); - - if (idx >= ARRAY_SIZE(arduino_pwm)) { - pinMode(pinNumber, OUTPUT); - digitalWrite(pinNumber, value > 127 ? HIGH : LOW); - return; - } - - if (!pwm_is_ready_dt(&arduino_pwm[idx])) { - pinMode(pinNumber, OUTPUT); - digitalWrite(pinNumber, value > 127 ? HIGH : LOW); - return; - } - - _reinit_peripheral_if_needed(pinNumber, arduino_pwm[idx].dev); - value = CLAMP(value, 0, maxInput); - - const uint32_t pulse = map64(value, 0, maxInput, 0, arduino_pwm[idx].period); - - /* - * A duty ratio determines by the period value defined in dts - * and the value arguments. So usually the period value sets as 255. - */ - (void)pwm_set_pulse_dt(&arduino_pwm[idx], pulse); -} - -#endif - -#ifdef CONFIG_DAC -void analogWrite(enum dacPins dacName, int value) { -#if DT_PROP_LEN_OR(DT_PATH(zephyr_user), dac_channels, 0) > 0 - const int maxInput = BIT(_analog_write_resolution) - 1U; - int ret = 0; - - if (dacName >= NUM_OF_DACS) { - return; - } - - if (!dac_channel_initialized[dacName]) { - if (!device_is_ready(dac_dev)) { - return; - } - - // TODO: add reverse map to find pin name from DAC* define - // In the meantime, consider A0 == DAC0 - _reinit_peripheral_if_needed((pin_size_t)(dacName + A0), dac_dev); - ret = dac_channel_setup(dac_dev, &dac_ch_cfg[dacName]); - if (ret != 0) { - return; - } - dac_channel_initialized[dacName] = true; - } - - value = CLAMP(value, 0, maxInput); - - const int max_dac_value = BIT(dac_ch_cfg[dacName].resolution) - 1; - const uint32_t output = map(value, 0, maxInput, 0, max_dac_value); - - (void)dac_write_value(dac_dev, dac_ch_cfg[dacName].channel_id, output); -#else - ARG_UNUSED(dacName); - ARG_UNUSED(value); -#endif -} -#endif - -#ifdef CONFIG_ADC - -void __attribute__((weak)) analogReference(uint8_t mode) { - /* - * The Arduino API not clearly defined what means of - * the mode argument of analogReference(). - * Treat the value as equivalent to zephyr's adc_reference. - */ - for (size_t i = 0; i < ARRAY_SIZE(channel_cfg); i++) { - channel_cfg[i].reference = static_cast(mode); - } -} - -// Note: We can not update the arduino_adc structure as it is read only... -static int read_resolution = 10; - -void analogReadResolution(int bits) { - read_resolution = bits; -} - -int analogReadResolution() { - return read_resolution; -} - -int analogRead(pin_size_t pinNumber) { - int err; - uint16_t buf; - struct adc_sequence seq = {.options = nullptr, - .channels = 0, - .buffer = &buf, - .buffer_size = sizeof(buf), -#if defined(CONFIG_ADC_SEQUENCE_PRIORITY) - .priority = 0, -#endif - .resolution = 0, - .oversampling = 0, - .calibrate = false}; - size_t idx = analog_pin_index(pinNumber); - - if (idx >= ARRAY_SIZE(arduino_adc)) { - return -EINVAL; - } - - /* - * ADC that is on MCU supported by Zephyr exists - * only 16bit resolution, currently. - */ - if (arduino_adc[idx].resolution > 16) { - return -ENOTSUP; - } - - _reinit_peripheral_if_needed(pinNumber, arduino_adc[idx].dev); - - err = adc_channel_setup(arduino_adc[idx].dev, &arduino_adc[idx].channel_cfg); - if (err < 0) { - return err; - } - - seq.channels = BIT(arduino_adc[idx].channel_id); - seq.resolution = arduino_adc[idx].resolution; - seq.oversampling = arduino_adc[idx].oversampling; - - err = adc_read(arduino_adc[idx].dev, &seq); - if (err < 0) { - return err; - } - - /* - * If necessary map the return value to the - * number of bits the user has asked for - */ - if (read_resolution == seq.resolution) { - return buf; - } - if (read_resolution < seq.resolution) { - return buf >> (seq.resolution - read_resolution); - } - return buf << (read_resolution - seq.resolution); -} - -#endif - -void attachInterrupt(pin_size_t pinNumber, voidFuncPtr callback, PinStatus pinStatus) { - RETURN_ON_INVALID_PIN(pinNumber); - - struct gpio_port_callback *pcb; - gpio_flags_t intmode = 0; - - if (!callback) { - return; - } - - if (pinStatus == LOW) { - intmode |= GPIO_INT_LEVEL_LOW; - } else if (pinStatus == HIGH) { - intmode |= GPIO_INT_LEVEL_HIGH; - } else if (pinStatus == CHANGE) { - intmode |= GPIO_INT_EDGE_BOTH; - } else if (pinStatus == FALLING) { - intmode |= GPIO_INT_EDGE_FALLING; - } else if (pinStatus == RISING) { - intmode |= GPIO_INT_EDGE_RISING; - } else { - return; - } - - pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); - __ASSERT(pcb != nullptr, "gpio_port_callback not found"); - - pcb->pins |= BIT(arduino_pins[pinNumber].pin); - 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); -} - -void detachInterrupt(pin_size_t pinNumber) { - RETURN_ON_INVALID_PIN(pinNumber); - - setInterruptHandler(pinNumber, nullptr); - disableInterrupt(pinNumber); -} - -#ifndef CONFIG_MINIMAL_LIBC_RAND - -#include - -void randomSeed(unsigned long seed) { - srand(seed); -} - -long random(long min, long max) { - return rand() % (max - min) + min; -} - -long random(long max) { - return rand() % max; -} - -#endif - -unsigned long pulseIn(pin_size_t pinNumber, uint8_t state, unsigned long timeout) { - RETURN_ON_INVALID_PIN(pinNumber, LOW); - - 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)) { - 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) - ; - if (k_timer_status_get(&timer) > 0) { - goto cleanup; - } - - while (gpio_pin_get_dt(spec) != 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) - ; - if (k_timer_status_get(&timer) > 0) { - goto cleanup; - } - end = k_uptime_ticks(); - - delta = k_ticks_to_us_floor64(end - start); - -cleanup: - k_timer_stop(&timer); - return (unsigned long)delta; -} - -void enableInterrupt(pin_size_t pinNumber) { - RETURN_ON_INVALID_PIN(pinNumber); - - struct gpio_port_callback *pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); - - if (pcb) { - pcb->handlers[arduino_pins[pinNumber].pin].enabled = true; - } -} - -void disableInterrupt(pin_size_t pinNumber) { - RETURN_ON_INVALID_PIN(pinNumber); - - struct gpio_port_callback *pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); - - if (pcb) { - pcb->handlers[arduino_pins[pinNumber].pin].enabled = false; - } -} - -void interrupts(void) { - if (interrupts_disabled) { - irq_unlock(irq_key); - interrupts_disabled = false; - } -} +const struct device *digitalPinToPortDevice(pin_size_t pinNumber) { + RETURN_ON_INVALID_PIN(pinNumber, nullptr); -void noInterrupts(void) { - if (!interrupts_disabled) { - irq_key = irq_lock(); - interrupts_disabled = true; - } + return arduino_pins[pinNumber].port; } -int digitalPinToInterrupt(pin_size_t pinNumber) { +int digitalPinToPinIndex(pin_size_t pinNumber) { RETURN_ON_INVALID_PIN(pinNumber, -1); - struct gpio_port_callback *pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); - - return (pcb) ? pinNumber : -1; + return arduino_pins[pinNumber].pin; } diff --git a/cores/arduino/zephyrInternal.h b/cores/arduino/zephyrInternal.h deleted file mode 100644 index e3938e603..000000000 --- a/cores/arduino/zephyrInternal.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) 2024 Ayush Singh - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -void enableInterrupt(pin_size_t); -void disableInterrupt(pin_size_t); -void _reinit_peripheral_if_needed(pin_size_t pin, const struct device *dev); - -#ifdef __cplusplus -} // extern "C" -#endif