Skip to content

Commit c5f4d17

Browse files
committed
zephyrCommon: Improved tone implementation
Improved to align more closely with the standard Arduino implementation. - Eliminated the end timer and used counting to determine the end. - Improved to handle infinity correctly. - Added a guard to prevent the timeout value from reaching 0. - Set the GPIO value to 0 when the timer starts. - Make configurable the max number of tones Signed-off-by: TOKITA Hiroshi <tokita.hiroshi@gmail.com>
1 parent 7505975 commit c5f4d17

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
static const struct gpio_dt_spec arduino_pins[] = {
1113
DT_FOREACH_PROP_ELEM_SEP(
1214
DT_PATH(zephyr_user), digital_pin_gpios, GPIO_DT_SPEC_GET_BY_IDX, (, ))};
@@ -230,49 +232,168 @@ PinStatus digitalRead(pin_size_t pinNumber) {
230232
return (gpio_pin_get_dt(&arduino_pins[pinNumber]) == 1) ? HIGH : LOW;
231233
}
232234

233-
struct k_timer arduino_pin_timers[ARRAY_SIZE(arduino_pins)];
234-
struct k_timer arduino_pin_timers_timeout[ARRAY_SIZE(arduino_pins)];
235+
#if CONFIG_ARDUINO_MAX_TONES < 0
236+
#define MAX_TONE_PINS DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios)
237+
#else
238+
#define MAX_TONE_PINS CONFIG_ARDUINO_MAX_TONES
239+
#endif
235240

236-
void tone_expiry_cb(struct k_timer *timer) {
237-
const struct gpio_dt_spec *spec = (gpio_dt_spec *)k_timer_user_data_get(timer);
238-
gpio_pin_toggle_dt(spec);
241+
#define TOGGLES_PER_CYCLE 2ULL
242+
243+
static struct pin_timer {
244+
struct k_timer timer;
245+
uint32_t count{0};
246+
pin_size_t pin{pin_size_t(-1)};
247+
bool infinity{false};
248+
bool timer_initialized{false};
249+
struct k_spinlock lock{};
250+
} arduino_pin_timers[MAX_TONE_PINS];
251+
252+
K_MUTEX_DEFINE(timer_cfg_lock);
253+
254+
void tone_expiry_cb(struct k_timer *timer);
255+
256+
/* Callers must hold timer_cfg_lock while using this helper. */
257+
static struct pin_timer *find_pin_timer(pin_size_t pinNumber, bool active_only) {
258+
for (size_t i = 0; i < ARRAY_SIZE(arduino_pin_timers); i++) {
259+
k_spinlock_key_t key = k_spin_lock(&arduino_pin_timers[i].lock);
260+
261+
if (arduino_pin_timers[i].pin == pinNumber) {
262+
k_spin_unlock(&arduino_pin_timers[i].lock, key);
263+
return &arduino_pin_timers[i];
264+
}
265+
266+
k_spin_unlock(&arduino_pin_timers[i].lock, key);
267+
}
268+
269+
if (active_only) {
270+
return nullptr;
271+
}
272+
273+
for (size_t i = 0; i < ARRAY_SIZE(arduino_pin_timers); i++) {
274+
k_spinlock_key_t key = k_spin_lock(&arduino_pin_timers[i].lock);
275+
276+
if (arduino_pin_timers[i].pin == pin_size_t(-1)) {
277+
arduino_pin_timers[i].pin = pinNumber;
278+
k_spin_unlock(&arduino_pin_timers[i].lock, key);
279+
return &arduino_pin_timers[i];
280+
}
281+
282+
k_spin_unlock(&arduino_pin_timers[i].lock, key);
283+
}
284+
285+
return nullptr;
239286
}
240287

241-
void tone_timeout_cb(struct k_timer *timer) {
242-
pin_size_t pinNumber = (pin_size_t)(uintptr_t)k_timer_user_data_get(timer);
243-
noTone(pinNumber);
288+
void tone_expiry_cb(struct k_timer *timer) {
289+
struct pin_timer *pt = CONTAINER_OF(timer, struct pin_timer, timer);
290+
k_spinlock_key_t key = k_spin_lock(&pt->lock);
291+
pin_size_t pin = pt->pin;
292+
293+
if (pt->count == 0 && !pt->infinity) {
294+
if (pin != pin_size_t(-1)) {
295+
gpio_pin_set_dt(&arduino_pins[pin], 0);
296+
}
297+
298+
k_timer_stop(timer);
299+
pt->count = 0;
300+
pt->infinity = false;
301+
pt->pin = pin_size_t(-1);
302+
} else {
303+
if (pin != pin_size_t(-1)) {
304+
gpio_pin_toggle_dt(&arduino_pins[pin]);
305+
}
306+
pt->count--;
307+
}
308+
309+
k_spin_unlock(&pt->lock, key);
244310
}
245311

246312
void tone(pin_size_t pinNumber, unsigned int frequency, unsigned long duration) {
247-
struct k_timer *timer = &arduino_pin_timers[pinNumber];
248-
const struct gpio_dt_spec *spec = &arduino_pins[pinNumber];
313+
k_spinlock_key_t key;
314+
uint64_t toggles_count;
315+
struct pin_timer *pt;
249316
k_timeout_t timeout;
250317

251-
pinMode(pinNumber, OUTPUT);
318+
if (k_is_in_isr()) {
319+
return;
320+
}
321+
322+
k_mutex_lock(&timer_cfg_lock, K_FOREVER);
323+
324+
pt = find_pin_timer(pinNumber, false);
252325

253-
if (frequency == 0) {
254-
gpio_pin_set_dt(spec, 0);
326+
if (pt == nullptr) {
327+
k_mutex_unlock(&timer_cfg_lock);
255328
return;
256329
}
257330

258-
timeout = K_NSEC(NSEC_PER_SEC / (2 * frequency));
331+
if (!pt->timer_initialized) {
332+
k_timer_init(&pt->timer, tone_expiry_cb, NULL);
333+
pt->timer_initialized = true;
334+
}
335+
336+
pinMode(pinNumber, OUTPUT);
337+
k_timer_stop(&pt->timer);
338+
339+
toggles_count = ((uint64_t)duration * frequency / (MSEC_PER_SEC / TOGGLES_PER_CYCLE));
340+
if (frequency == 0 || (toggles_count == 0 && duration != 0)) {
341+
key = k_spin_lock(&pt->lock);
342+
pt->count = 0;
343+
pt->infinity = false;
344+
pt->pin = pin_size_t(-1);
345+
k_spin_unlock(&pt->lock, key);
259346

260-
k_timer_init(timer, tone_expiry_cb, NULL);
261-
k_timer_user_data_set(timer, (void *)spec);
262-
gpio_pin_set_dt(spec, 1);
263-
k_timer_start(timer, timeout, timeout);
347+
gpio_pin_set_dt(&arduino_pins[pinNumber], 0);
348+
349+
k_mutex_unlock(&timer_cfg_lock);
350+
return;
351+
}
264352

265-
if (duration > 0) {
266-
timer = &arduino_pin_timers_timeout[pinNumber];
267-
k_timer_init(timer, tone_timeout_cb, NULL);
268-
k_timer_user_data_set(timer, (void *)(uintptr_t)pinNumber);
269-
k_timer_start(timer, K_MSEC(duration), K_NO_WAIT);
353+
timeout = K_NSEC(NSEC_PER_SEC / (TOGGLES_PER_CYCLE * frequency));
354+
if (timeout.ticks == 0) {
355+
timeout.ticks = 1;
270356
}
357+
358+
key = k_spin_lock(&pt->lock);
359+
pt->infinity = (duration == 0);
360+
pt->count = min(toggles_count, UINT32_MAX);
361+
pt->pin = pinNumber;
362+
k_spin_unlock(&pt->lock, key);
363+
364+
gpio_pin_set_dt(&arduino_pins[pinNumber], 1);
365+
k_timer_start(&pt->timer, timeout, timeout);
366+
367+
k_mutex_unlock(&timer_cfg_lock);
271368
}
272369

273370
void noTone(pin_size_t pinNumber) {
274-
k_timer_stop(&arduino_pin_timers[pinNumber]);
371+
struct pin_timer *pt;
372+
k_spinlock_key_t key;
373+
374+
if (k_is_in_isr()) {
375+
return;
376+
}
377+
378+
k_mutex_lock(&timer_cfg_lock, K_FOREVER);
379+
380+
pt = find_pin_timer(pinNumber, true);
381+
382+
if (pt == nullptr) {
383+
k_mutex_unlock(&timer_cfg_lock);
384+
return;
385+
}
386+
387+
key = k_spin_lock(&pt->lock);
388+
k_timer_stop(&pt->timer);
389+
pt->count = 0;
390+
pt->infinity = false;
391+
pt->pin = pin_size_t(-1);
392+
k_spin_unlock(&pt->lock, key);
393+
275394
gpio_pin_set_dt(&arduino_pins[pinNumber], 0);
395+
396+
k_mutex_unlock(&timer_cfg_lock);
276397
}
277398

278399
void delay(unsigned long ms) {

0 commit comments

Comments
 (0)