fix: improve buzzer volume control on nRF52

- 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 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-05-20 09:06:00 +02:00
parent 31e91bda21
commit 6399df1f4a
2 changed files with 6 additions and 2 deletions

View File

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

View File

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