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) }