Skip to content

Commit 8f812a8

Browse files
authored
Merge pull request #340 from soburi/improve_tone
cores: arduino: zephyrCommon: rework tone slot handling and timer lifecycle
2 parents 85a2cfe + 351b287 commit 8f812a8

2 files changed

Lines changed: 153 additions & 24 deletions

File tree

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};
@@ -263,53 +265,172 @@ PinStatus digitalRead(pin_size_t pinNumber) {
263265
return (gpio_pin_get_dt(&arduino_pins[pinNumber]) == 1) ? HIGH : LOW;
264266
}
265267

266-
struct k_timer arduino_pin_timers[ARRAY_SIZE(arduino_pins)];
267-
struct k_timer arduino_pin_timers_timeout[ARRAY_SIZE(arduino_pins)];
268+
#if CONFIG_ARDUINO_MAX_TONES < 0
269+
#define MAX_TONE_PINS DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios)
270+
#else
271+
#define MAX_TONE_PINS CONFIG_ARDUINO_MAX_TONES
272+
#endif
268273

269-
void tone_expiry_cb(struct k_timer *timer) {
270-
const struct gpio_dt_spec *spec = (gpio_dt_spec *)k_timer_user_data_get(timer);
271-
gpio_pin_toggle_dt(spec);
274+
#define TOGGLES_PER_CYCLE 2ULL
275+
276+
static struct pin_timer {
277+
struct k_timer timer;
278+
uint32_t count{0};
279+
pin_size_t pin{pin_size_t(-1)};
280+
bool infinity{false};
281+
bool timer_initialized{false};
282+
struct k_spinlock lock{};
283+
} arduino_pin_timers[MAX_TONE_PINS];
284+
285+
K_MUTEX_DEFINE(timer_cfg_lock);
286+
287+
void tone_expiry_cb(struct k_timer *timer);
288+
289+
/* Callers must hold timer_cfg_lock while using this helper. */
290+
static struct pin_timer *find_pin_timer(pin_size_t pinNumber, bool active_only) {
291+
for (size_t i = 0; i < ARRAY_SIZE(arduino_pin_timers); i++) {
292+
k_spinlock_key_t key = k_spin_lock(&arduino_pin_timers[i].lock);
293+
294+
if (arduino_pin_timers[i].pin == pinNumber) {
295+
k_spin_unlock(&arduino_pin_timers[i].lock, key);
296+
return &arduino_pin_timers[i];
297+
}
298+
299+
k_spin_unlock(&arduino_pin_timers[i].lock, key);
300+
}
301+
302+
if (active_only) {
303+
return nullptr;
304+
}
305+
306+
for (size_t i = 0; i < ARRAY_SIZE(arduino_pin_timers); i++) {
307+
k_spinlock_key_t key = k_spin_lock(&arduino_pin_timers[i].lock);
308+
309+
if (arduino_pin_timers[i].pin == pin_size_t(-1)) {
310+
arduino_pin_timers[i].pin = pinNumber;
311+
k_spin_unlock(&arduino_pin_timers[i].lock, key);
312+
return &arduino_pin_timers[i];
313+
}
314+
315+
k_spin_unlock(&arduino_pin_timers[i].lock, key);
316+
}
317+
318+
return nullptr;
272319
}
273320

274-
void tone_timeout_cb(struct k_timer *timer) {
275-
pin_size_t pinNumber = (pin_size_t)(uintptr_t)k_timer_user_data_get(timer);
276-
noTone(pinNumber);
321+
void tone_expiry_cb(struct k_timer *timer) {
322+
struct pin_timer *pt = CONTAINER_OF(timer, struct pin_timer, timer);
323+
k_spinlock_key_t key = k_spin_lock(&pt->lock);
324+
pin_size_t pin = pt->pin;
325+
326+
if (pt->count == 0 && !pt->infinity) {
327+
if (pin != pin_size_t(-1)) {
328+
gpio_pin_set_dt(&arduino_pins[pin], 0);
329+
}
330+
331+
k_timer_stop(timer);
332+
pt->count = 0;
333+
pt->infinity = false;
334+
pt->pin = pin_size_t(-1);
335+
} else {
336+
if (pin != pin_size_t(-1)) {
337+
gpio_pin_toggle_dt(&arduino_pins[pin]);
338+
}
339+
pt->count--;
340+
}
341+
342+
k_spin_unlock(&pt->lock, key);
277343
}
278344

279345
void tone(pin_size_t pinNumber, unsigned int frequency, unsigned long duration) {
280346
RETURN_ON_INVALID_PIN(pinNumber);
281347

282-
struct k_timer *timer = &arduino_pin_timers[pinNumber];
283-
const struct gpio_dt_spec *spec = &arduino_pins[pinNumber];
348+
k_spinlock_key_t key;
349+
uint64_t toggles_count;
350+
struct pin_timer *pt;
284351
k_timeout_t timeout;
285352

286-
pinMode(pinNumber, OUTPUT);
353+
if (k_is_in_isr()) {
354+
return;
355+
}
356+
357+
k_mutex_lock(&timer_cfg_lock, K_FOREVER);
358+
359+
pt = find_pin_timer(pinNumber, false);
287360

288-
if (frequency == 0) {
289-
gpio_pin_set_dt(spec, 0);
361+
if (pt == nullptr) {
362+
k_mutex_unlock(&timer_cfg_lock);
290363
return;
291364
}
292365

293-
timeout = K_NSEC(NSEC_PER_SEC / (2 * frequency));
366+
if (!pt->timer_initialized) {
367+
k_timer_init(&pt->timer, tone_expiry_cb, NULL);
368+
pt->timer_initialized = true;
369+
}
370+
371+
pinMode(pinNumber, OUTPUT);
372+
k_timer_stop(&pt->timer);
373+
374+
toggles_count = ((uint64_t)duration * frequency / (MSEC_PER_SEC / TOGGLES_PER_CYCLE));
375+
if (frequency == 0 || (toggles_count == 0 && duration != 0)) {
376+
key = k_spin_lock(&pt->lock);
377+
pt->count = 0;
378+
pt->infinity = false;
379+
pt->pin = pin_size_t(-1);
380+
k_spin_unlock(&pt->lock, key);
294381

295-
k_timer_init(timer, tone_expiry_cb, NULL);
296-
k_timer_user_data_set(timer, (void *)spec);
297-
gpio_pin_set_dt(spec, 1);
298-
k_timer_start(timer, timeout, timeout);
382+
gpio_pin_set_dt(&arduino_pins[pinNumber], 0);
383+
384+
k_mutex_unlock(&timer_cfg_lock);
385+
return;
386+
}
299387

300-
if (duration > 0) {
301-
timer = &arduino_pin_timers_timeout[pinNumber];
302-
k_timer_init(timer, tone_timeout_cb, NULL);
303-
k_timer_user_data_set(timer, (void *)(uintptr_t)pinNumber);
304-
k_timer_start(timer, K_MSEC(duration), K_NO_WAIT);
388+
timeout = K_NSEC(NSEC_PER_SEC / (TOGGLES_PER_CYCLE * frequency));
389+
if (timeout.ticks == 0) {
390+
timeout.ticks = 1;
305391
}
392+
393+
key = k_spin_lock(&pt->lock);
394+
pt->infinity = (duration == 0);
395+
pt->count = min(toggles_count, UINT32_MAX);
396+
pt->pin = pinNumber;
397+
k_spin_unlock(&pt->lock, key);
398+
399+
gpio_pin_set_dt(&arduino_pins[pinNumber], 1);
400+
k_timer_start(&pt->timer, timeout, timeout);
401+
402+
k_mutex_unlock(&timer_cfg_lock);
306403
}
307404

308405
void noTone(pin_size_t pinNumber) {
309406
RETURN_ON_INVALID_PIN(pinNumber);
310407

311-
k_timer_stop(&arduino_pin_timers[pinNumber]);
408+
struct pin_timer *pt;
409+
k_spinlock_key_t key;
410+
411+
if (k_is_in_isr()) {
412+
return;
413+
}
414+
415+
k_mutex_lock(&timer_cfg_lock, K_FOREVER);
416+
417+
pt = find_pin_timer(pinNumber, true);
418+
419+
if (pt == nullptr) {
420+
k_mutex_unlock(&timer_cfg_lock);
421+
return;
422+
}
423+
424+
key = k_spin_lock(&pt->lock);
425+
k_timer_stop(&pt->timer);
426+
pt->count = 0;
427+
pt->infinity = false;
428+
pt->pin = pin_size_t(-1);
429+
k_spin_unlock(&pt->lock, key);
430+
312431
gpio_pin_set_dt(&arduino_pins[pinNumber], 0);
432+
433+
k_mutex_unlock(&timer_cfg_lock);
313434
}
314435

315436
unsigned long micros(void) {

0 commit comments

Comments
 (0)