Unverified Commit 05356da2 authored by Earle F. Philhower, III's avatar Earle F. Philhower, III Committed by GitHub

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
parent 47a4d9f8
......@@ -30,6 +30,8 @@ static uint32_t analogScale = 255;
static uint16_t analogFreq = 1000;
static bool pwmInitted = false;
static bool adcInitted = false;
static uint16_t analogWritePseudoScale = 1;
static uint16_t analogWriteSlowScale = 1;
auto_init_mutex(_dacMutex);
......@@ -77,6 +79,21 @@ extern "C" void analogWrite(pin_size_t pin, int val) {
return;
}
if (!pwmInitted) {
// For low frequencies, we need to scale the output max value up to achieve lower periods
analogWritePseudoScale = 1;
while (((clock_get_hz(clk_sys) / (float)(analogScale * analogFreq)) > 255.0) && (analogScale < 32678)) {
analogWritePseudoScale++;
analogScale *= 2;
DEBUGCORE("Adjusting analogWrite values PS=%d, scale=%d\n", analogWritePseudoScale, analogScale);
}
// For high frequencies, we need to scale the output max value down to actually hit the frequency target
analogWriteSlowScale = 1;
while (((clock_get_hz(clk_sys) / (float)(analogScale * analogFreq)) < 2.0) && (analogScale > 32)) {
analogWriteSlowScale++;
analogScale /= 2;
DEBUGCORE("Adjusting analogWrite values SS=%d, scale=%d\n", analogWriteSlowScale, analogScale);
}
pwm_config c = pwm_get_default_config();
pwm_config_set_clkdiv(&c, clock_get_hz(clk_sys) / (float)(analogScale * analogFreq));
pwm_config_set_wrap(&c, analogScale);
......@@ -86,6 +103,9 @@ extern "C" void analogWrite(pin_size_t pin, int val) {
pwmInitted = true;
}
val <<= analogWritePseudoScale;
val >>= analogWriteSlowScale;
if (val < 0) {
val = 0;
} else if ((uint32_t)val > analogScale) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment