|
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}; |
@@ -244,49 +246,168 @@ PinStatus digitalRead(pin_size_t pinNumber) { |
244 | 246 | return (gpio_pin_get_dt(&arduino_pins[pinNumber]) == 1) ? HIGH : LOW; |
245 | 247 | } |
246 | 248 |
|
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 |
249 | 254 |
|
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; |
253 | 300 | } |
254 | 301 |
|
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); |
258 | 324 | } |
259 | 325 |
|
260 | 326 | 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; |
263 | 330 | k_timeout_t timeout; |
264 | 331 |
|
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); |
266 | 339 |
|
267 | | - if (frequency == 0) { |
268 | | - gpio_pin_set_dt(spec, 0); |
| 340 | + if (pt == nullptr) { |
| 341 | + k_mutex_unlock(&timer_cfg_lock); |
269 | 342 | return; |
270 | 343 | } |
271 | 344 |
|
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); |
273 | 360 |
|
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 | + } |
278 | 366 |
|
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; |
284 | 370 | } |
| 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); |
285 | 382 | } |
286 | 383 |
|
287 | 384 | 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 | + |
289 | 408 | gpio_pin_set_dt(&arduino_pins[pinNumber], 0); |
| 409 | + |
| 410 | + k_mutex_unlock(&timer_cfg_lock); |
290 | 411 | } |
291 | 412 |
|
292 | 413 | unsigned long micros(void) { |
|
0 commit comments