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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions cores/arduino/SerialUSB.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,6 @@ class SerialUSB_ : public ZephyrSerial {
uint32_t baudrate;
static void baudChangeHandler(const struct device *dev, uint32_t rate);

void _reinit_if_needed() override {
/* prevent reinit: USB device is always available */
}

private:
bool started = false;

Expand Down
198 changes: 174 additions & 24 deletions cores/arduino/zephyrCommon.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/*
* Copyright (c) 2022 Dhruva Gole
*
Expand All @@ -6,20 +6,152 @@

#include <Arduino.h>
#include "zephyrInternal.h"
#include <zephyr/drivers/pinctrl.h>

#include <zephyr/spinlock.h>

// 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};
#if defined(ARDUINO)
/*
* The global ARDUINO macro is numeric (e.g. 10607) in Arduino builds.
* Temporarily hide it so pinctrl token concatenation can use the literal
* custom state name "ARDUINO" from devicetree pinctrl-names.
* Otherwise, the generated pinctrl state identifiers would be like PINCTRL_STATE_10607 instead of
* PINCTRL_STATE_ARDUINO.
*/
#pragma push_macro("ARDUINO")
#undef ARDUINO
#endif

/*
* Pinctrl configuration structures for dynamic pin switching.
*
* Map deferred-init peripherals with their pinctrl configuration from devicetree.
*/
#define PINCTRL_NODE_DEFERRED(node_id) DT_PROP(node_id, zephyr_deferred_init)

#define PINCTRL_DECLARE_IF_DEFERRED(node_id) \
COND_CODE_1(PINCTRL_NODE_DEFERRED(node_id), (PINCTRL_DT_DEV_CONFIG_DECLARE(node_id);), ())

DT_FOREACH_STATUS_OKAY_NODE(PINCTRL_DECLARE_IF_DEFERRED)

#if DT_HAS_ALIAS(arduino_log_uart)
PINCTRL_DT_DEV_CONFIG_DECLARE(DT_ALIAS(arduino_log_uart));
#endif

struct pinctrl_map_entry {
const struct device *dev;
const struct pinctrl_dev_config *pcfg;
};

#define PINCTRL_MAP_ENTRY(node_id) {DEVICE_DT_GET(node_id), PINCTRL_DT_DEV_CONFIG_GET(node_id)},
#define PINCTRL_MAP_ENTRY_IF_PRESENT(node_id) \
COND_CODE_1(PINCTRL_NODE_DEFERRED(node_id), (PINCTRL_MAP_ENTRY(node_id)), ())
Comment on lines +47 to +48
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly defining all entries here, or those that have a PINCTRL_STATE_ARDUINO, is better because it allows other devices (e.g serial, DAC) to be used in the same way.


/* Add the pinctrl_dev_config for the log-uart nodes, that are not deferred init */
#if DT_HAS_ALIAS(arduino_log_uart)
#define PINCTRL_MAP_LOG_UART_ENTRY PINCTRL_MAP_ENTRY(DT_ALIAS(arduino_log_uart))
#else
#define PINCTRL_MAP_LOG_UART_ENTRY
#endif

static const struct pinctrl_map_entry pinctrl_map[] = {DT_FOREACH_STATUS_OKAY_NODE(
PINCTRL_MAP_ENTRY_IF_PRESENT) PINCTRL_MAP_LOG_UART_ENTRY{nullptr, nullptr}};

#if defined(ARDUINO)
#pragma pop_macro("ARDUINO")
#endif

/* Get pinctrl_dev_config for a device from the generated map. */
static const struct pinctrl_dev_config *get_known_pcfg(const struct device *dev) {
for (size_t i = 0; i < ARRAY_SIZE(pinctrl_map); i++) {
if (pinctrl_map[i].dev == dev) {
return pinctrl_map[i].pcfg;
}
}

return nullptr;
}

/**
* @brief Initialize the peripheral and acquire a single pin to ARDUINO state.
*
* Switches peripheral pins back to ARDUINO pinctrl state (alternate function),
* typically after a temporary transition to GPIO mode.
*
* @param dev Pointer to the peripheral device
* @param state_pin_idx Index of the pin within the device's ARDUINO pinctrl state
* @return 0 on success, negative on error
*/
int init_dev_apply_channel_pinctrl(const struct device *dev, size_t state_pin_idx) {

if (dev == nullptr) {
return -EINVAL;
}

if (!device_is_ready(dev)) {
// init device for first usage, if not ready
int err = device_init(dev);
if (err < 0) {
return err;
}
}

const struct pinctrl_state *state;
const struct pinctrl_dev_config *pcfg = get_known_pcfg(dev);

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);
if (pcfg == nullptr) {
/* Device not in DT mapping - add to pinctrl_map if needed */
return -ENOTSUP;
}

int err = pinctrl_lookup_state(pcfg, PINCTRL_STATE_ARDUINO, &state);
if (err < 0) {
return err; /* Fails if the state is not defined in pinctrl-names */
}

/* bounds check */
if (state_pin_idx >= state->pin_cnt) {
return -ERANGE;
}

/*
* On platforms without CONFIG_PINCTRL_STORE_REG (e.g. STM32) the pcfg->reg is not present but
* the argument is ignored by their pinctrl driver, so passing PINCTRL_REG_NONE is safe.
*/
#ifdef CONFIG_PINCTRL_STORE_REG
uintptr_t reg = pcfg->reg;
#else
uintptr_t reg = PINCTRL_REG_NONE;
#endif

return pinctrl_configure_pins(&state->pins[state_pin_idx], 1, reg);
}

/**
* @brief Optimize peripheral transitions applying pinctrl state PINCTRL_STATE_DEFAULT.
*
* @param dev Target peripheral device to acquire pin for
*/
int init_dev_apply_pinctrl(const struct device *dev) {

if (dev == nullptr) {
return -EINVAL;
}

if (!device_is_ready(dev)) {
int ret = device_init(dev);
if (ret != 0 && ret != -EALREADY) {
return ret;
}
}

const struct pinctrl_dev_config *pcfg = get_known_pcfg(dev);
if (pcfg == nullptr) {
/* Device not in DT mapping - add to pinctrl_map if needed */
return -ENOTSUP;
}

return pinctrl_apply_state(pcfg, PINCTRL_STATE_DEFAULT);
}

static const struct gpio_dt_spec arduino_pins[] = {
Expand Down Expand Up @@ -143,6 +275,24 @@
}
}

/*
* Resolve pin index in a device ARDUINO pinctrl state from a DT spec array.
* The resulting index is the per-device ordinal at spec_idx.
*/
template <typename DT_SPEC, size_t N>
static size_t state_pin_index_from_spec_index(const DT_SPEC (&specs)[N], size_t spec_idx) {
const struct device *dev = specs[spec_idx].dev;
size_t state_pin_idx = 0;

for (size_t i = 0; i < spec_idx; i++) {
if (specs[i].dev == dev) {
state_pin_idx++;
}
}

return state_pin_idx;
}
Comment on lines +282 to +294
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⁉️ this information is not helpful. You're looking for the peripheral channel, which is completely independent of how the entries are sequenced in the board pinout.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the goal here is not to find a peripheral channel number; it is to find the correct pinctrl entry for the selected Arduino pin: for example on GIGA, A0 maps to adc1_inp4_pc4, and D3 maps to tim2_ch3_pa2.

There isn't a Zephyr generic API to map an arbitrary peripheral channel back to the specific pinctrl entry required by pinctrl_configure_pins(), so we derive the pinctrl entry from the board DT ordering.
This approach is peripheral and driver agnostic - works for all Arduino adc and pwm.


#ifdef CONFIG_PWM

#define PWM_DT_SPEC(n, p, i) PWM_DT_SPEC_GET_BY_IDX(n, i),
Expand Down Expand Up @@ -216,7 +366,6 @@
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
Expand Down Expand Up @@ -251,7 +400,6 @@
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
Expand Down Expand Up @@ -486,13 +634,15 @@
return;
}

(void)init_dev_apply_channel_pinctrl(arduino_pwm[idx].dev,
state_pin_index_from_spec_index(arduino_pwm, idx));

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);
Expand All @@ -516,19 +666,13 @@
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
(void)init_dev_apply_pinctrl(dac_dev);

// 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;
ret = dac_channel_setup(dac_dev, &dac_ch_cfg[dacName]);
if (ret != 0) {
return;
}

value = CLAMP(value, 0, maxInput);
Expand Down Expand Up @@ -586,7 +730,13 @@
return -ENOTSUP;
}

_reinit_peripheral_if_needed(pinNumber, arduino_adc[idx].dev);
/*
* Init the ADC device for the first acquisition and restore only the required pin to analog mode when
* transitioning from GPIO. The pin is selected from the ADC device "arduino" pinctrl state. Not
* checking the return value because the device might not have pinctrl (e.g. nRF SAADC).
*/
(void)init_dev_apply_channel_pinctrl(arduino_adc[idx].dev,
state_pin_index_from_spec_index(arduino_adc, idx));

err = adc_channel_setup(arduino_adc[idx].dev, &arduino_adc[idx].channel_cfg);
if (err < 0) {
Expand Down
3 changes: 2 additions & 1 deletion cores/arduino/zephyrInternal.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/*
* Copyright (c) 2024 Ayush Singh <ayush@beagleboard.org>
*
Expand All @@ -14,7 +14,8 @@

void enableInterrupt(pin_size_t);
void disableInterrupt(pin_size_t);
void _reinit_peripheral_if_needed(pin_size_t pin, const struct device *dev);

int init_dev_apply_pinctrl(const struct device *dev);

#ifdef __cplusplus
} // extern "C"
Expand Down
8 changes: 7 additions & 1 deletion cores/arduino/zephyrSerial.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/*
* Copyright (c) 2022 Dhruva Gole
* Copyright (c) Arduino s.r.l. and/or its affiliated companies
Expand All @@ -5,7 +5,10 @@
* SPDX-License-Identifier: Apache-2.0
*/

#include "zephyrInternal.h"

#include <zephyr/devicetree.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/uart.h>

#include <api/HardwareSerial.h>
Expand Down Expand Up @@ -60,7 +63,10 @@
.flow_ctrl = UART_CFG_FLOW_CTRL_NONE,
};

_reinit_if_needed();
/* Re-apply DEFAULT pinctrl state so shared pins
* are remuxed back to Serial after other peripherals have used them.
*/
(void)init_dev_apply_pinctrl(uart);

uart_configure(uart, &config);
uart_irq_callback_user_data_set(uart, arduino::ZephyrSerial::IrqDispatch, this);
Expand Down
8 changes: 1 addition & 7 deletions cores/arduino/zephyrSerial.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,7 @@ class ZephyrSerial : public HardwareSerial {

void end() {
#ifdef CONFIG_DEVICE_DEINIT_SUPPORT
if (uart->ops.deinit) {
uart->ops.deinit(uart);
}
(void)device_deinit(uart);
#endif
}

Expand Down Expand Up @@ -108,10 +106,6 @@ class ZephyrSerial : public HardwareSerial {
const struct device *uart;
ZephyrSerialBuffer<CONFIG_ARDUINO_API_SERIAL_BUFFER_SIZE> tx;
ZephyrSerialBuffer<CONFIG_ARDUINO_API_SERIAL_BUFFER_SIZE> rx;

virtual void _reinit_if_needed() {
uart->ops.init(uart);
}
};

} // namespace arduino
Expand Down
Loading
Loading