|
7 | 7 | #include <Arduino.h> |
8 | 8 | #include "zephyrInternal.h" |
9 | 9 |
|
| 10 | +#include <zephyr/spinlock.h> |
| 11 | + |
10 | 12 | // create an array of arduino_pins with functions to reinitialize pins if needed |
11 | 13 | static const struct device *pinmux_array[DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios)] = { |
12 | 14 | nullptr}; |
@@ -263,53 +265,172 @@ PinStatus digitalRead(pin_size_t pinNumber) { |
263 | 265 | return (gpio_pin_get_dt(&arduino_pins[pinNumber]) == 1) ? HIGH : LOW; |
264 | 266 | } |
265 | 267 |
|
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 |
268 | 273 |
|
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; |
272 | 319 | } |
273 | 320 |
|
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); |
277 | 343 | } |
278 | 344 |
|
279 | 345 | void tone(pin_size_t pinNumber, unsigned int frequency, unsigned long duration) { |
280 | 346 | RETURN_ON_INVALID_PIN(pinNumber); |
281 | 347 |
|
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; |
284 | 351 | k_timeout_t timeout; |
285 | 352 |
|
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); |
287 | 360 |
|
288 | | - if (frequency == 0) { |
289 | | - gpio_pin_set_dt(spec, 0); |
| 361 | + if (pt == nullptr) { |
| 362 | + k_mutex_unlock(&timer_cfg_lock); |
290 | 363 | return; |
291 | 364 | } |
292 | 365 |
|
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); |
294 | 381 |
|
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 | + } |
299 | 387 |
|
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; |
305 | 391 | } |
| 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); |
306 | 403 | } |
307 | 404 |
|
308 | 405 | void noTone(pin_size_t pinNumber) { |
309 | 406 | RETURN_ON_INVALID_PIN(pinNumber); |
310 | 407 |
|
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 | + |
312 | 431 | gpio_pin_set_dt(&arduino_pins[pinNumber], 0); |
| 432 | + |
| 433 | + k_mutex_unlock(&timer_cfg_lock); |
313 | 434 | } |
314 | 435 |
|
315 | 436 | unsigned long micros(void) { |
|
0 commit comments