Skip to content

Commit aec0d2e

Browse files
committed
cores: arduino: zephyrCommon: rework tone slot handling and timer lifecycle
Rework tone/noTone internals to better match Arduino behavior while making concurrent tone usage configurable and safer. - Add CONFIG_ARDUINO_MAX_TONES (default: -1). - Negative values fall back to the digital pin count from devicetree. - Replace per-pin tone timer arrays with slot-based pin_timer entries. - Remove the timeout companion timer and finish finite tones by counting remaining toggles in tone_expiry_cb(). Signed-off-by: TOKITA Hiroshi <tokita.hiroshi@gmail.com>
1 parent 16c23fa commit aec0d2e

File tree

2 files changed

+153
-24
lines changed

2 files changed

+153
-24
lines changed

Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ config ARDUINO_ENTRY
3030
bool "Provide arduino setup and loop entry points"
3131
default y
3232

33+
config ARDUINO_MAX_TONES
34+
int "Maximum number of tones that can be played simultaneously with tone()"
35+
default -1
36+
help
37+
Specify the maximum number of tones that can be played simultaneously with tone().
38+
If set to -1 (or any other negative value), the maximum number will be
39+
determined from the system's digital pin configuration.
40+
3341
endif
3442

3543
if USB_DEVICE_STACK_NEXT

cores/arduino/zephyrCommon.cpp

Lines changed: 145 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include <Arduino.h>
88
#include "zephyrInternal.h"
99

10+
#include <zephyr/spinlock.h>
11+
1012
// create an array of arduino_pins with functions to reinitialize pins if needed
1113
static const struct device *pinmux_array[DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios)] = {
1214
nullptr};
@@ -244,49 +246,168 @@ PinStatus digitalRead(pin_size_t pinNumber) {
244246
return (gpio_pin_get_dt(&arduino_pins[pinNumber]) == 1) ? HIGH : LOW;
245247
}
246248

247-
struct k_timer arduino_pin_timers[ARRAY_SIZE(arduino_pins)];
248-
struct k_timer arduino_pin_timers_timeout[ARRAY_SIZE(arduino_pins)];
249+
#if CONFIG_ARDUINO_MAX_TONES < 0
250+
#define MAX_TONE_PINS DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios)
251+
#else
252+
#define MAX_TONE_PINS CONFIG_ARDUINO_MAX_TONES
253+
#endif
249254

250-
void tone_expiry_cb(struct k_timer *timer) {
251-
const struct gpio_dt_spec *spec = (gpio_dt_spec *)k_timer_user_data_get(timer);
252-
gpio_pin_toggle_dt(spec);
255+
#define TOGGLES_PER_CYCLE 2ULL
256+
257+
static struct pin_timer {
258+
struct k_timer timer;
259+
uint32_t count{0};
260+
pin_size_t pin{pin_size_t(-1)};
261+
bool infinity{false};
262+
bool timer_initialized{false};
263+
struct k_spinlock lock{};
264+
} arduino_pin_timers[MAX_TONE_PINS];
265+
266+
K_MUTEX_DEFINE(timer_cfg_lock);
267+
268+
void tone_expiry_cb(struct k_timer *timer);
269+
270+
/* Callers must hold timer_cfg_lock while using this helper. */
271+
static struct pin_timer *find_pin_timer(pin_size_t pinNumber, bool active_only) {
272+
for (size_t i = 0; i < ARRAY_SIZE(arduino_pin_timers); i++) {
273+
k_spinlock_key_t key = k_spin_lock(&arduino_pin_timers[i].lock);
274+
275+
if (arduino_pin_timers[i].pin == pinNumber) {
276+
k_spin_unlock(&arduino_pin_timers[i].lock, key);
277+
return &arduino_pin_timers[i];
278+
}
279+
280+
k_spin_unlock(&arduino_pin_timers[i].lock, key);
281+
}
282+
283+
if (active_only) {
284+
return nullptr;
285+
}
286+
287+
for (size_t i = 0; i < ARRAY_SIZE(arduino_pin_timers); i++) {
288+
k_spinlock_key_t key = k_spin_lock(&arduino_pin_timers[i].lock);
289+
290+
if (arduino_pin_timers[i].pin == pin_size_t(-1)) {
291+
arduino_pin_timers[i].pin = pinNumber;
292+
k_spin_unlock(&arduino_pin_timers[i].lock, key);
293+
return &arduino_pin_timers[i];
294+
}
295+
296+
k_spin_unlock(&arduino_pin_timers[i].lock, key);
297+
}
298+
299+
return nullptr;
253300
}
254301

255-
void tone_timeout_cb(struct k_timer *timer) {
256-
pin_size_t pinNumber = (pin_size_t)(uintptr_t)k_timer_user_data_get(timer);
257-
noTone(pinNumber);
302+
void tone_expiry_cb(struct k_timer *timer) {
303+
struct pin_timer *pt = CONTAINER_OF(timer, struct pin_timer, timer);
304+
k_spinlock_key_t key = k_spin_lock(&pt->lock);
305+
pin_size_t pin = pt->pin;
306+
307+
if (pt->count == 0 && !pt->infinity) {
308+
if (pin != pin_size_t(-1)) {
309+
gpio_pin_set_dt(&arduino_pins[pin], 0);
310+
}
311+
312+
k_timer_stop(timer);
313+
pt->count = 0;
314+
pt->infinity = false;
315+
pt->pin = pin_size_t(-1);
316+
} else {
317+
if (pin != pin_size_t(-1)) {
318+
gpio_pin_toggle_dt(&arduino_pins[pin]);
319+
}
320+
pt->count--;
321+
}
322+
323+
k_spin_unlock(&pt->lock, key);
258324
}
259325

260326
void tone(pin_size_t pinNumber, unsigned int frequency, unsigned long duration) {
261-
struct k_timer *timer = &arduino_pin_timers[pinNumber];
262-
const struct gpio_dt_spec *spec = &arduino_pins[pinNumber];
327+
k_spinlock_key_t key;
328+
uint64_t toggles_count;
329+
struct pin_timer *pt;
263330
k_timeout_t timeout;
264331

265-
pinMode(pinNumber, OUTPUT);
332+
if (k_is_in_isr()) {
333+
return;
334+
}
335+
336+
k_mutex_lock(&timer_cfg_lock, K_FOREVER);
337+
338+
pt = find_pin_timer(pinNumber, false);
266339

267-
if (frequency == 0) {
268-
gpio_pin_set_dt(spec, 0);
340+
if (pt == nullptr) {
341+
k_mutex_unlock(&timer_cfg_lock);
269342
return;
270343
}
271344

272-
timeout = K_NSEC(NSEC_PER_SEC / (2 * frequency));
345+
if (!pt->timer_initialized) {
346+
k_timer_init(&pt->timer, tone_expiry_cb, NULL);
347+
pt->timer_initialized = true;
348+
}
349+
350+
pinMode(pinNumber, OUTPUT);
351+
k_timer_stop(&pt->timer);
352+
353+
toggles_count = ((uint64_t)duration * frequency / (MSEC_PER_SEC / TOGGLES_PER_CYCLE));
354+
if (frequency == 0 || (toggles_count == 0 && duration != 0)) {
355+
key = k_spin_lock(&pt->lock);
356+
pt->count = 0;
357+
pt->infinity = false;
358+
pt->pin = pin_size_t(-1);
359+
k_spin_unlock(&pt->lock, key);
273360

274-
k_timer_init(timer, tone_expiry_cb, NULL);
275-
k_timer_user_data_set(timer, (void *)spec);
276-
gpio_pin_set_dt(spec, 1);
277-
k_timer_start(timer, timeout, timeout);
361+
gpio_pin_set_dt(&arduino_pins[pinNumber], 0);
362+
363+
k_mutex_unlock(&timer_cfg_lock);
364+
return;
365+
}
278366

279-
if (duration > 0) {
280-
timer = &arduino_pin_timers_timeout[pinNumber];
281-
k_timer_init(timer, tone_timeout_cb, NULL);
282-
k_timer_user_data_set(timer, (void *)(uintptr_t)pinNumber);
283-
k_timer_start(timer, K_MSEC(duration), K_NO_WAIT);
367+
timeout = K_NSEC(NSEC_PER_SEC / (TOGGLES_PER_CYCLE * frequency));
368+
if (timeout.ticks == 0) {
369+
timeout.ticks = 1;
284370
}
371+
372+
key = k_spin_lock(&pt->lock);
373+
pt->infinity = (duration == 0);
374+
pt->count = min(toggles_count, UINT32_MAX);
375+
pt->pin = pinNumber;
376+
k_spin_unlock(&pt->lock, key);
377+
378+
gpio_pin_set_dt(&arduino_pins[pinNumber], 1);
379+
k_timer_start(&pt->timer, timeout, timeout);
380+
381+
k_mutex_unlock(&timer_cfg_lock);
285382
}
286383

287384
void noTone(pin_size_t pinNumber) {
288-
k_timer_stop(&arduino_pin_timers[pinNumber]);
385+
struct pin_timer *pt;
386+
k_spinlock_key_t key;
387+
388+
if (k_is_in_isr()) {
389+
return;
390+
}
391+
392+
k_mutex_lock(&timer_cfg_lock, K_FOREVER);
393+
394+
pt = find_pin_timer(pinNumber, true);
395+
396+
if (pt == nullptr) {
397+
k_mutex_unlock(&timer_cfg_lock);
398+
return;
399+
}
400+
401+
key = k_spin_lock(&pt->lock);
402+
k_timer_stop(&pt->timer);
403+
pt->count = 0;
404+
pt->infinity = false;
405+
pt->pin = pin_size_t(-1);
406+
k_spin_unlock(&pt->lock, key);
407+
289408
gpio_pin_set_dt(&arduino_pins[pinNumber], 0);
409+
410+
k_mutex_unlock(&timer_cfg_lock);
290411
}
291412

292413
unsigned long micros(void) {

0 commit comments

Comments
 (0)