Compare commits

...
14 Commits
Author SHA1 Message Date
Earle F. Philhower, III 5296241949 Update version 2024-12-31 10:35:02 -08:00
Earle F. Philhower, III d84ff02f03 Allow setting PWMAudio frequency before begin (#2726)
I2S allows setting freq before ::begin, so allow same for PWMAudio.
2024-12-30 10:57:40 -08:00
Earle F. Philhower, III cc2581afd6 Properly wrap AudioBufferManager block writes (#2725)
The memcpy-version of the ABM::write updated the destination and count
but neglected to move the source forward.  If a block write crossed a
frame boundary then the 2nd frame would repeat the data from the first
half of the buffer.

Fix by incrementing the source pointer by the same amount as was written.
2024-12-29 16:26:23 -08:00
Earle F. Philhower, III bb682bbb33 Use the distributed ARM ar in libpico, not system (#2721)
Fixes #508
2024-12-29 10:29:42 -08:00
Earle F. Philhower, III 809beffc8b Add AudioOutputBase keyword 2024-12-27 10:15:05 -08:00
Earle F. Philhower, III fa22c6c627 RTD has its own key for PDF generation 2024-12-27 08:48:39 -08:00
Earle F. Philhower, III 0788a42477 RTD doesn't do formats:PDF, revert 2024-12-27 08:44:58 -08:00
Earle F. Philhower, III d732ac82ba Precalculate common PWMAudio dividers, avoid noise (#2714)
Calculating the DMA clock divider for PWMAudio can take a very long time
because there are 65K floating point divisions required.  But most audio
will be one of a dozen sample rates, so it is possible to precalculate
the rates on the host and use a small lookup table to avoid any math at
all.  Removes occasional scratching in PWMAudio when the BackgroundAudio
library changes sample rates.
2024-12-22 14:05:44 -08:00
Earle F. Philhower, III 5fb5e16be8 Add real block write for AudioRequestBuffer (#2712)
Optimize AudioRequestBuffer writing when large blocks are available (i.e.
I2S writes of full MP3 or AAC frames in BackgroundAudio).  Update I2S to use
the new call.  Reduces 1152 calls to arb::write() to a single call/return
and optimized memcpy in that case.
2024-12-21 14:15:40 -08:00
Earle F. Philhower, III 45c0b3a1b4 Move MacX86 runner up to new minimum spec (#2711)
The MacOS12 runner is dead, so move up to the lowest version still available for CI.
2024-12-21 10:27:44 -08:00
Markus Gyger 7961d2943d Overclock to 153.6 MHz (instead of 147.6 MHz) for I²S 48 kHz sample rate (#2708) 2024-12-21 07:36:57 -08:00
Kjell Braden 4d03edc7d5 Don't go through runtime initializers when there is no OTA command (#2697)
Leave that to the main app instead, so we don't reset peripherals twice.
* ota: fix copy error for riscv headers
* ota: initialize bootrom bit ops and add explanation
2024-12-19 10:05:46 -08:00
CIncinnatus d5a6888cac Fix issue with undefined LED_BUILTIN on Seeed Xiao RP2350 (#2704)
* Modify the pin definitions for XIAO RP2350

* Fix issue with undefined LED_BUILTIN
2024-12-19 09:02:32 -08:00
Earle F. Philhower, III a13d236afd Update fs.rst 2024-12-19 07:00:59 -08:00
24 changed files with 259 additions and 61 deletions
+1 -1
View File
@@ -196,7 +196,7 @@ jobs:
name: Mac
strategy:
matrix:
os: [macOS-12, macOS-14]
os: [macOS-13, macOS-14]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
+2
View File
@@ -3,5 +3,7 @@ system
tools/dist
docs/_build
ota/build
ota/build-rp2350
ota/build-rp2350-riscv
tools/libpico/build
platform.local.txt
+2 -2
View File
@@ -29,8 +29,8 @@ sphinx:
# fail_on_warning: true
# Optionally build your docs in additional formats such as PDF and ePub
formats:
- pdf
formats:
- pdf
# - epub
# Optional but recommended, declare the Python requirements required
+2 -2
View File
@@ -1,5 +1,5 @@
#pragma once
#define ARDUINO_PICO_MAJOR 4
#define ARDUINO_PICO_MINOR 4
#define ARDUINO_PICO_REVISION 0
#define ARDUINO_PICO_VERSION_STR "4.4.0"
#define ARDUINO_PICO_REVISION 1
#define ARDUINO_PICO_VERSION_STR "4.4.1"
+2 -2
View File
@@ -54,9 +54,9 @@ author = u'Earle F. Philhower, III'
# built documents.
#
# The short X.Y version.
version = u'4.4.0'
version = u'4.4.1'
# The full version, including alpha/beta/rc tags.
release = u'4.4.0'
release = u'4.4.1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
+4
View File
@@ -143,15 +143,19 @@ it using the ``mklittlefs`` tool. A working ``OpenOCD`` setup, DebugProbe, and
To download the raw filesystem, from within ``GDB`` run:
.. code::
^C (break)
(gdb) dump binary memory littlefs.bin &_FS_start &_FS_end
It may take a few seconds as ``GDB`` reads out the flash to the file. Once the raw file is downloaded it can be extracted using the ``mklittlefs`` tool from the BASH/Powershell/command line
.. code::
$ <path-to-mklittlefs>/mklittlefs -u output-dir littlefs.bin
Directory <output-dir> does not exists. Try to create it.
gmon.out > <output-dir>/gmon.out size: 24518 Bytes
gmon.bak > <output-dir>/gmon.bak size: 1 Bytes
The defaults built into ``mklittlefs`` should be appropriate for normal LittleFS filesystems built on the device or using the upload tool.
SD Library Information
+1 -1
View File
@@ -71,7 +71,7 @@ sample rate on-the-fly.
bool setSysClk(int samplerate)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Changes the PICO system clock to optimise for the desired samplerate.
The clock changes to 147.6 MHz for samplerates that are a multiple of 8 kHz, and 135.6 MHz for multiples of 11.025 kHz.
The clock changes to 153.6 MHz for samplerates that are a multiple of 8 kHz, and 135.6 MHz for multiples of 11.025 kHz.
Note that using ``setSysClk()`` may affect the timing of other sysclk-dependent functions.
Should be called before any I2S functions and any other sysclk dependent initialisations.
+1
View File
@@ -14,6 +14,7 @@ File KEYWORD1
timeval KEYWORD1
time_t KEYWORD1
SoftwareSerial KEYWORD1
AudioOutputBase KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
@@ -188,6 +188,39 @@ bool AudioBufferManager::write(uint32_t v, bool sync) {
return true;
}
size_t AudioBufferManager::write(const uint32_t *v, size_t words, bool sync) {
size_t written = 0;
if (!_running || !_isOutput) {
return 0;
}
while (words) {
AudioBuffer ** volatile p = (AudioBuffer ** volatile)&_empty;
if (!*p) {
if (!sync) {
return written;
} else {
while (!*p) {
/* noop busy wait */
}
}
}
size_t availToWriteThisBuff = _wordsPerBuffer - _userOff;
size_t toWrite = std::min(availToWriteThisBuff, words);
memcpy(&((*p)->buff[_userOff]), v, toWrite * sizeof(uint32_t));
v += toWrite;
written += toWrite;
_userOff += toWrite;
words -= toWrite;
if (_userOff == _wordsPerBuffer) {
_addToList(&_filled, _takeFromList(p));
_userOff = 0;
}
}
return written;
}
bool AudioBufferManager::read(uint32_t *v, bool sync) {
if (!_running || _isOutput) {
return false;
@@ -34,6 +34,7 @@ public:
bool begin(int dreq, volatile void *pioFIFOAddr);
bool write(uint32_t v, bool sync = true);
size_t write(const uint32_t *v, size_t words, bool sync = true);
bool read(uint32_t *v, bool sync = true);
void flush();
+3 -18
View File
@@ -123,12 +123,10 @@ bool I2S::setFrequency(int newFreq) {
bool I2S::setSysClk(int samplerate) { // optimise sys_clk for desired samplerate
if (samplerate % 11025 == 0) {
set_sys_clock_khz(I2SSYSCLK_44_1, false); // 147.6 unsuccessful - no I2S no USB
return true;
return set_sys_clock_khz(I2SSYSCLK_44_1, false);
}
if (samplerate % 8000 == 0) {
set_sys_clock_khz(I2SSYSCLK_8, false);
return true;
return set_sys_clock_khz(I2SSYSCLK_8, false);
}
return false;
}
@@ -502,20 +500,7 @@ size_t I2S::write(const uint8_t *buffer, size_t size) {
if (size & 0x3 || !_running || !_isOutput) {
return 0;
}
size_t writtenSize = 0;
uint32_t *p = (uint32_t *)buffer;
while (size) {
if (!_arb->write(*p, false)) {
// Blocked, stop write here
return writtenSize;
} else {
p++;
size -= 4;
writtenSize += 4;
}
}
return writtenSize;
return _arb->write((const uint32_t *)buffer, size / sizeof(uint32_t), false);
}
int I2S::availableForWrite() {
+1 -1
View File
@@ -163,5 +163,5 @@ private:
int _sm, _smMCLK;
static const int I2SSYSCLK_44_1 = 135600; // 44.1, 88.2 kHz sample rates
static const int I2SSYSCLK_8 = 147600; // 8k, 16, 32, 48, 96, 192 kHz
static const int I2SSYSCLK_8 = 153600; // 8k, 16, 32, 48, 96, 192 kHz
};
+17 -1
View File
@@ -20,6 +20,7 @@
*/
#include <Arduino.h>
#include "PWMAudio.h"
#include "PWMAudioPrecalc.h"
#include <hardware/pwm.h>
@@ -100,7 +101,8 @@ bool PWMAudio::setFrequency(int frequency) {
return true; // We're already at the right speed
}
if (_pacer < 0) {
return false;
_sampleRate = frequency;
return true;
}
uint16_t _pacer_D, _pacer_N;
// Flip fraction(N for D, D for N) because we are using it as sys_clk * fraction(mechanic of dma_timer_set_fraction) for smaller than sys_clk values
@@ -151,6 +153,7 @@ bool PWMAudio::begin() {
if (_pacer < 0) {
return false;
}
uint16_t _pacer_D = 0;
uint16_t _pacer_N = 0;
// Flip fraction(N for D, D for N) because we are using it as sys_clk * fraction(mechanic of dma_timer_set_fraction) for smaller than sys_clk values
@@ -281,6 +284,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;
+37
View File
@@ -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
+10 -1
View File
@@ -63,6 +63,8 @@ target_compile_definitions(ota PUBLIC
PICO_RP2040_B2_SUPPORTED=1
PICO_PRINTF_SUPPORT_FLOAT=0
PICO_PRINTF_SUPPORT_LONG_LONG=0
PICO_RUNTIME_INIT_AEABI_BIT_OPS=00090
PICO_RUNTIME_INIT_AEABI_MEM_OPS=00091
LIB_PICO_PRINTF_NONE=1
LFS_READONLY=1
LFS_NO_DEBUG=1
@@ -112,5 +114,12 @@ target_link_libraries(ota
add_custom_command(TARGET ota POST_BUILD
COMMAND ../../system/${TUPLE}/bin/${TUPLE}-ld -r -A ${OBJARCH} -b binary -o ota.o ota.bin
COMMAND ../../system/${TUPLE}/bin/${TUPLE}-objcopy --rename-section .data=.OTA ota.o ../../lib/${cpu}/ota.o
COMMAND cp ../ota_command.h ../../include/${cpu}/pico_base/pico/.
)
if(NOT ${cpu} MATCHES "rp2350-riscv$")
# riscv build uses the same include directories in lib/rp2350-riscv/platform_inc.txt
# so we don't copy in that case
add_custom_command(TARGET ota POST_BUILD
COMMAND cp ../ota_command.h ../../include/${cpu}/pico_base/pico/.
)
endif()
+56 -28
View File
@@ -24,6 +24,7 @@
#include <hardware/sync.h>
#include <hardware/flash.h>
#include <pico/time.h>
#include <pico/runtime_init.h>
#include <hardware/gpio.h>
#include <hardware/uart.h>
#include <hardware/watchdog.h>
@@ -54,26 +55,33 @@ void dumphex(uint32_t x) {
}
extern OTACmdPage _ota_cmd;
void do_ota() {
static uint32_t blockToErase;
bool has_ota() {
if (*__FS_START__ == *__FS_END__) {
return;
return false;
}
if (!lfsMount(*__FS_START__, 4096, *__FS_END__ - *__FS_START__)) {
uart_puts(uart0, "mount failed\n");
return;
return false;
}
// We are very naughty and record the last block read, since it should be the actual data block of the
// OTA structure. We'll erase it behind the scenes to avoid bringing in all of LittleFS write infra.
uint32_t blockToErase;
if (!lfsReadOTA(&_ota_cmd, &blockToErase)) {
return;
return false;
}
if (memcmp(_ota_cmd.sign, "Pico OTA", 8)) {
return; // No signature
return false; // No signature
}
return true;
}
void do_ota() {
uint32_t crc = 0xffffffff;
const uint8_t *data = (const uint8_t *)&_ota_cmd;
for (uint32_t i = 0; i < offsetof(OTACmdPage, crc32); i++) {
@@ -159,11 +167,48 @@ void do_ota() {
// Do a hard reset just in case the start up sequence is not the same
watchdog_reboot(0, 0, 100);
while (true) {
tight_loop_contents();
}
}
#pragma GCC push_options
#pragma GCC optimize("O0")
__attribute__((noreturn))
void boot_normal() {
#ifdef __riscv
extern void __mainapp();
__mainapp();
#else
// Reset the interrupt/etc. vectors to the real app. Will be copied to RAM in app's runtime_init
scb_hw->vtor = (uint32_t)0x10003000;
// Jump to it
register uint32_t* sp asm("sp");
register uint32_t _sp = *(uint32_t *)0x10003000;
register void (*fcn)(void) = (void (*)(void)) *(uint32_t *)0x10003004;
sp = (uint32_t *)_sp;
fcn();
(void)sp;
#endif
__builtin_unreachable();
}
void check_ota() {
// this runs before the full runtime and hardware is initialized to prevent
// initialization happening again in the application. Care must be taken that
// no uninitialized peripheral or part of the runtime is used.
// If necessary, init routines can be moved before check_ota() by adjusting
// their PICO_RUNTIME_INIT_* in CMakeLists.txt.
// refer to this source for existing init routines:
// https://github.com/raspberrypi/pico-sdk/blob/2.1.0/src/rp2_common/pico_runtime_init/include/pico/runtime_init.h
if (!has_ota()) {
boot_normal();
}
}
PICO_RUNTIME_INIT_FUNC_RUNTIME(check_ota, "00099");
int main(int a, char **b) {
(void) a;
(void) b;
@@ -176,28 +221,11 @@ int main(int a, char **b) {
uart_set_format(uart0, 8, 1, UART_PARITY_NONE);
#endif
// if we arrive here, it looks like we have an OTA command in the LittleFS
do_ota();
#ifdef __riscv
extern void __mainapp();
__mainapp();
// Should never get here!
return 0;
#else
// Reset the interrupt/etc. vectors to the real app. Will be copied to RAM in app's runtime_init
scb_hw->vtor = (uint32_t)0x10003000;
// Jump to it
register uint32_t* sp asm("sp");
register uint32_t _sp = *(uint32_t *)0x10003000;
register void (*fcn)(void) = (void (*)(void)) *(uint32_t *)0x10003004;
sp = (uint32_t *)_sp;
fcn();
// Should never get here!
return *sp;
#endif
// fallback to normal boot if do_ota() failed, e.g. due to CRC failure or corruption
boot_normal();
}
#pragma GCC pop_options
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "framework-arduinopico",
"version": "1.40400.0",
"version": "1.40401.0",
"description": "Arduino Wiring-based Framework (RPi Pico RP2040, RP2350)",
"keywords": [
"framework",
+1 -1
View File
@@ -20,7 +20,7 @@
# https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5---3rd-party-Hardware-specification
name=Raspberry Pi RP2040/RP2350 Boards
version=4.4.0
version=4.4.1
# Required discoveries and monitors
# ---------------------------------
+2 -2
View File
@@ -312,8 +312,8 @@ target_link_libraries(ipv4-ipv6-bt-big-${cpu}
foreach(tgt pico ipv4 ipv4-ipv6 ipv4-big ipv4-ipv6-big ipv4-bt ipv4-ipv6-bt ipv4-bt-big ipv4-ipv6-bt-big)
add_custom_command(TARGET ${tgt}-${cpu} POST_BUILD
COMMAND ar d lib${tgt}-${cpu}.a stdio.c.obj stdio_uart.c.obj stdio_usb.c.obj stdio_usb_descriptors.c.obj pico_malloc.c.obj newlib_interface.c.obj
COMMAND ar d lib${tgt}-${cpu}.a btstack_flash_bank.c.obj # Need to override with our own implementation
COMMAND arm-none-eabi-ar d lib${tgt}-${cpu}.a stdio.c.obj stdio_uart.c.obj stdio_usb.c.obj stdio_usb_descriptors.c.obj pico_malloc.c.obj newlib_interface.c.obj
COMMAND arm-none-eabi-ar d lib${tgt}-${cpu}.a btstack_flash_bank.c.obj # Need to override with our own implementation
COMMAND cp lib${tgt}-${cpu}.a ../../../lib/${cpu}/lib${tgt}.a
)
endforeach()
+81
View File
@@ -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);
}
@@ -28,6 +28,7 @@ static const uint8_t D18 = (9u);
// LEDs
#define PIN_LED (25u)
#define LED_BUILTIN PIN_LED
// Serial
#define PIN_SERIAL1_TX (0u)