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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
192 changes: 168 additions & 24 deletions cores/arduino/zephyrCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,145 @@

#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 and zephyr,console (that is not deferred) with their
* pinctrl configuration from devicetree.
*/
#define NODE_SELECTED(node_id) \
UTIL_OR(DT_PROP(node_id, zephyr_deferred_init), \
DT_SAME_NODE(node_id, DT_CHOSEN(zephyr_console)))

#define PINCTRL_DEFINE_IF_SELECTED(node_id) \
COND_CODE_1(NODE_SELECTED(node_id), (PINCTRL_DT_DEFINE(node_id);), ())

DT_FOREACH_STATUS_OKAY_NODE(PINCTRL_DEFINE_IF_SELECTED)

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(NODE_SELECTED(node_id), (PINCTRL_MAP_ENTRY(node_id)), ())

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

#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);

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;
}

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 (!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 +268,24 @@ void handleGpioCallback(const struct device *port, struct gpio_callback *cb, uin
}
}

/*
* 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 thread
pillo79 marked this conversation as resolved.

#ifdef CONFIG_PWM

#define PWM_DT_SPEC(n, p, i) PWM_DT_SPEC_GET_BY_IDX(n, i),
Expand Down Expand Up @@ -216,7 +359,6 @@ static const struct device *const dac_dev = DEVICE_DT_GET(DAC_NODE);
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 +393,6 @@ 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
Expand Down Expand Up @@ -486,13 +627,15 @@ void analogWrite(pin_size_t pinNumber, int value) {
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 +659,13 @@ void analogWrite(enum dacPins dacName, int value) {
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 +723,14 @@ int analogRead(pin_size_t pinNumber) {
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
Expand Up @@ -14,7 +14,8 @@ 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);

int init_dev_apply_pinctrl(const struct device *dev);

#ifdef __cplusplus
} // extern "C"
Expand Down
7 changes: 6 additions & 1 deletion cores/arduino/zephyrSerial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* SPDX-License-Identifier: Apache-2.0
*/

#include "zephyrInternal.h"

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

Expand Down Expand Up @@ -60,7 +62,10 @@ void arduino::ZephyrSerial::begin(unsigned long baud, uint16_t conf) {
.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