From 9aecd42ef7dabdd8b293b82428a04ce41aa9f0bf Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Thu, 5 Feb 2026 09:36:29 +0900 Subject: [PATCH 1/3] cores: arduino: zephyrCommon: Correct tone() behavior with duration=0 Fixed a bug where tone() with duration=0 would stop the tone immediately instead of ringing infinitely. Signed-off-by: TOKITA Hiroshi --- cores/arduino/zephyrCommon.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cores/arduino/zephyrCommon.cpp b/cores/arduino/zephyrCommon.cpp index 92d99ff02..244aafd70 100644 --- a/cores/arduino/zephyrCommon.cpp +++ b/cores/arduino/zephyrCommon.cpp @@ -229,14 +229,12 @@ void tone_expiry_cb(struct k_timer *timer) { struct pin_timer *pt = CONTAINER_OF(timer, struct pin_timer, timer); const struct gpio_dt_spec *spec = &arduino_pins[pt->pin]; - if (pt->count == 0) { + if (pt->count == 0 && !pt->infinity) { k_timer_stop(timer); gpio_pin_set_dt(spec, 0); } else { gpio_pin_toggle_dt(spec); - if (!pt->infinity) { - pt->count--; - } + pt->count--; } } From a2173671e894aba1d9a7aa68174f979225fbdd7e Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Sat, 31 Jan 2026 16:26:29 +0900 Subject: [PATCH 2/3] zephyrCommon: Make configurable the max number of tones Allows you to change the maximum number of notes that can be played with `tone()`. Signed-off-by: TOKITA Hiroshi --- Kconfig | 7 +++ cores/arduino/zephyrCommon.cpp | 111 ++++++++++++++++++++++++++------- 2 files changed, 94 insertions(+), 24 deletions(-) diff --git a/Kconfig b/Kconfig index 20f88b8d2..4d4215905 100644 --- a/Kconfig +++ b/Kconfig @@ -34,4 +34,11 @@ config ARDUINO_ENTRY bool "Provide arduino setup and loop entry points" default y +config ARDUINO_MAX_TONES + int "Maximum number of tones that can be played simultaneously with tone()" + default -1 + help + Specify the maximum number of tones that can be played simultaneously with tone(). + If set to a negative value, the maximum number will be determined from the + system's digital pin configuration. endif diff --git a/cores/arduino/zephyrCommon.cpp b/cores/arduino/zephyrCommon.cpp index 244aafd70..6b011c078 100644 --- a/cores/arduino/zephyrCommon.cpp +++ b/cores/arduino/zephyrCommon.cpp @@ -7,6 +7,8 @@ #include #include "zephyrInternal.h" +#include + static const struct gpio_dt_spec arduino_pins[] = {DT_FOREACH_PROP_ELEM_SEP( DT_PATH(zephyr_user), digital_pin_gpios, GPIO_DT_SPEC_GET_BY_IDX, (, ))}; @@ -212,49 +214,97 @@ PinStatus digitalRead(pin_size_t pinNumber) { return (gpio_pin_get_dt(&arduino_pins[pinNumber]) == 1) ? HIGH : LOW; } -#ifndef MAX_TONE_PINS +#if CONFIG_ARDUINO_MAX_TONES < 0 #define MAX_TONE_PINS DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios) +#else +#define MAX_TONE_PINS CONFIG_ARDUINO_MAX_TONES #endif #define TOGGLES_PER_CYCLE 2ULL static struct pin_timer { struct k_timer timer; - uint32_t count; - pin_size_t pin; - bool infinity; + uint32_t count{0}; + pin_size_t pin{pin_size_t(-1)}; + bool infinity{false}; + struct k_spinlock lock; } arduino_pin_timers[MAX_TONE_PINS]; +static struct pin_timer* find_pin_timer(pin_size_t pinNumber, bool active_only) { + for (size_t i = 0; i < ARRAY_SIZE(arduino_pin_timers); i++) { + k_spinlock_key_t key = k_spin_lock(&arduino_pin_timers[i].lock); + + if (arduino_pin_timers[i].pin == pinNumber) { + k_spin_unlock(&arduino_pin_timers[i].lock, key); + return &arduino_pin_timers[i]; + } + + k_spin_unlock(&arduino_pin_timers[i].lock, key); + } + + if (active_only) { + return nullptr; + } + + for (size_t i = 0; i < ARRAY_SIZE(arduino_pin_timers); i++) { + k_spinlock_key_t key = k_spin_lock(&arduino_pin_timers[i].lock); + + if (arduino_pin_timers[i].pin == pin_size_t(-1)) { + arduino_pin_timers[i].pin = pinNumber; + k_spin_unlock(&arduino_pin_timers[i].lock, key); + return &arduino_pin_timers[i]; + } + + k_spin_unlock(&arduino_pin_timers[i].lock, key); + } + + return nullptr; +} + void tone_expiry_cb(struct k_timer *timer) { struct pin_timer *pt = CONTAINER_OF(timer, struct pin_timer, timer); - const struct gpio_dt_spec *spec = &arduino_pins[pt->pin]; + k_spinlock_key_t key = k_spin_lock(&pt->lock); + pin_size_t pin = pt->pin; if (pt->count == 0 && !pt->infinity) { + if (pin != pin_size_t(-1)) { + gpio_pin_set_dt(&arduino_pins[pin], 0); + } + k_timer_stop(timer); - gpio_pin_set_dt(spec, 0); + pt->pin = pin_size_t(-1); } else { - gpio_pin_toggle_dt(spec); + if (pin != pin_size_t(-1)) { + gpio_pin_toggle_dt(&arduino_pins[pin]); + } + pt->count--; } + + k_spin_unlock(&pt->lock, key); } void tone(pin_size_t pinNumber, unsigned int frequency, unsigned long duration) { - const struct gpio_dt_spec *spec = &arduino_pins[pinNumber]; - struct k_timer *timer; + k_spinlock_key_t key; + struct pin_timer *pt; k_timeout_t timeout; - if (pinNumber >= MAX_TONE_PINS) { + pt = find_pin_timer(pinNumber, false); + + if (pt == nullptr) { return; } - timer = &arduino_pin_timers[pinNumber].timer; - pinMode(pinNumber, OUTPUT); - k_timer_stop(&arduino_pin_timers[pinNumber].timer); + k_timer_stop(&pt->timer); if (frequency == 0) { - gpio_pin_set_dt(spec, 0); + key = k_spin_lock(&pt->lock); + pt->pin = pin_size_t(-1); + k_spin_unlock(&pt->lock, key); + + gpio_pin_set_dt(&arduino_pins[pinNumber], 0); return; } @@ -263,21 +313,34 @@ void tone(pin_size_t pinNumber, unsigned int frequency, timeout.ticks = 1; } - arduino_pin_timers[pinNumber].infinity = (duration == 0); - arduino_pin_timers[pinNumber].count = (uint64_t)duration * frequency * - (MSEC_PER_SEC / TOGGLES_PER_CYCLE); - arduino_pin_timers[pinNumber].pin = pinNumber; - k_timer_init(timer, tone_expiry_cb, NULL); + key = k_spin_lock(&pt->lock); + pt->infinity = (duration == 0); + pt->count = min((uint64_t)duration * frequency * TOGGLES_PER_CYCLE / MSEC_PER_SEC, UINT32_MAX); + pt->pin = pinNumber; + k_spin_unlock(&pt->lock, key); + + k_timer_init(&pt->timer, tone_expiry_cb, NULL); - gpio_pin_set_dt(spec, 0); - k_timer_start(timer, timeout, timeout); + gpio_pin_set_dt(&arduino_pins[pinNumber], 0); + k_timer_start(&pt->timer, timeout, timeout); } void noTone(pin_size_t pinNumber) { - const struct gpio_dt_spec *spec = &arduino_pins[pinNumber]; + struct pin_timer *pt; + k_spinlock_key_t key; + + pt = find_pin_timer(pinNumber, true); + + if (pt == nullptr) { + return; + } + + key = k_spin_lock(&pt->lock); + k_timer_stop(&pt->timer); + pt->pin = pin_size_t(-1); + k_spin_unlock(&pt->lock, key); - k_timer_stop(&arduino_pin_timers[pinNumber].timer); - gpio_pin_set_dt(spec, 0); + gpio_pin_set_dt(&arduino_pins[pinNumber], 0); } void delay(unsigned long ms) { From 033ed294cc4e659154bd3d03dfc6684efec04647 Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Thu, 5 Feb 2026 13:07:29 +0900 Subject: [PATCH 3/3] samples: Added tone_doremi sample Add a tone_doremi sample to demonstrate how to sounding with tone API Signed-off-by: TOKITA Hiroshi --- samples/tone_doremi/CMakeLists.txt | 29 ++++++++++++++++++++ samples/tone_doremi/README.rst | 21 ++++++++++++++ samples/tone_doremi/prj.conf | 2 ++ samples/tone_doremi/src/app.cpp | 44 ++++++++++++++++++++++++++++++ 4 files changed, 96 insertions(+) create mode 100644 samples/tone_doremi/CMakeLists.txt create mode 100644 samples/tone_doremi/README.rst create mode 100644 samples/tone_doremi/prj.conf create mode 100644 samples/tone_doremi/src/app.cpp diff --git a/samples/tone_doremi/CMakeLists.txt b/samples/tone_doremi/CMakeLists.txt new file mode 100644 index 000000000..15a1857f1 --- /dev/null +++ b/samples/tone_doremi/CMakeLists.txt @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +# get value of NORMALIZED_BOARD_TARGET early +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE} COMPONENTS yaml boards) + +if (IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../variants/${NORMALIZED_BOARD_TARGET}_${BOARD_REVISION}) + set(variant_dir ${CMAKE_CURRENT_SOURCE_DIR}/../../variants/${NORMALIZED_BOARD_TARGET}_${BOARD_REVISION}) +elseif (IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../variants/${NORMALIZED_BOARD_TARGET}) + set(variant_dir ${CMAKE_CURRENT_SOURCE_DIR}/../../variants/${NORMALIZED_BOARD_TARGET}) +elseif (IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../variants/${BOARD}) + set(variant_dir ${CMAKE_CURRENT_SOURCE_DIR}/../../variants/${BOARD}) +endif() + +if (EXISTS ${variant_dir}/${NORMALIZED_BOARD_TARGET}_${BOARD_REVISION}.overlay) + set(DTC_OVERLAY_FILE ${variant_dir}/${NORMALIZED_BOARD_TARGET}_${BOARD_REVISION}.overlay) +elseif (EXISTS ${variant_dir}/${NORMALIZED_BOARD_TARGET}.overlay) + set(DTC_OVERLAY_FILE ${variant_dir}/${NORMALIZED_BOARD_TARGET}.overlay) +elseif (EXISTS ${variant_dir}/${BOARD}.overlay) + set(DTC_OVERLAY_FILE ${variant_dir}/${BOARD}.overlay) +endif() + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(tone_doremi) + +target_sources(app PRIVATE src/app.cpp) + +zephyr_compile_options(-Wno-unused-variable -Wno-comment) diff --git a/samples/tone_doremi/README.rst b/samples/tone_doremi/README.rst new file mode 100644 index 000000000..3fccd00e7 --- /dev/null +++ b/samples/tone_doremi/README.rst @@ -0,0 +1,21 @@ +.. _tone_doremi: + +Tone DoReMi +########### + +Overview +******** + +Use tone to play the scale. +Play Do, Re, Mi on D4 pin, then Fa, So, La on D5 pin. + +Building and Running +******************** + +Build and flash tone_doremi sample as follows, + +.. code-block:: sh + + $> west build -p -b arduino_nano_33_ble samples/tone_doremi/ + + $> west flash --bossac=/home/$USER/.arduino15/packages/arduino/tools/bossac/1.9.1-arduino2/bossac diff --git a/samples/tone_doremi/prj.conf b/samples/tone_doremi/prj.conf new file mode 100644 index 000000000..ef150fb54 --- /dev/null +++ b/samples/tone_doremi/prj.conf @@ -0,0 +1,2 @@ +CONFIG_ARDUINO_API=y +CONFIG_ARDUINO_MAX_TONES=2 diff --git a/samples/tone_doremi/src/app.cpp b/samples/tone_doremi/src/app.cpp new file mode 100644 index 000000000..8ef6f26bb --- /dev/null +++ b/samples/tone_doremi/src/app.cpp @@ -0,0 +1,44 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2026 TOKITA Hiroshi + */ + +#include +#include "zephyrSerial.h" + +#define NOTE_C4 262 +#define NOTE_D4 294 +#define NOTE_E4 330 +#define NOTE_F4 349 +#define NOTE_G4 392 +#define NOTE_A4 440 + +void setup() { + Serial.begin(115200); +} + +void loop() { + Serial.println("Do@D4"); + tone(D4, NOTE_C4, 1000); + delay(2000); + Serial.println("Re@D4"); + tone(D4, NOTE_D4, 1000); + delay(2000); + Serial.println("Mi@D4 - Infinity"); + tone(D4, NOTE_E4, 0); + delay(2000); + Serial.println("Fa@D5"); + tone(D5, NOTE_F4, 1000); + delay(2000); + Serial.println("So@D5"); + tone(D5, NOTE_G4, 1000); + delay(2000); + Serial.println("La@D5 - Infinity"); + tone(D5, NOTE_A4, 0); + + while(true) { + delay(1000); + } +} +