|
7 | 7 | #include <Arduino.h> |
8 | 8 | #include "zephyrInternal.h" |
9 | 9 |
|
| 10 | +#include <zephyr/spinlock.h> |
| 11 | + |
10 | 12 | static const struct gpio_dt_spec arduino_pins[] = { |
11 | 13 | DT_FOREACH_PROP_ELEM_SEP( |
12 | 14 | DT_PATH(zephyr_user), digital_pin_gpios, GPIO_DT_SPEC_GET_BY_IDX, (, ))}; |
@@ -230,49 +232,168 @@ PinStatus digitalRead(pin_size_t pinNumber) { |
230 | 232 | return (gpio_pin_get_dt(&arduino_pins[pinNumber]) == 1) ? HIGH : LOW; |
231 | 233 | } |
232 | 234 |
|
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 |
235 | 240 |
|
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; |
239 | 286 | } |
240 | 287 |
|
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); |
244 | 310 | } |
245 | 311 |
|
246 | 312 | 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; |
249 | 316 | k_timeout_t timeout; |
250 | 317 |
|
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); |
252 | 325 |
|
253 | | - if (frequency == 0) { |
254 | | - gpio_pin_set_dt(spec, 0); |
| 326 | + if (pt == nullptr) { |
| 327 | + k_mutex_unlock(&timer_cfg_lock); |
255 | 328 | return; |
256 | 329 | } |
257 | 330 |
|
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); |
259 | 346 |
|
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 | + } |
264 | 352 |
|
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; |
270 | 356 | } |
| 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); |
271 | 368 | } |
272 | 369 |
|
273 | 370 | 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 | + |
275 | 394 | gpio_pin_set_dt(&arduino_pins[pinNumber], 0); |
| 395 | + |
| 396 | + k_mutex_unlock(&timer_cfg_lock); |
276 | 397 | } |
277 | 398 |
|
278 | 399 | void delay(unsigned long ms) { |
|
0 commit comments