Skip to content

Commit 05356da

Browse files
Fix PWM frequency at low and high frequencies (#244)
The PWM HW can only divide 125MHZ sysclk by 1...256. That's only down to about 500khz. If the max count of the PWM (analogWriteScale) is too low then the actual PWM period will be much less than desired (and PWM frequency of course much higher). For example, at analogWriteFreq(100); analogWriteScale(256); the true PWM period would be 125M / 256 (pwmdiv) / 256 (scale) = ~2khz. Conversely, at high frequencies and large scales it is impossible to achieve the requested frequency and a much lower one would be generated. For example: freq(60K), scale(32768). PWM period = 125M / 1 (pwmdiv) / 32768 = ~4kHz. Avoid this by adjusting the analogWrite scale in the core to either increase the PWM count for low frequencies, or decrease it for high frequencies. This is done behind the scenes and code is not required to be changed. The PWM frequency will still not be perfcetly exact due to the divider HW and clocks involved, but it will be very close across the whole range. Fixes #234
1 parent 47a4d9f commit 05356da

File tree

1 file changed

+20
-0
lines changed

1 file changed

+20
-0
lines changed

cores/rp2040/wiring_analog.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ static uint32_t analogScale = 255;
3030
static uint16_t analogFreq = 1000;
3131
static bool pwmInitted = false;
3232
static bool adcInitted = false;
33+
static uint16_t analogWritePseudoScale = 1;
34+
static uint16_t analogWriteSlowScale = 1;
3335

3436
auto_init_mutex(_dacMutex);
3537

@@ -77,6 +79,21 @@ extern "C" void analogWrite(pin_size_t pin, int val) {
7779
return;
7880
}
7981
if (!pwmInitted) {
82+
// For low frequencies, we need to scale the output max value up to achieve lower periods
83+
analogWritePseudoScale = 1;
84+
while (((clock_get_hz(clk_sys) / (float)(analogScale * analogFreq)) > 255.0) && (analogScale < 32678)) {
85+
analogWritePseudoScale++;
86+
analogScale *= 2;
87+
DEBUGCORE("Adjusting analogWrite values PS=%d, scale=%d\n", analogWritePseudoScale, analogScale);
88+
}
89+
// For high frequencies, we need to scale the output max value down to actually hit the frequency target
90+
analogWriteSlowScale = 1;
91+
while (((clock_get_hz(clk_sys) / (float)(analogScale * analogFreq)) < 2.0) && (analogScale > 32)) {
92+
analogWriteSlowScale++;
93+
analogScale /= 2;
94+
DEBUGCORE("Adjusting analogWrite values SS=%d, scale=%d\n", analogWriteSlowScale, analogScale);
95+
}
96+
8097
pwm_config c = pwm_get_default_config();
8198
pwm_config_set_clkdiv(&c, clock_get_hz(clk_sys) / (float)(analogScale * analogFreq));
8299
pwm_config_set_wrap(&c, analogScale);
@@ -86,6 +103,9 @@ extern "C" void analogWrite(pin_size_t pin, int val) {
86103
pwmInitted = true;
87104
}
88105

106+
val <<= analogWritePseudoScale;
107+
val >>= analogWriteSlowScale;
108+
89109
if (val < 0) {
90110
val = 0;
91111
} else if ((uint32_t)val > analogScale) {

0 commit comments

Comments
 (0)