Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Precalculate common PWMAudio dividers, avoid noise #2714

Merged
merged 1 commit into from
Dec 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions libraries/PWMAudio/src/PWMAudio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
*/
#include <Arduino.h>
#include "PWMAudio.h"
#include "PWMAudioPrecalc.h"
#include <hardware/pwm.h>


Expand Down Expand Up @@ -281,6 +282,19 @@ void PWMAudio::find_pacer_fraction(int target, uint16_t *numerator, uint16_t *de
return;
}

// See if it's one of the precalculated values
for (size_t i = 0; i < sizeof(__PWMAudio_pacer) / sizeof(__PWMAudio_pacer[0]); i++) {
if (target == (int)__PWMAudio_pacer[i].freq) {
last_target = target;
bestNum = __PWMAudio_pacer[i].n;
bestDenom = __PWMAudio_pacer[i].d;
*numerator = bestNum;
*denominator = bestDenom;
return;
}
}

// Nope, do exhaustive search. This is gonna be slooooow
float targetRatio = (float)F_CPU / target;
float lowestError = HUGE_VALF;

Expand Down
37 changes: 37 additions & 0 deletions libraries/PWMAudio/src/PWMAudioPrecalc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Generated by tools/makepacer.cpp, do not edit
typedef struct {
uint32_t freq;
uint16_t n;
uint16_t d;
} PWMPacerPrecalc;
#if F_CPU == 50000000
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 6250, 1}, {11025, 31746, 7}, {16000, 3125, 1}, {22050, 15873, 7}, {32000, 3125, 2}, {44100, 53288, 47}, {48000, 3125, 3}, {88200, 42517, 75}, {96000, 3125, 6}, {176400, 55839, 197}, {192000, 3125, 12}};
#elif F_CPU == 100000000
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 12500, 1}, {11025, 63492, 7}, {16000, 6250, 1}, {22050, 31746, 7}, {32000, 3125, 1}, {44100, 15873, 7}, {48000, 6250, 3}, {88200, 53288, 47}, {96000, 3125, 3}, {176400, 42517, 75}, {192000, 3125, 6}};
#elif F_CPU == 120000000
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 15000, 1}, {11025, 32653, 3}, {16000, 7500, 1}, {22050, 59864, 11}, {32000, 3750, 1}, {44100, 62585, 23}, {48000, 2500, 1}, {88200, 62585, 46}, {96000, 1250, 1}, {176400, 62585, 92}, {192000, 625, 1}};
#elif F_CPU == 125000000
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 15625, 1}, {11025, 56689, 5}, {16000, 15625, 2}, {22050, 62358, 11}, {32000, 15625, 4}, {44100, 42517, 15}, {48000, 15625, 6}, {88200, 42517, 30}, {96000, 15625, 12}, {176400, 42517, 60}, {192000, 15625, 24}};
#elif F_CPU == 128000000
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 16000, 1}, {11025, 11610, 1}, {16000, 8000, 1}, {22050, 5805, 1}, {32000, 4000, 1}, {44100, 5805, 2}, {48000, 8000, 3}, {88200, 5805, 4}, {96000, 4000, 3}, {176400, 61678, 85}, {192000, 2000, 3}};
#elif F_CPU == 133000000
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 16625, 1}, {11025, 24127, 2}, {16000, 16625, 2}, {22050, 24127, 4}, {32000, 16625, 4}, {44100, 24127, 8}, {48000, 16625, 6}, {88200, 24127, 16}, {96000, 16625, 12}, {176400, 47500, 63}, {192000, 16625, 24}};
#elif F_CPU == 150000000
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 18750, 1}, {11025, 27211, 2}, {16000, 9375, 1}, {22050, 47619, 7}, {32000, 9375, 2}, {44100, 37415, 11}, {48000, 3125, 1}, {88200, 42517, 25}, {96000, 3125, 2}, {176400, 42517, 50}, {192000, 3125, 4}};
#elif F_CPU == 175000000
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 21875, 1}, {11025, 15873, 1}, {16000, 21875, 2}, {22050, 15873, 2}, {32000, 21875, 4}, {44100, 15873, 4}, {48000, 21875, 6}, {88200, 15873, 8}, {96000, 21875, 12}, {176400, 62500, 63}, {192000, 21875, 24}};
#elif F_CPU == 200000000
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 25000, 1}, {11025, 54422, 3}, {16000, 12500, 1}, {22050, 63492, 7}, {32000, 6250, 1}, {44100, 31746, 7}, {48000, 12500, 3}, {88200, 15873, 7}, {96000, 6250, 3}, {176400, 53288, 47}, {192000, 3125, 3}};
#elif F_CPU == 225000000
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 28125, 1}, {11025, 20408, 1}, {16000, 28125, 2}, {22050, 10204, 1}, {32000, 28125, 4}, {44100, 5102, 1}, {48000, 9375, 2}, {88200, 63776, 25}, {96000, 9375, 4}, {176400, 62500, 49}, {192000, 9375, 8}};
#elif F_CPU == 240000000
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 30000, 1}, {11025, 65306, 3}, {16000, 15000, 1}, {22050, 32653, 3}, {32000, 7500, 1}, {44100, 59864, 11}, {48000, 5000, 1}, {88200, 62585, 23}, {96000, 2500, 1}, {176400, 62585, 46}, {192000, 1250, 1}};
#elif F_CPU == 250000000
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 31250, 1}, {11025, 45351, 2}, {16000, 15625, 1}, {22050, 56689, 5}, {32000, 15625, 2}, {44100, 62358, 11}, {48000, 15625, 3}, {88200, 42517, 15}, {96000, 15625, 6}, {176400, 42517, 30}, {192000, 15625, 12}};
#elif F_CPU == 275000000
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 34375, 1}, {11025, 49887, 2}, {16000, 34375, 2}, {22050, 37415, 3}, {32000, 34375, 4}, {44100, 37415, 6}, {48000, 34375, 6}, {88200, 37415, 12}, {96000, 34375, 12}, {176400, 35856, 23}, {192000, 34375, 24}};
#elif F_CPU == 300000000
static const PWMPacerPrecalc __PWMAudio_pacer[] = {{8000, 37500, 1}, {11025, 27211, 1}, {16000, 18750, 1}, {22050, 27211, 2}, {32000, 9375, 1}, {44100, 47619, 7}, {48000, 6250, 1}, {88200, 37415, 11}, {96000, 3125, 1}, {176400, 42517, 25}, {192000, 3125, 2}};
#else
const PWMPacerPrecalc __PWMAudio_pacer[] = {{1, 1, 1}}; // Invalid, should never match
#endif
81 changes: 81 additions & 0 deletions tools/makepacer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Generates a header with precalculated best dividers for PWMAudio at
// standard clock frequencies and audio rates.
//

#include <stdint.h>
#include <stdio.h>
#include <math.h>

void find_pacer_fraction(int F_CPU, int target, uint16_t *numerator, uint16_t *denominator) {
const uint16_t max = 0xFFFF;

/*Cache last results so we dont have to recalculate*/
static int last_target;
static uint16_t bestNum;
static uint16_t bestDenom;
/*Check if we can load the previous values*/
if (target == last_target) {
*numerator = bestNum;
*denominator = bestDenom;
return;
}

float targetRatio = (float)F_CPU / target;
float lowestError = 10000000;

for (uint16_t denom = 1; denom < max; denom++) {
uint16_t num = (int)((targetRatio * denom) + 0.5f); /*Calculate numerator, rounding to nearest integer*/

/*Check if numerator is within bounds*/
if (num > 0 && num < max) {
float actualRatio = (float)num / denom;
float error = fabsf(actualRatio - targetRatio);

if (error < lowestError) {
bestNum = num;
bestDenom = denom;
lowestError = error;
if (error == 0) {
break;
}
}
}
}

last_target = target;
*numerator = bestNum;
*denominator = bestDenom;
}

int main(int argc, char **argv) {
(void) argc;
(void) argv;
int M = 1000000;
int fsys[] = {50*M, 100*M, 120*M, 125*M, 128*M, 133*M, 150*M, 175*M, 200*M, 225*M, 240*M, 250*M, 275*M, 300*M};
int freq[] = {8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000};
FILE *f = fopen("../libraries/PWMAudio/src/PWMAudioPrecalc.h", "w");
fprintf(f, "// Generated by tools/makepacer.cpp, do not edit\n");
fprintf(f, "typedef struct {\n");
fprintf(f, " uint32_t freq;\n");
fprintf(f, " uint16_t n;\n");
fprintf(f, " uint16_t d;\n");
fprintf(f, "} PWMPacerPrecalc;\n");
for (int i = 0; i < sizeof(fsys)/sizeof(fsys[0]); i++) {
fprintf(f, "#%s F_CPU == %d\n", i == 0 ? "if" : "elif", fsys[i]);
fprintf(f, "static const PWMPacerPrecalc __PWMAudio_pacer[] = {");
for (int j = 0; j < sizeof(freq)/sizeof(freq[0]); j++) {
uint16_t n, d;
find_pacer_fraction(fsys[i], freq[j], &n, &d);
fprintf(f, "{%d, %d, %d}", freq[j], n, d);
if (j < sizeof(freq)/sizeof(freq[0]) - 1) {
fprintf(f, ", ");
}
}
fprintf(f, "};\n");
}
fprintf(f, "#else\n");
fprintf(f, "const PWMPacerPrecalc __PWMAudio_pacer[] = {{1, 1, 1}}; // Invalid, should never match\n");
fprintf(f, "#endif\n");
fclose(f);
}

Loading