From 6399df1f4a6a71a0f3f5961afbee767f32004023 Mon Sep 17 00:00:00 2001 From: Jakub <106778416+MarekZegare4@users.noreply.github.com> Date: Wed, 20 May 2026 09:06:00 +0200 Subject: [PATCH] fix: improve buzzer volume control on nRF52 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use inverted PWM polarity (0x8000) with PCT {2,5,12,25,50} giving ~6-8 dB perceptual steps (-24/-16/-9/-3/0 dB) vs the original uneven steps where levels 4 and 5 were only 1 dB apart - Guard against cmp=0 truncation in both _nrfStartPwm and applyVolume: at level 1 with high notes (freq > ~2.5 kHz) integer math gives cmp=0 which with inverted polarity means 100% HIGH → complete silence - Change volume preview note from C5 (523 Hz) to C6 (1047 Hz) to better match the piezo resonant frequency range and actual notification sounds Co-Authored-By: Claude Sonnet 4.6 --- examples/companion_radio/ui-new/UITask.cpp | 2 +- src/helpers/ui/buzzer.cpp | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 456ebc1d..40e12c1c 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -1560,7 +1560,7 @@ void UITask::setBuzzerVolumeLevel(uint8_t level) { if (level > 4) level = 4; _node_prefs->buzzer_volume = level; buzzer.setVolume(level); - if (level > 0) buzzer.playForced("Vol:d=16,o=5,b=120:c"); + if (level > 0) buzzer.playForced("Vol:d=16,o=6,b=120:c"); _next_refresh = 0; #endif } diff --git a/src/helpers/ui/buzzer.cpp b/src/helpers/ui/buzzer.cpp index d35a3428..12cb515a 100644 --- a/src/helpers/ui/buzzer.cpp +++ b/src/helpers/ui/buzzer.cpp @@ -86,7 +86,9 @@ bool genericBuzzer::_parseNext(const char*& p, uint8_t def_dur, uint8_t def_oct, } uint8_t genericBuzzer::_dutyPct() const { - static const uint8_t PCT[5] = { 2, 8, 20, 35, 50 }; + // Inverted polarity (0x8000 bit): duty_HIGH = 100% - PCT. + // Values chosen for ~6-8 dB perceptual steps: -24/-16/-9/-3/0 dB. + static const uint8_t PCT[5] = { 2, 5, 12, 25, 50 }; return PCT[_volume_level < 5 ? _volume_level : 4]; } @@ -96,6 +98,7 @@ void genericBuzzer::_nrfStartPwm(uint16_t freq) { uint32_t nrf_pin = g_ADigitalPinMap[PIN_BUZZER]; uint16_t top = 125000 / freq; uint16_t cmp = (uint16_t)(((uint32_t)top * _dutyPct()) / 100); + if (cmp == 0) cmp = 1; // inverted polarity: cmp=0 → 100% HIGH → no AC → silence // Write duty BEFORE SEQSTART so DMA reads our value on the very first period _duty_buf = 0x8000U | cmp; @@ -168,6 +171,7 @@ void genericBuzzer::applyVolume() { if (!_pwm_on) return; uint16_t top = (uint16_t)NRF_PWM2->COUNTERTOP; uint16_t cmp = (uint16_t)(((uint32_t)top * _dutyPct()) / 100); + if (cmp == 0) cmp = 1; _duty_buf = 0x8000U | cmp; // DMA picks this up within one period (< 2.3 ms at A4) }