66
77#include < Arduino.h>
88#include " zephyrInternal.h"
9+ #include < zephyr/drivers/pinctrl.h>
910
1011#include < zephyr/spinlock.h>
1112
12- // create an array of arduino_pins with functions to reinitialize pins if needed
13- static const struct device *pinmux_array[DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios)] = {
14- nullptr };
13+ #if defined(ARDUINO)
14+ /*
15+ * The global ARDUINO macro is numeric (e.g. 10607) in Arduino builds.
16+ * Temporarily hide it so pinctrl token concatenation can use the literal
17+ * custom state name "ARDUINO" from devicetree pinctrl-names.
18+ * Otherwise, the generated pinctrl state identifiers would be like PINCTRL_STATE_10607 instead of
19+ * PINCTRL_STATE_ARDUINO.
20+ */
21+ #pragma push_macro("ARDUINO")
22+ #undef ARDUINO
23+ #endif
24+
25+ /*
26+ * Pinctrl configuration structures for dynamic pin switching.
27+ *
28+ * Map deferred-init peripherals and zephyr,console (that is not deferred) with their
29+ * pinctrl configuration from devicetree.
30+ */
31+ #define NODE_SELECTED (node_id ) \
32+ UTIL_OR (DT_PROP(node_id, zephyr_deferred_init), \
33+ DT_SAME_NODE(node_id, DT_CHOSEN(zephyr_console)))
34+
35+ #define PINCTRL_DEFINE_IF_SELECTED (node_id ) \
36+ COND_CODE_1 (NODE_SELECTED(node_id), (PINCTRL_DT_DEFINE(node_id);), ())
37+
38+ DT_FOREACH_STATUS_OKAY_NODE (PINCTRL_DEFINE_IF_SELECTED)
39+
40+ struct pinctrl_map_entry {
41+ const struct device *dev;
42+ const struct pinctrl_dev_config *pcfg;
43+ };
44+
45+ #define PINCTRL_MAP_ENTRY (node_id ) {DEVICE_DT_GET (node_id), PINCTRL_DT_DEV_CONFIG_GET (node_id)},
46+ #define PINCTRL_MAP_ENTRY_IF_PRESENT (node_id ) \
47+ COND_CODE_1 (NODE_SELECTED(node_id), (PINCTRL_MAP_ENTRY(node_id)), ())
48+
49+ static const struct pinctrl_map_entry pinctrl_map[] = {
50+ DT_FOREACH_STATUS_OKAY_NODE (PINCTRL_MAP_ENTRY_IF_PRESENT){NULL , NULL },
51+ };
52+
53+ #if defined(ARDUINO)
54+ #pragma pop_macro("ARDUINO")
55+ #endif
56+
57+ /* Get pinctrl_dev_config for a device from the generated map. */
58+ static const struct pinctrl_dev_config *get_known_pcfg (const struct device *dev) {
59+ for (size_t i = 0 ; i < ARRAY_SIZE (pinctrl_map); i++) {
60+ if (pinctrl_map[i].dev == dev) {
61+ return pinctrl_map[i].pcfg ;
62+ }
63+ }
64+
65+ return nullptr ;
66+ }
67+
68+ /* *
69+ * @brief Initialize the peripheral and acquire a single pin to ARDUINO state.
70+ *
71+ * Switches peripheral pins back to ARDUINO pinctrl state (alternate function),
72+ * typically after a temporary transition to GPIO mode.
73+ *
74+ * @param dev Pointer to the peripheral device
75+ * @param state_pin_idx Index of the pin within the device's ARDUINO pinctrl state
76+ * @return 0 on success, negative on error
77+ */
78+ int init_dev_apply_channel_pinctrl (const struct device *dev, size_t state_pin_idx) {
79+
80+ if (dev == nullptr ) {
81+ return -EINVAL;
82+ }
83+
84+ if (!device_is_ready (dev)) {
85+ // init device for first usage, if not ready
86+ int err = device_init (dev);
87+ if (err < 0 ) {
88+ return err;
89+ }
90+ }
91+
92+ const struct pinctrl_state *state;
93+ const struct pinctrl_dev_config *pcfg = get_known_pcfg (dev);
94+
95+ if (pcfg == nullptr ) {
96+ /* Device not in DT mapping - add to pinctrl_map if needed */
97+ return -ENOTSUP;
98+ }
99+
100+ int err = pinctrl_lookup_state (pcfg, PINCTRL_STATE_ARDUINO, &state);
101+ if (err < 0 ) {
102+ return err; /* Fails if the state is not defined in pinctrl-names */
103+ }
104+
105+ /* bounds check */
106+ if (state_pin_idx >= state->pin_cnt ) {
107+ return -ERANGE;
108+ }
109+
110+ /*
111+ * On platforms without CONFIG_PINCTRL_STORE_REG (e.g. STM32) the pcfg->reg is not present but
112+ * the argument is ignored by their pinctrl driver, so passing PINCTRL_REG_NONE is safe.
113+ */
114+ #ifdef CONFIG_PINCTRL_STORE_REG
115+ uintptr_t reg = pcfg->reg ;
116+ #else
117+ uintptr_t reg = PINCTRL_REG_NONE;
118+ #endif
119+
120+ return pinctrl_configure_pins (&state->pins [state_pin_idx], 1 , reg);
121+ }
122+
123+ /* *
124+ * @brief Optimize peripheral transitions applying pinctrl state PINCTRL_STATE_DEFAULT.
125+ *
126+ * @param dev Target peripheral device to acquire pin for
127+ */
128+ int init_dev_apply_pinctrl (const struct device *dev) {
129+
130+ if (dev == nullptr ) {
131+ return -EINVAL;
132+ }
15133
16- void _reinit_peripheral_if_needed (pin_size_t pin, const struct device *dev) {
17- if (pinmux_array[pin] != dev) {
18- pinmux_array[pin] = dev;
19- if (dev != NULL ) {
20- device_init (dev);
134+ if (!device_is_ready (dev)) {
135+ int ret = device_init (dev);
136+ if (ret != 0 && ret != -EALREADY) {
137+ return ret;
21138 }
22139 }
140+
141+ const struct pinctrl_dev_config *pcfg = get_known_pcfg (dev);
142+ if (pcfg == nullptr ) {
143+ /* Device not in DT mapping - add to pinctrl_map if needed */
144+ return -ENOTSUP;
145+ }
146+
147+ return pinctrl_apply_state (pcfg, PINCTRL_STATE_DEFAULT);
23148}
24149
25150static const struct gpio_dt_spec arduino_pins[] = {
@@ -143,6 +268,24 @@ void handleGpioCallback(const struct device *port, struct gpio_callback *cb, uin
143268 }
144269}
145270
271+ /*
272+ * Resolve pin index in a device ARDUINO pinctrl state from a DT spec array.
273+ * The resulting index is the per-device ordinal at spec_idx.
274+ */
275+ template <typename DT_SPEC, size_t N>
276+ static size_t state_pin_index_from_spec_index (const DT_SPEC (&specs)[N], size_t spec_idx) {
277+ const struct device *dev = specs[spec_idx].dev ;
278+ size_t state_pin_idx = 0 ;
279+
280+ for (size_t i = 0 ; i < spec_idx; i++) {
281+ if (specs[i].dev == dev) {
282+ state_pin_idx++;
283+ }
284+ }
285+
286+ return state_pin_idx;
287+ }
288+
146289#ifdef CONFIG_PWM
147290
148291#define PWM_DT_SPEC (n, p, i ) PWM_DT_SPEC_GET_BY_IDX(n, i),
@@ -216,7 +359,6 @@ static const struct device *const dac_dev = DEVICE_DT_GET(DAC_NODE);
216359static const struct dac_channel_cfg dac_ch_cfg[] = {
217360 DT_FOREACH_PROP_ELEM (DT_PATH (zephyr_user), dac_channels, DAC_CHANNEL_DEFINE)};
218361
219- static bool dac_channel_initialized[NUM_OF_DACS];
220362#endif
221363
222364#endif
@@ -251,7 +393,6 @@ int digitalPinToPinIndex(pin_size_t pinNumber) {
251393void pinMode (pin_size_t pinNumber, PinMode pinMode) {
252394 RETURN_ON_INVALID_PIN (pinNumber);
253395
254- _reinit_peripheral_if_needed (pinNumber, NULL );
255396 if (pinMode == INPUT) { // input mode
256397 gpio_pin_configure_dt (&arduino_pins[pinNumber], GPIO_INPUT | GPIO_ACTIVE_HIGH);
257398 } else if (pinMode == INPUT_PULLUP) { // input with internal pull-up
@@ -486,13 +627,15 @@ void analogWrite(pin_size_t pinNumber, int value) {
486627 return ;
487628 }
488629
630+ (void )init_dev_apply_channel_pinctrl (arduino_pwm[idx].dev ,
631+ state_pin_index_from_spec_index (arduino_pwm, idx));
632+
489633 if (!pwm_is_ready_dt (&arduino_pwm[idx])) {
490634 pinMode (pinNumber, OUTPUT);
491635 digitalWrite (pinNumber, value > 127 ? HIGH : LOW);
492636 return ;
493637 }
494638
495- _reinit_peripheral_if_needed (pinNumber, arduino_pwm[idx].dev );
496639 value = CLAMP (value, 0 , maxInput);
497640
498641 const uint32_t pulse = map64 (value, 0 , maxInput, 0 , arduino_pwm[idx].period );
@@ -516,19 +659,13 @@ void analogWrite(enum dacPins dacName, int value) {
516659 return ;
517660 }
518661
519- if (!dac_channel_initialized[dacName]) {
520- if (!device_is_ready (dac_dev)) {
521- return ;
522- }
662+ // TODO: add reverse map to find pin name from DAC* define
663+ // In the meantime, consider A0 == DAC0
664+ (void )init_dev_apply_pinctrl (dac_dev);
523665
524- // TODO: add reverse map to find pin name from DAC* define
525- // In the meantime, consider A0 == DAC0
526- _reinit_peripheral_if_needed ((pin_size_t )(dacName + A0), dac_dev);
527- ret = dac_channel_setup (dac_dev, &dac_ch_cfg[dacName]);
528- if (ret != 0 ) {
529- return ;
530- }
531- dac_channel_initialized[dacName] = true ;
666+ ret = dac_channel_setup (dac_dev, &dac_ch_cfg[dacName]);
667+ if (ret != 0 ) {
668+ return ;
532669 }
533670
534671 value = CLAMP (value, 0 , maxInput);
@@ -586,7 +723,14 @@ int analogRead(pin_size_t pinNumber) {
586723 return -ENOTSUP;
587724 }
588725
589- _reinit_peripheral_if_needed (pinNumber, arduino_adc[idx].dev );
726+ /*
727+ * Init the ADC device for the first acquisition and restore only the required pin to analog
728+ * mode when transitioning from GPIO. The pin is selected from the ADC device "arduino" pinctrl
729+ * state. Not checking the return value because the device might not have pinctrl (e.g. nRF
730+ * SAADC).
731+ */
732+ (void )init_dev_apply_channel_pinctrl (arduino_adc[idx].dev ,
733+ state_pin_index_from_spec_index (arduino_adc, idx));
590734
591735 err = adc_channel_setup (arduino_adc[idx].dev , &arduino_adc[idx].channel_cfg );
592736 if (err < 0 ) {
0 commit comments