diff --git a/libraries/PWMAudio/src/PWMAudio.cpp b/libraries/PWMAudio/src/PWMAudio.cpp index e435b16..c52b251 100644 --- a/libraries/PWMAudio/src/PWMAudio.cpp +++ b/libraries/PWMAudio/src/PWMAudio.cpp @@ -20,6 +20,7 @@ */ #include #include "PWMAudio.h" +#include "PWMAudioPrecalc.h" #include @@ -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; diff --git a/libraries/PWMAudio/src/PWMAudioPrecalc.h b/libraries/PWMAudio/src/PWMAudioPrecalc.h new file mode 100644 index 0000000..872a17e --- /dev/null +++ b/libraries/PWMAudio/src/PWMAudioPrecalc.h @@ -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 diff --git a/tools/makepacer.cpp b/tools/makepacer.cpp new file mode 100644 index 0000000..1d75394 --- /dev/null +++ b/tools/makepacer.cpp @@ -0,0 +1,81 @@ +// Generates a header with precalculated best dividers for PWMAudio at +// standard clock frequencies and audio rates. +// + +#include +#include +#include + +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); +} +