Compare commits

...
34 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
Earle F. Philhower, III 2f99b0ae2f Update version 2024-12-18 17:51:11 -08:00
Tristan Rowley 681a4c5482 Add Pimoroni Plasma 2350, fix Plasma 2040 I2C (#2698)
* my attempt at adding plasma_2040 support.. cant seem to get it working to test yet though?

* Tracked+replaced files, this now works it seems!

* correct serial count

* Update pins_arduino.h

* Rudimentary Pimoroni Plasma2350 support.

* include correct files + changes needed for proper support.

also!! fixes pin definitions for the i2c on Pimoroni Plasma 2350 (tested, working) and (presumably, untested) on the Plasma RP2040 too by swapping the i2c numbers.  i2c now works!!

* use correct USB PID for Plasma 2350
2024-12-18 17:49:07 -08:00
Earle F. Philhower, III eecbcdf59a Derive playback devices from common AudioOutputBase (#2703)
The audio output objects all have the same general necessary configuration
calls.  Abstract them out to a generic AudioOutputBase interface class that
they will inherit from.  Simplifies letting applications use different
output channels.

Should be backwards compatible with existing code.
2024-12-18 12:19:40 -08:00
Earle F. Philhower, III f1b965f704 Allow I2S constructor to set pins, like PWMAudio (#2702)
Pins are a physical connection, makes sense to define them at construction.
2024-12-18 10:06:54 -08:00
Earle F. Philhower, III 66af359578 Remove unused variable from AudioBufferManager class (#2701) 2024-12-18 07:06:40 -08:00
Earle F. Philhower, III d65b030644 Add PDF output to readthedocs.yaml (#2700) 2024-12-18 06:48:47 -08:00
Earle F. Philhower, III 33b126836f Support setting channel for SoftAP (#2695)
Fixes #2694
2024-12-17 08:34:14 -08:00
Earle F. Philhower, III def00bac66 Fix ordering of boards in menus (#2696)
Been a little cruft in the order of boards, rearrange alphabetically.
2024-12-16 19:45:30 -08:00
mr.chaiwat chainvong 06e3ef9556 Add MyMakers RP2040 board (#2692) 2024-12-16 19:28:03 -08:00
Heng Teng YiandHTY2003 dc0dc50e36 Modify EVN Alpha board (#2690)
Co-authored-by: HTY2003 <randumbperson@gmail.com>
2024-12-15 16:39:31 -08:00
Earle F. Philhower, III 21a767e7e4 Semihosting part of core, RISC-V support (#2685)
Semihosting is handy for debugging, so allow the core to use `SerialSemi` as the
::printf port.  Add menu item to the IDE to allow selection.

Add RISC-V implementation of semihost call
2024-12-14 09:42:45 -08:00
Earle F. Philhower, III a02e188fc7 PWMAudio setFrequency optimization (#2683)
If we set the frequency to the same one running, no need to do anything.
2024-12-13 08:04:50 -08:00
Earle F. Philhower, III e56a295e34 Add MDNS.addServiceTxt() to SimpleMDNS (#2679)
* Add MDNS.addServiceTxt() to SimpleMDNS

Fixes #2678

A simple mapping allows for basic service text addition even when using
SimpleMDNS and ArduinoOTA.

* Protection against duplicate services addition
2024-12-12 11:55:14 -08:00
Earle F. Philhower, III 5b4dff9a77 Add optional callback parameters for I2S/PWMAudio (#2677)
Like GPIO interrupts, allow the user to pass in a callback data pointer.
2024-12-11 14:53:54 -08:00
Kirk D. Benell abc07ef4e5 Add build.mcu to board definitions (#2673)
* added logic so that the build.mcu value is written to a board defintion in boards.txt. This is needed for Arduino libs that use precompiled/archive libs

* move the setting for the build.mcu as recommended - to manage the different archs available on the rp2340
2024-12-06 10:38:08 -08:00
Earle F. Philhower, III 0061d3f97f Enable gprof onboard profiling (#2669)
Adds a menu item to enable onboard profiling.  This requires significant
RAM and really only makes sense on devices with PSRAM to store the state.

When the menu item is selected, allocates RAM and tracks function calls and
periodically samples the PC to generate a histogram of application usage.
The onboard gmon.out file can be written over Semihosting or
some other way to transfer to a PC for analysis.

Adds a profiling example with command lines.
2024-12-05 17:30:45 -08:00
Earle F. Philhower, III 48bc91af36 Update semihosting.rst typo 2024-12-04 16:33:46 -08:00
Earle F. Philhower, III 1725e2109f Add semihosting support (SerialSemi and SemiFS) (#2670)
Enable ARM-only semihosting mode.  This mode allows applications on the
Pico to write to the OpenOCD console and read and write files on the
host system (i.e. debugging dump information, etc.)

It is not very fast because of the way it uses breakpoints on the Pico
to communicate, but it is useful in cases when you want to get a single
file off of the Pico while debugging.

Note that this **requires** a connected OpenOCD and GDB or else the
semihosting will cause a system panic.
2024-12-04 16:07:46 -08:00
Earle F. Philhower, III f2d30abb1c Add -O0 optimization mode to menus (#2667)
GCC will still do several optimizations in -Og mode which can make it hard
to see local temporary variables.
2024-12-03 16:11:56 -08:00
Earle F. Philhower, III f2fc68f55e GDB + mklittlefs can expand on-device filesystems (#2666) 2024-12-03 16:01:18 -08:00
61 changed files with 3998 additions and 392 deletions
+2 -2
View File
@@ -20,7 +20,7 @@ jobs:
uses: codespell-project/actions-codespell@v2
with:
skip: ./ArduinoCore-API,./libraries/ESP8266SdFat,./libraries/Adafruit_TinyUSB_Arduino,./libraries/LittleFS/lib,./tools/pyserial,./pico-sdk,./.github,./docs/i2s.rst,./cores/rp2040/api,./libraries/FreeRTOS,./tools/libbearssl/bearssl,./include,./libraries/WiFi/examples/BearSSL_Server,./ota/uzlib,./libraries/http-parser/lib,./libraries/WebServer/examples/HelloServerBearSSL/HelloServerBearSSL.ino,./libraries/HTTPUpdateServer/examples/SecureBearSSLUpdater/SecureBearSSLUpdater.ino,./.git,./libraries/FatFS/lib/fatfs,./libraries/FatFS/src/diskio.h,./libraries/FatFS/src/ff.cpp,./libraries/FatFS/src/ffconf.h,./libraries/FatFS/src/ffsystem.cpp,./libraries/FatFS/src/ff.h,./libraries/lwIP_WINC1500/src/driver,./libraries/lwIP_WINC1500/src/common,./libraries/lwIP_WINC1500/src/bus_wrapper,./libraries/lwIP_WINC1500/src/spi_flash
ignore_words_list: ser,dout,shiftIn,acount
ignore_words_list: ser,dout,shiftIn,acount,froms
- name: Get submodules for following tests
run: git submodule update --init
- name: Check package references
@@ -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
+5 -1
View File
@@ -68,14 +68,16 @@ Read the [Contributing Guide](https://github.com/earlephilhower/arduino-pico/blo
* Melopero Cookie RP2040
* Melopero Shake RP2040
* METE HOCA Akana R1
* MyMakers RP2040
* Neko Systems BL2040 Mini
* Olimex RP2040-Pico30
* Newsan Archi
* nullbits Bit-C PRO
* Olimex RP2040-Pico30
* Pimoroni PGA2040
* Pimoroni Pico Plus 2
* Pimoroni Pico Plus 2W
* Pimoroni Plasma2040
* Pimoroni Plasma2350
* Pimoroni Tiny2040
* Pimoroni Tiny2350
* Pintronix PinMax
@@ -137,6 +139,8 @@ Read the [Contributing Guide](https://github.com/earlephilhower/arduino-pico/blo
* printf (i.e. debug) output over USB serial
* Transparent use of PSRAM globals and heap (RP2350 only)
* ARM or RISC-V (Hazard3) support for the RP2350
* Semihosted serial and file system access
* GPROF profiling support
The RP2040 PIO state machines (SMs) are used to generate jitter-free:
* Servos
+1763 -230
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -126,6 +126,7 @@ extern const String emptyString;
#endif
#include "SerialUART.h"
#include "SerialSemi.h"
#include "RP2040Support.h"
#include "SerialPIO.h"
#include "Bootsel.h"
+21
View File
@@ -0,0 +1,21 @@
// Abstract class for audio output devices to allow easy swapping between output devices
#pragma once
#include <Print.h>
class AudioOutputBase : public Print {
public:
virtual ~AudioOutputBase() { }
virtual bool setBuffers(size_t buffers, size_t bufferWords, int32_t silenceSample = 0) = 0;
virtual bool setBitsPerSample(int bps) = 0;
virtual bool setFrequency(int freq) = 0;
virtual bool setStereo(bool stereo = true) = 0;
virtual bool begin() = 0;
virtual bool end() = 0;
virtual bool getUnderflow() = 0;
virtual void onTransmit(void(*)(void *), void *) = 0;
// From Print
virtual size_t write(const uint8_t *buffer, size_t size) = 0;
virtual int availableForWrite() = 0;
};
+17 -1
View File
@@ -18,9 +18,11 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <Arduino.h>
#include <pico/runtime.h>
#ifdef PICO_RP2040
#include <Arduino.h>
#include <hardware/structs/psm.h>
extern "C" void boot_double_tap_check();
@@ -35,3 +37,17 @@ void RP2040::enableDoubleResetBootloader() {
}
#endif
#ifdef __PROFILE
Stream *__profileFile;
int __writeProfileCB(const void *data, int len) {
return __profileFile->write((const char *)data, len);
}
#ifdef __PROFILE
extern "C" void runtime_init_setup_profiling();
#define PICO_RUNTIME_INIT_PROFILING "11011" // Towards the end, after PSRAM
PICO_RUNTIME_INIT_FUNC_RUNTIME(runtime_init_setup_profiling, PICO_RUNTIME_INIT_PROFILING);
#endif
#endif
+30 -6
View File
@@ -18,6 +18,8 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include <hardware/clocks.h>
#include <hardware/irq.h>
#include <hardware/pio.h>
@@ -45,6 +47,13 @@
extern "C" volatile bool __otherCoreIdled;
extern "C" {
#ifdef __PROFILE
typedef int (*profileWriteCB)(const void *data, int len);
extern void _writeProfile(profileWriteCB writeCB);
#endif
}
class _MFIFO {
public:
_MFIFO() { /* noop */ };
@@ -180,7 +189,7 @@ public:
void begin() {
_epoch = 0;
#if !defined(__riscv)
#if !defined(__riscv) && !defined(__PROFILE)
if (!__isFreeRTOS) {
// Enable SYSTICK exception
exception_set_exclusive_handler(SYSTICK_EXCEPTION, _SystickHandler);
@@ -193,7 +202,7 @@ public:
_ccountPgm->prepare(&_pio, &_sm, &off);
ccount_program_init(_pio, _sm, off);
pio_sm_set_enabled(_pio, _sm, true);
#if !defined(__riscv)
#if !defined(__riscv) && !defined(__PROFILE)
}
#endif
}
@@ -217,7 +226,7 @@ public:
// Get CPU cycle count. Needs to do magic to extens 24b HW to something longer
volatile uint64_t _epoch = 0;
inline uint32_t getCycleCount() {
#if !defined(__riscv)
#if !defined(__riscv) && !defined(__PROFILE)
if (!__isFreeRTOS) {
uint32_t epoch;
uint32_t ctr;
@@ -229,13 +238,13 @@ public:
} else {
#endif
return ccount_read(_pio, _sm);
#if !defined(__riscv)
#if !defined(__riscv) && !defined(__PROFILE)
}
#endif
}
inline uint64_t getCycleCount64() {
#if !defined(__riscv)
#if !defined(__riscv) && !defined(__PROFILE)
if (!__isFreeRTOS) {
uint64_t epoch;
uint64_t ctr;
@@ -247,7 +256,7 @@ public:
} else {
#endif
return ccount_read(_pio, _sm);
#if !defined(__riscv)
#if !defined(__riscv) && !defined(__PROFILE)
}
#endif
}
@@ -473,6 +482,21 @@ public:
#endif
}
#ifdef __PROFILE
void writeProfiling(Stream *f) {
extern Stream *__profileFile;
extern int __writeProfileCB(const void *data, int len);
__profileFile = f;
_writeProfile(__writeProfileCB);
}
size_t getProfileMemoryUsage() {
extern int __profileMemSize;
return (size_t) __profileMemSize;
}
#endif
private:
static void _SystickHandler() {
+2 -2
View File
@@ -1,5 +1,5 @@
#pragma once
#define ARDUINO_PICO_MAJOR 4
#define ARDUINO_PICO_MINOR 3
#define ARDUINO_PICO_MINOR 4
#define ARDUINO_PICO_REVISION 1
#define ARDUINO_PICO_VERSION_STR "4.3.1"
#define ARDUINO_PICO_VERSION_STR "4.4.1"
+297
View File
@@ -0,0 +1,297 @@
/*
SemiFS.h - File system wrapper for Semihosting ARM
Copyright (c) 2024 Earle F. Philhower, III. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include "Semihosting.h"
#include "FS.h"
#include "FSImpl.h"
using namespace fs;
namespace semifs {
class SemiFSFileImpl;
class SemiFSConfig : public FSConfig {
public:
static constexpr uint32_t FSId = 0x53454d49;
SemiFSConfig() : FSConfig(FSId, false) { }
};
class SemiFSFileImpl : public FileImpl {
public:
SemiFSFileImpl(int fd, const char *name, bool writable)
: _fd(fd), _opened(true), _writable(writable) {
_name = std::shared_ptr<char>(new char[strlen(name) + 1], std::default_delete<char[]>());
strcpy(_name.get(), name);
}
~SemiFSFileImpl() override {
flush();
close();
}
int availableForWrite() override {
return 1; // TODO - not implemented? _opened ? _fd->availableSpaceForWrite() : 0;
}
size_t write(const uint8_t *buf, size_t size) override {
if (_opened) {
uint32_t a[3];
a[0] = _fd;
a[1] = (uint32_t)buf;
a[2] = size;
return 0 == Semihost(SEMIHOST_SYS_WRITE, a) ? size : -1;
}
return -1; // some kind of error
}
int read(uint8_t* buf, size_t size) override {
if (_opened) {
uint32_t a[3];
a[0] = _fd;
a[1] = (uint32_t)buf;
a[2] = size;
int ret = Semihost(SEMIHOST_SYS_READ, a);
if (ret == 0) {
return size;
} else if (ret == (int)size) {
return -1;
} else {
return ret;
}
}
return -1;
}
void flush() override {
/* noop */
}
bool seek(uint32_t pos, SeekMode mode) override {
if (!_opened || (mode != SeekSet)) {
// No seek cur/end in semihost
return false;
}
uint32_t a[2];
a[0] = _fd;
a[1] = pos;
return !Semihost(SEMIHOST_SYS_SEEK, a);
}
size_t position() const override {
return 0; // Not available semihost
}
size_t size() const override {
if (!_opened) {
return 0;
}
uint32_t a;
a = _fd;
int ret = Semihost(SEMIHOST_SYS_FLEN, &a);
if (ret < 0) {
return 0;
}
return ret;
}
bool truncate(uint32_t size) override {
return false; // Not allowed
}
void close() override {
if (_opened) {
uint32_t a = _fd;
Semihost(SEMIHOST_SYS_CLOSE, &a);
_opened = false;
}
}
const char* name() const override {
if (!_opened) {
DEBUGV("SemiFSFileImpl::name: file not opened\n");
return nullptr;
} else {
const char *p = _name.get();
const char *slash = strrchr(p, '/');
// For names w/o any path elements, return directly
// If there are slashes, return name after the last slash
// (note that strrchr will return the address of the slash,
// so need to increment to ckip it)
return (slash && slash[1]) ? slash + 1 : p;
}
}
const char* fullName() const override {
return _opened ? _name.get() : nullptr;
}
bool isFile() const override {
return _opened; // Could look at ISTTY but that's not the sense here. Just differentiating between dirs and files
}
bool isDirectory() const override {
return false;
}
time_t getLastWrite() override {
return getCreationTime(); // TODO - FatFS doesn't seem to report both filetimes
}
time_t getCreationTime() override {
time_t ftime = 0;
return ftime;
}
protected:
int _fd;
std::shared_ptr<char> _name;
bool _opened;
bool _writable;
};
class SemiFSImpl : public FSImpl {
public:
SemiFSImpl() {
/* noop */
}
FileImplPtr open(const char* path, OpenMode openMode, AccessMode accessMode) override {
if (!path || !path[0]) {
DEBUGV("SemiFSImpl::open() called with invalid filename\n");
return FileImplPtr();
}
// Mode conversion https://developer.arm.com/documentation/dui0471/m/what-is-semihosting-/sys-open--0x01-?lang=en
int mode = 1; // "rb"
if (accessMode == AM_READ) {
mode = 1; // "rb"
} else if (accessMode == AM_WRITE) {
if (openMode & OM_APPEND) {
mode = 9; // "ab";
} else {
mode = 5; // "wb";
}
} else {
if (openMode & OM_TRUNCATE) {
mode = 7; // "w+b";
} else if (openMode & OM_APPEND) {
mode = 3; // "r+b"
} else {
mode = 11; // "a+b";
}
}
uint32_t a[3];
a[0] = (uint32_t)path;
a[1] = mode;
a[2] = strlen(path);
int handle = Semihost(SEMIHOST_SYS_OPEN, a);
if (handle < 0) {
return FileImplPtr();
}
return std::make_shared<SemiFSFileImpl>(handle, path, (accessMode & AM_WRITE) ? true : false);
}
bool exists(const char* path) override {
File f = open(path, OM_DEFAULT, AM_READ);
return f ? true : false;
}
DirImplPtr openDir(const char* path) override {
// No directories
return DirImplPtr();
}
bool rename(const char* pathFrom, const char* pathTo) override {
uint32_t a[4];
a[0] = (uint32_t)pathFrom;
a[1] = strlen(pathFrom);
a[2] = (uint32_t)pathTo;
a[3] = strlen(pathTo);
return !Semihost(SEMIHOST_SYS_RENAME, a);
}
bool info(FSInfo& info) override {
// Not available
return false;
}
bool remove(const char* path) override {
uint32_t a[2];
a[0] = (uint32_t)path;
a[1] = strlen(path);
return !Semihost(SEMIHOST_SYS_REMOVE, a);
}
bool mkdir(const char* path) override {
// No mkdir
return false;
}
bool rmdir(const char* path) override {
// No rmdir
return false;
}
bool stat(const char *path, FSStat *st) override {
if (!path || !path[0]) {
return false;
}
uint32_t a[3];
a[0] = (uint32_t)path;
a[1] = 0; // READ
a[2] = strlen(path);
int fn = Semihost(SEMIHOST_SYS_OPEN, a);
if (fn < 0) {
return false;
}
bzero(st, sizeof(*st));
a[0] = fn;
st->size = Semihost(SEMIHOST_SYS_FLEN, a);
a[0] = fn;
Semihost(SEMIHOST_SYS_CLOSE, a);
return true;
}
bool setConfig(const FSConfig &cfg) override {
return true;
}
bool begin() override {
/* noop */
return true;
}
void end() override {
/* noop */
}
bool format() override {
return false;
}
};
}; // namespace sdfs
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SEMIFS)
extern FS SemiFS;
using semifs::SemiFSConfig;
#endif
+6
View File
@@ -0,0 +1,6 @@
#include "Semihosting.h"
#include "SerialSemi.h"
#include "SemiFS.h"
SerialSemiClass SerialSemi;
FS SemiFS = FS(FSImplPtr(new semifs::SemiFSImpl()));
+99
View File
@@ -0,0 +1,99 @@
/*
Semihosting.h - Semihosting for Serial and FS access via GDB
Copyright (c) 2024 Earle F. Philhower, III. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
// Be sure to only use this library with GDB and to enable the ARM semihosting support
// (gdb) monitor arm semihosting enable
// Input/output will be handled by OpenOCD
// From https://developer.arm.com/documentation/dui0471/g/Semihosting/Semihosting-operations?lang=en
typedef enum {
SEMIHOST_SYS_CLOSE = 0x02,
SEMIHOST_SYS_CLOCK = 0x10,
SEMIHOST_SYS_ELAPSED = 0x30,
SEMIHOST_SYS_ERRNO = 0x13,
SEMIHOST_SYS_FLEN = 0x0C,
SEMIHOST_SYS_GET_CMDLINE = 0x15,
SEMIHOST_SYS_HEAPINFO = 0x16,
SEMIHOST_SYS_ISERROR = 0x08,
SEMIHOST_SYS_ISTTY = 0x09,
SEMIHOST_SYS_OPEN = 0x01,
SEMIHOST_SYS_READ = 0x06,
SEMIHOST_SYS_READC = 0x07,
SEMIHOST_SYS_REMOVE = 0x0E,
SEMIHOST_SYS_RENAME = 0x0F,
SEMIHOST_SYS_SEEK = 0x0A,
SEMIHOST_SYS_SYSTEM = 0x12,
SEMIHOST_SYS_TICKFREQ = 0x31,
SEMIHOST_SYS_TIME = 0x11,
SEMIHOST_SYS_TMPNAM = 0x0D,
SEMIHOST_SYS_WRITE = 0x05,
SEMIHOST_SYS_WRITEC = 0x03,
SEMIHOST_SYS_WRITE0 = 0x04
} SEMIHOST_OPCODES;
#ifdef __arm__
// From https://github.com/ErichStyger/mcuoneclipse/blob/master/Examples/MCUXpresso/FRDM-K22F/FRDM-K22F_Semihosting/source/McuSemihost.c
static inline int __attribute__((always_inline)) Semihost(int reason, void *arg) {
int value;
__asm volatile(
"mov r0, %[rsn] \n" /* place semihost operation code into R0 */
"mov r1, %[arg] \n" /* R1 points to the argument array */
"bkpt 0xAB \n" /* call debugger */
"mov %[val], r0 \n" /* debugger has stored result code in R0 */
: [val] "=r"(value) /* outputs */
: [rsn] "r"(reason), [arg] "r"(arg) /* inputs */
: "r0", "r1", "r2", "r3", "ip", "lr", "memory", "cc" /* clobber */
);
return value; /* return result code, stored in R0 */
}
#else
// https://groups.google.com/a/groups.riscv.org/g/sw-dev/c/n-5VQ9PHZ4w/m/KbzH5t9MBgAJ
static inline int __attribute__((always_inline)) Semihost(int reason, void *argPack) {
register int value asm("a0") = reason;
register void *ptr asm("a1") = argPack;
asm volatile(
// Force 16-byte alignment to make sure that the 3 instructions fall
// within the same virtual page.
" .balign 16 \n"
" .option push \n"
// Force non-compressed RISC-V instructions
" .option norvc \n"
// semihosting e-break sequence
" slli x0, x0, 0x1f \n" // # Entry NOP
" ebreak \n" // # Break to debugger
" srai x0, x0, 0x7 \n" // # NOP encoding the semihosting call number 7
" .option pop \n"
/*mark (value) as an output operand*/
: "=r"(value) /* Outputs */
// The semihosting call number is passed in a0, and the argument in a1.
: "0"(value), "r"(ptr) /* Inputs */
// The "memory" clobber makes GCC assume that any memory may be arbitrarily read or written by the asm block,
// so will prevent the compiler from reordering loads or stores across it, or from caching memory values in registers across it.
// The "memory" clobber also prevents the compiler from removing the asm block as dead code.
: "memory" /* Clobbers */
);
return value;
}
#endif
+98
View File
@@ -0,0 +1,98 @@
/*
SerialSemi.h - Serial port over Semihosting for ARM
Copyright (c) 2024 Earle F. Philhower, III. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include "Semihosting.h"
#include "Arduino.h"
#include "api/HardwareSerial.h"
class SerialSemiClass : public HardwareSerial {
public:
SerialSemiClass() {
/* noop */
}
~SerialSemiClass() {
/* noop */
}
void begin(unsigned long baudIgnored = 115200) override {
(void)baudIgnored;
}
void begin(unsigned long baudIgnored, uint16_t configIgnored) override {
(void)baudIgnored;
(void)configIgnored;
}
void end() override {
/* noop */
}
virtual int peek() override {
// Can't really peek on SH, so fake it best we can
if (!_peeked) {
_peekedChar = read();
_peeked = true;
}
return _peekedChar;
}
virtual int read() override {
if (_peeked) {
_peeked = false;
return _peekedChar;
}
return Semihost(SEMIHOST_SYS_READC, nullptr);
}
virtual int available() override {
// Can't really tell with SH, so always true. Buyer beware
return 1;
}
virtual int availableForWrite() override {
// Can't really tell with SH, so always true. Buyer beware
return 1;
}
virtual void flush() override {
/* noop */
}
virtual size_t write(uint8_t c) override {
int32_t param = c;
Semihost(SEMIHOST_SYS_WRITEC, &param);
return 1;
}
using Print::write;
operator bool() override {
return true;
}
private:
bool _peeked = false;
uint8_t _peekedChar;
};
extern SerialSemiClass SerialSemi;
+23 -19
View File
@@ -30,32 +30,36 @@ extern bool __isFreeRTOS;
// FreeRTOS has been set up
extern volatile bool __freeRTOSinitted;
#ifdef __cplusplus
extern "C" {
struct QueueDefinition; /* Using old naming convention so as not to break kernel aware debuggers. */
typedef struct QueueDefinition * QueueHandle_t;
typedef QueueHandle_t SemaphoreHandle_t;
typedef int32_t BaseType_t;
#endif // __cplusplus
struct QueueDefinition; /* Using old naming convention so as not to break kernel aware debuggers. */
typedef struct QueueDefinition * QueueHandle_t;
typedef QueueHandle_t SemaphoreHandle_t;
typedef int32_t BaseType_t;
extern bool __freertos_check_if_in_isr() __attribute__((weak));
extern bool __freertos_check_if_in_isr() __attribute__((weak));
extern SemaphoreHandle_t __freertos_mutex_create() __attribute__((weak));
extern SemaphoreHandle_t _freertos_recursive_mutex_create() __attribute__((weak));
extern SemaphoreHandle_t __freertos_mutex_create() __attribute__((weak));
extern SemaphoreHandle_t _freertos_recursive_mutex_create() __attribute__((weak));
extern void __freertos_mutex_take(SemaphoreHandle_t mtx) __attribute__((weak));
extern void __freertos_mutex_take(SemaphoreHandle_t mtx) __attribute__((weak));
extern int __freertos_mutex_take_from_isr(SemaphoreHandle_t mtx, BaseType_t* pxHigherPriorityTaskWoken) __attribute__((weak));
extern int __freertos_mutex_try_take(SemaphoreHandle_t mtx) __attribute__((weak));
extern void __freertos_mutex_give(SemaphoreHandle_t mtx) __attribute__((weak));
extern void __freertos_mutex_give_from_isr(SemaphoreHandle_t mtx, BaseType_t* pxHigherPriorityTaskWoken) __attribute__((weak));
extern int __freertos_mutex_take_from_isr(SemaphoreHandle_t mtx, BaseType_t* pxHigherPriorityTaskWoken) __attribute__((weak));
extern int __freertos_mutex_try_take(SemaphoreHandle_t mtx) __attribute__((weak));
extern void __freertos_mutex_give(SemaphoreHandle_t mtx) __attribute__((weak));
extern void __freertos_mutex_give_from_isr(SemaphoreHandle_t mtx, BaseType_t* pxHigherPriorityTaskWoken) __attribute__((weak));
extern void __freertos_recursive_mutex_take(SemaphoreHandle_t mtx) __attribute__((weak));
extern int __freertos_recursive_mutex_try_take(SemaphoreHandle_t mtx) __attribute__((weak));
extern void __freertos_recursive_mutex_give(SemaphoreHandle_t mtx) __attribute__((weak));
extern void __freertos_recursive_mutex_take(SemaphoreHandle_t mtx) __attribute__((weak));
extern int __freertos_recursive_mutex_try_take(SemaphoreHandle_t mtx) __attribute__((weak));
extern void __freertos_recursive_mutex_give(SemaphoreHandle_t mtx) __attribute__((weak));
extern void __freertos_idle_other_core() __attribute__((weak));
extern void __freertos_resume_other_core() __attribute__((weak));
extern void __freertos_idle_other_core() __attribute__((weak));
extern void __freertos_resume_other_core() __attribute__((weak));
extern void __freertos_task_exit_critical() __attribute__((weak));
extern void __freertos_task_enter_critical() __attribute__((weak));
extern void __freertos_task_exit_critical() __attribute__((weak));
extern void __freertos_task_enter_critical() __attribute__((weak));
#ifdef __cplusplus
}
extern SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m, bool recursive = false);
#endif // __cplusplus
+470
View File
@@ -0,0 +1,470 @@
/* -
Copyright (c) 1983, 1992, 1993
The Regents of the University of California. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
4. Neither the name of the University nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
*/
// This code is built as a C file because otherwise G++ would add profiling
// code to the preamble of these functions as well, leading to an infinite
// loop in the mcount routine. Because the Arduino IDE can't (easily)
// apply different compile parameters to different files, we set all C++
// files to "-pg" but leave all C files uninstrumented.
// Original code and organization taken from https://mcuoneclipse.com/2015/08/23/tutorial-using-gnu-profiling-gprof-with-arm-cortex-m/
#include <Arduino.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
// Frequency of sampling PC
#ifndef GMON_HZ
#define GMON_HZ 10000
#endif
// Fraction of text space to allocate for histogram counters here, 1/2
#ifndef HISTFRACTION
#ifdef PICO_RP2350
#define HISTFRACTION 4 // Every 8 bytes of .text
#else
#define HISTFRACTION 8 // Every 16 bytes of .text
#endif
#endif
// Fraction of text space to allocate for from hash buckets.
// The value of HASHFRACTION is based on the minimum number of bytes
// of separation between two subroutine call points in the object code.
// Given MIN_SUBR_SEPARATION bytes of separation the value of
// HASHFRACTION is calculated as:
//
// HASHFRACTION = MIN_SUBR_SEPARATION / (2 * sizeof(short) - 1);
//
// For example, on the VAX, the shortest two call sequence is:
//
// calls $0,(r0)
// calls $0,(r0)
//
// which is separated by only three bytes, thus HASHFRACTION is
// calculated as:
//
// HASHFRACTION = 3 / (2 * 2 - 1) = 1
//
// Note that the division above rounds down, thus if MIN_SUBR_FRACTION
// is less than three, this algorithm will not work!
//
// In practice, however, call instructions are rarely at a minimal
// distance. Hence, we will define HASHFRACTION to be 2 across all
// architectures. This saves a reasonable amount of space for
// profiling data structures without (in practice) sacrificing
// any granularity.
#ifndef HASHFRACTION
#define HASHFRACTION 2
#endif
// Percent of text space to allocate for tostructs with a minimum.
#ifndef ARCDENSITY
#define ARCDENSITY 2 // This is in percentage, relative to text size!
#endif
#define MINARCS 50
#define MAXARCS ((1 << (8 * sizeof(HISTCOUNTER))) - 2)
// Histogram counters are unsigned shorts (according to the kernel)
typedef uint16_t HISTCOUNTER; //#define HISTCOUNTER unsigned short
// In the original profiler code selfpc and count are full 32 bits each
// so the structure actually comes to 12 bytes due to padding (with 2
// bytes wasted per entry). We don't have that much to spare on the Picos,
// so limit the recorded address to 16MB (which is the flash address
// window, anyway) and the counts to 16M (saturating). This saves 4 bytes
// (33%) per entry at the cost of some logic to expand/pack it.
struct tostruct {
uint8_t selfpc[3]; // Callee address/program counter. The caller address is in froms[] array which points to tos[] array
uint8_t count[3]; // How many times it has been called
uint16_t link; // Link to next entry in hash table. For tos[0] this points to the last used entry
};
typedef enum { PROFILE_NOT_INIT = 0, PROFILE_ON, PROFILE_OFF } PROFILE_State;
struct profinfo {
PROFILE_State state; // Profiling state
uint16_t *counter; // Profiling counters
size_t lowpc, highpc; // Range to be profiled
uint32_t scale; // Scale value of bins
};
// Global profinfo for profil() call
static struct profinfo prof = { PROFILE_NOT_INIT, 0, 0, 0, 0 };
// Possible states of profiling
typedef enum { GMON_PROF_ON = 0, GMON_PROF_BUSY, GMON_PROF_ERROR, GMON_PROF_OFF } GMON_State;
// The profiling data structures are housed in this structure.
struct gmonparam {
int state;
uint16_t *kcount; // Histogram PC sample array
size_t kcountsize; // Size of kcount[] array in bytes
uint16_t *froms; // Array of hashed 'from' addresses. The 16bit value is an index into the tos[] array
size_t fromssize; // Size of froms[] array in bytes
struct tostruct *tos; // to struct, contains histogram counter
size_t tossize; // Size of tos[] array in bytes
long tolimit;
size_t lowpc; // Low program counter of area
size_t highpc; // High program counter
size_t textsize; // Code size
};
static struct gmonparam _gmonparam = { GMON_PROF_OFF, NULL, 0, NULL, 0, NULL, 0, 0L, 0, 0, 0};
static bool already_setup = false; // Flag to indicate if we need to init
static bool _perf_in_setup = false; // Are we currently trying to initialize? (avoid infinite recursion)
int __profileMemSize = 0; // Memory allocated by the profiler to store tables
static int s_scale = 0;
#define SCALE_1_TO_1 0x10000L
// Convert an addr to an index
static inline __attribute__((always_inline)) size_t profidx(size_t pc, size_t base, size_t scale) {
size_t i = (pc - base) / 2;
return (unsigned long long int) i * scale / 65536;
}
// Sample the current program counter periodically
#if defined(__riscv)
// TODO - systick-like handler
#else
static void __no_inline_not_in_flash_func(_SystickHandler)(void) {
static size_t pc, idx; // Ensure in heap, not on stack
extern volatile bool __otherCoreIdled;
if (!__otherCoreIdled && (prof.state == PROFILE_ON)) {
pc = ((uint32_t*)(__builtin_frame_address(0)))[14]; // Get SP and use it to get the return address from stack
if ((pc >= prof.lowpc) && (pc < prof.highpc)) {
idx = profidx(pc, prof.lowpc, prof.scale);
prof.counter[idx]++;
}
}
}
#endif
// Convert an index into an address
static inline __attribute__((always_inline)) size_t profaddr(size_t idx, size_t base, size_t scale) {
return base + ((((unsigned long long)(idx) << 16) / (unsigned long long)(scale)) << 1);
}
// Start or stop profiling
// Profiling goes into the SAMPLES buffer of size SIZE (which is treated as an array of uint16_ts of size size/2).
// Each bin represents a range of pc addresses from OFFSET. The number of pc addresses in a bin depends on SCALE.
// (A scale of 65536 maps each bin to two addresses, A scale of 32768 maps each bin to 4 addresses, a scale of
// 1 maps each bin to 128k address). Scale may be 1 - 65536, or zero to turn off profiling
static int __no_inline_not_in_flash_func(profile_ctl)(char *samples, size_t size, size_t offset, uint32_t scale) {
size_t maxbin;
if (scale > 65536) {
return -1;
}
prof.state = PROFILE_OFF;
if (scale) {
bzero(samples, size);
bzero(&prof, sizeof(prof));
maxbin = size >> 1;
prof.counter = (uint16_t*)samples;
prof.lowpc = offset;
prof.highpc = profaddr(maxbin, offset, scale);
prof.scale = scale;
prof.state = PROFILE_ON;
}
return 0;
}
// Control profiling. Profiling is what mcount checks to see if all the data structures are ready.
static void __no_inline_not_in_flash_func(moncontrol)(int mode) {
if (mode) { // Start
profile_ctl((char *)_gmonparam.kcount, _gmonparam.kcountsize, _gmonparam.lowpc, s_scale);
_gmonparam.state = GMON_PROF_ON;
} else { // Stop
profile_ctl((char *)NULL, 0, 0, 0);
_gmonparam.state = GMON_PROF_OFF;
}
}
// General rounding functions
static inline __attribute__((always_inline)) size_t rounddown(size_t x, size_t y) {
return (x / y) * y;
}
static inline __attribute__((always_inline)) size_t roundup(size_t x, size_t y) {
return ((x + y - 1) / y) * y;
}
// Allocate memory and set boundaries before any sampling is performed
void __no_inline_not_in_flash_func(monstartup)(size_t lowpc, size_t highpc) {
register size_t o;
char *cp;
struct gmonparam *p = &_gmonparam;
// Round lowpc and highpc to multiples of the density we're using so the rest of the scaling (here and in gprof) stays in ints.
p->lowpc = rounddown(lowpc, HISTFRACTION * sizeof(HISTCOUNTER));
p->highpc = roundup(highpc, HISTFRACTION * sizeof(HISTCOUNTER));
p->textsize = p->highpc - p->lowpc;
p->kcountsize = p->textsize / HISTFRACTION;
p->fromssize = p->textsize / HASHFRACTION;
p->tolimit = p->textsize * ARCDENSITY / 100;
if (p->tolimit < MINARCS) {
p->tolimit = MINARCS;
} else if (p->tolimit > MAXARCS) {
p->tolimit = MAXARCS;
}
p->tossize = p->tolimit * sizeof(struct tostruct);
__profileMemSize = p->kcountsize + p->fromssize + p->tossize;
#ifdef RP2350_PSRAM_CS
cp = pmalloc(__profileMemSize);
#else
cp = malloc(__profileMemSize);
#endif
if (cp == NULL) {
// OOM
already_setup = false;
return;
}
// Zero out cp as value will be added there
bzero(cp, p->kcountsize + p->fromssize + p->tossize);
p->tos = (struct tostruct *)cp;
cp += p->tossize;
p->kcount = (uint16_t *)cp;
cp += p->kcountsize;
p->froms = (uint16_t *)cp;
p->tos[0].link = 0;
o = p->highpc - p->lowpc;
if (p->kcountsize < o) {
s_scale = ((float)p->kcountsize / o) * SCALE_1_TO_1;
} else {
s_scale = SCALE_1_TO_1;
}
moncontrol(1); // Start
}
// Accessors for the selfpc and count fields
static inline __attribute__((always_inline)) void setselfpc(struct tostruct *x, size_t d) {
x->selfpc[0] = d & 0xff;
x->selfpc[1] = (d >> 8) & 0xff;
x->selfpc[2] = (d >> 16) & 0xff;
}
static inline __attribute__((always_inline))void setcount(struct tostruct *x, size_t d) {
x->count[0] = d & 0xff;
x->count[1] = (d >> 8) & 0xff;
x->count[2] = (d >> 16) & 0xff;
}
static inline __attribute__((always_inline)) uint32_t getselfpc(const struct tostruct *x) {
return 0x10000000 | ((uint32_t)x->selfpc[0]) | (((uint32_t)x->selfpc[1]) << 8) | (((uint32_t)x->selfpc[2]) << 16);
}
static inline __attribute__((always_inline)) uint32_t getcount(const struct tostruct *x) {
return ((uint32_t)x->count[0]) | (((uint32_t)x->count[1]) << 8) | (((uint32_t)x->count[2]) << 16);
}
// Called by the GCC function shim (gprof_shim.S) on function entry to record an arc hit
void __no_inline_not_in_flash_func(_mcount_internal)(uint32_t *frompcindex, uint32_t *selfpc) {
register struct tostruct *top;
register struct tostruct *prevtop;
register long toindex;
struct gmonparam *p = &_gmonparam;
if (_perf_in_setup) {
// Avoid infinite recursion
return;
}
if (!already_setup) {
extern char __flash_binary_start; // Start of flash
extern char __etext; // End of .text
already_setup = true;
_perf_in_setup = true;
monstartup((uint32_t)&__flash_binary_start, (uint32_t)&__etext);
_perf_in_setup = false;
}
// Check that we are profiling and that we aren't recursively invoked.
if (p->state != GMON_PROF_ON) {
return;
}
p->state++;
// Check that frompcindex is a reasonable pc value.
frompcindex = (uint32_t*)((long)frompcindex - (long)p->lowpc);
if ((unsigned long)frompcindex > p->textsize) {
goto done;
}
frompcindex = (uint32_t*)&p->froms[((long)frompcindex) / (HASHFRACTION * sizeof(*p->froms))];
toindex = *((uint16_t*)frompcindex); // Get froms[] value
if (toindex == 0) {
// First time traversing this arc
toindex = ++p->tos[0].link; // The link of tos[0] points to the last used record in the array
if (toindex >= p->tolimit) { // More tos[] entries than we can handle!
goto overflow;
}
*((uint16_t*)frompcindex) = (uint16_t)toindex; // Store new 'to' value into froms[]
top = &p->tos[toindex];
setselfpc(top, (uint32_t)selfpc);
setcount(top, 1);
top->link = 0;
goto done;
}
top = &p->tos[toindex];
if (getselfpc(top) == (size_t)selfpc) {
// Arc at front of chain; usual case.
uint32_t cnt = getcount(top) + 1;
if (cnt >= 1 << 24) {
cnt = (1 << 24) - 1;
}
setcount(top, cnt);
goto done;
}
// Have to go looking down chain for it. top points to what we are looking at, prevtop points to previous top. We know it is not at the head of the chain.
for (; /* goto done */;) {
if (top->link == 0) {
// top is end of the chain and none of the chain had top->selfpc == selfpc, so we allocate a new tostruct and link it to the head of the chain.
toindex = ++p->tos[0].link;
if (toindex >= p->tolimit) {
goto overflow;
}
top = &p->tos[toindex];
setselfpc(top, (uint32_t)selfpc);
setcount(top, 1);
top->link = *((uint16_t*)frompcindex);
*(uint16_t*)frompcindex = (uint16_t)toindex;
goto done;
}
// Otherwise, check the next arc on the chain.
prevtop = top;
top = &p->tos[top->link];
if (getselfpc(top) == (size_t)selfpc) {
// Increment its count, move it to the head of the chain.
uint32_t cnt = getcount(top) + 1;
if (cnt >= 1 << 24) {
cnt = (1 << 24) - 1;
}
setcount(top, cnt);
toindex = prevtop->link;
prevtop->link = top->link;
top->link = *((uint16_t*)frompcindex);
*((uint16_t*)frompcindex) = (uint16_t)toindex;
goto done;
}
}
done:
p->state--;
return;
overflow:
p->state++; // Halt further profiling
return;
}
// Write out the GMON.OUT file using internal state
void _writeProfile(int (*writeCB)(const void *data, int len)) {
struct gmonhdr { // GMON.OUT header
size_t lpc; // base pc address of sample buffer
size_t hpc; // max pc address of sampled buffer
int ncnt; // size of sample buffer (plus this header)
int version; // version number
int profrate; // profiling clock rate
int spare[3]; // reserved
};
const unsigned int GMONVERSION = 0x00051879;
struct rawarc { // Per-arc on-disk data format
size_t raw_frompc;
size_t raw_selfpc;
long raw_count;
};
int fromindex;
int endfrom;
size_t frompc;
int toindex;
struct rawarc rawarc;
const int BS = 64;
struct rawarc rawarcbuff[BS];
int rawarcbuffptr = 0;
struct gmonparam *p = &_gmonparam;
struct gmonhdr hdr;
moncontrol(0); // Stop
hdr.lpc = p->lowpc;
hdr.hpc = p->highpc;
hdr.ncnt = p->kcountsize + sizeof(hdr);
hdr.version = GMONVERSION;
hdr.profrate = GMON_HZ;
writeCB((void *)&hdr, sizeof(hdr));
writeCB((void *)p->kcount, p->kcountsize);
endfrom = p->fromssize / sizeof(*p->froms);
for (fromindex = 0; fromindex < endfrom; fromindex++) {
if (p->froms[fromindex] == 0) {
continue;
}
frompc = p->lowpc;
frompc += fromindex * HASHFRACTION * sizeof(*p->froms);
for (toindex = p->froms[fromindex]; toindex != 0; toindex = p->tos[toindex].link) {
rawarc.raw_frompc = frompc;
rawarc.raw_selfpc = getselfpc(&p->tos[toindex]);
rawarc.raw_count = getcount(&p->tos[toindex]);
// Buffer up writes because Semihosting is really slow per write call
rawarcbuff[rawarcbuffptr++] = rawarc;
if (rawarcbuffptr == BS) {
writeCB((void *)rawarcbuff, BS * sizeof(struct rawarc));
rawarcbuffptr = 0;
}
}
}
// Write any remaining bits
if (rawarcbuffptr) {
writeCB((void *)rawarcbuff, rawarcbuffptr * sizeof(struct rawarc));
}
}
// These are referenced by RP2040Support.cpp and called by the runtime init SDK
// Install a periodic PC sampler at the specified frequency
#if defined(__riscv)
void runtime_init_setup_profiling() {
// TODO - is there an equivalent? Or do we need to build a timer IRQ here?
}
#else
#include <hardware/exception.h>
#include <hardware/structs/systick.h>
void runtime_init_setup_profiling() {
exception_set_exclusive_handler(SYSTICK_EXCEPTION, _SystickHandler);
systick_hw->csr = 0x7;
systick_hw->rvr = (F_CPU / GMON_HZ) - 1;
}
#endif
+58
View File
@@ -0,0 +1,58 @@
#if defined(__riscv)
// Originally from https://github.com/sbzpro/riscv-gprof
# define RSIZE 4
.section .text
.align 2
.globl _mcount
_mcount:
addi sp,sp,-4*RSIZE
sw ra, 3*RSIZE(sp)
mv a1,ra
call _mcount_internal; //jal _mcount_internal
lw ra, 3*RSIZE(sp)
addi sp,sp,4*RSIZE
ret
#else
/*
* profiler.S
* Implements the gprof profiler arc counting function.
* Created on: 06.08.2015
* Author: Erich Styger
* Modified for RP2040/RP2350 on Dec 3 2024 by Earle F. Philhower, III.
*/
.syntax unified
.arch armv7-m
.cpu cortex-m0plus
.text
.thumb
.thumb_func
.align 2
.globl __gnu_mcount_nc
.type __gnu_mcount_nc, %function
.section .time_critical
__gnu_mcount_nc:
// LR = to return to
// SP = to-replace-LR with
push {r0, r1, r2, r3}
push {lr}
// Swap 24/0
ldr r0, [sp, #20]
ldr r1, [sp, #0]
str r0, [sp, #0]
str r1, [sp, #20]
mov r1, lr
ldr r0, [sp, #0] /* caller - at the top of the stack */
bl _mcount_internal /* when __gnu_mcount_nc is called */
pop {r0}
mov lr, r0
pop {r0, r1, r2, r3}
pop {pc}
.end __gnu_mcount_nc
#endif
+2 -2
View File
@@ -54,9 +54,9 @@ author = u'Earle F. Philhower, III'
# built documents.
#
# The short X.Y version.
version = u'4.3.1'
version = u'4.4.1'
# The full version, including alpha/beta/rc tags.
release = u'4.3.1'
release = u'4.4.1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
+23
View File
@@ -135,6 +135,29 @@ directory into a new LittleFS flash file system.
- Double check the Serial Monitor is closed. Uploads will fail if the Serial Monitor has control of the serial port.
- Enter ``[Ctrl]`` + ``[Shift]`` + ``[P]`` to bring up the command palette, then select/type ``Upload LittleFS to Pico/ESP8266``
Downloading Files from a LittleFS System
----------------------------------------
Using ``gdb`` it is possible to dump the flash data making up the filesystem and then extract
it using the ``mklittlefs`` tool. A working ``OpenOCD`` setup, DebugProbe, and ``gdb`` are required.
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
----------------------
The included ``SD`` library is the Arduino standard one. Please refer to
+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.
+2
View File
@@ -43,6 +43,8 @@ For the latest version, always check https://github.com/earlephilhower/arduino-p
File Systems (SD, SDFS, LittleFS) <fs>
USB (Arduino and Adafruit_TinyUSB) <usb>
Multicore Processing <multicore>
Semihosting <semihosting>
Profiling (GPROF) <profiling>
RP2350 Specific Notes <rp2350>
RP2350 PSRAM <psram>
+76
View File
@@ -0,0 +1,76 @@
Profiling Applications with GPROF
=================================
Applications running on the Pico can be profiled using GNU GPROF to show where the CPU is using its time
on the device and how often certain functions are called. It does this by recompiling the application
and adding a small preamble to each function built to identify what functions call what others (and
how frequently). It also uses the ``SYSTICK`` exception timer to sample and record the PC 10,000 times
per second. When an application is complete, the recorded date can be dumped to the host PC as a
``gmon.,out`` file which can be processed by ``arm-none-eabi-gprof`` into useful date.
s histogram of PCs and tally of function caller/callees can take a significant amount of RAM, from 100KB
to 10000KB depending on the size of the application. As such, while the RP2040 **may** be able to
profile small applications, this is only really recommended on the RP2350 with external PSRAM. The
profiler will automatically use PSRAM when available. Call ``rp2040.getProfileMemoryUsage()`` to get the
memory allocated at runtime.
Profiling also adds processing overhead in terms of the periodic sampling and the function preambles.
In most cases there is no reason to enable (and many reasons to disable) profiling when an application
is deployed to the field.
To transfer the ``GMON.OUT`` data from the Pico to the host HP can be done by having the application
write it out to an SD card or a LittleFS filesystem which is then manually dumped, but for ease of use
semihosting can be used to allow the Pico (under the control of OpenOCD and GDB) to write the
``gmon.out`` file directly on the host PC, ready for use.
**NOTE** Semihosting only works when connected to an OpenOCD + GDB debug session. Running an application
compiled for Semihosting without the debugger will cause a panic and hang the chip.
As of now, only ARM has support for Semihosting or GPROF.
Enabling Profiling in an Application
------------------------------------
The ``Tools->Profiling->Enabled`` menu needs to be selected to enable profiling support in GCC. This will
add the necessary preamble to every function compiled (**Note** that the ``libpico`` and ``libc`` will not
be instrumented because they are pre-built so calls from them will not be fully instrumented. However,
PC data will still be grabbed and decoded from them at runtime.)
The application will automatically start collecting profiling data even before ``setup`` starts in this
mode. It will continue collecting data until you stop and write out the profiling data using
``rp2040.writeProfiling()`` to dump to the host, a file, serial port, etc.
For example, an application which does all its processing in ``setup()`` might look like:
.. code:: cpp
#include <SemiFS.h>
void setup() {
SerialSemi.printf("BEGIN\n");
do_some_work_that_takes_a_long_time_with_many_function_calls();
// Do lots of other work...
// Now all done...
SerialSemi.printf("Writing GMON.OUT\n");
SemiFS.begin();
File gmon = SemiFS.open("gmon.out", "w");
rp2040.writeProfiling(&gmon);
gmon.close();
SerialSemi.printf("END\n");
}
void loop() {}
Collecting and Analyzing Profile Data
-------------------------------------
Running this application under `semihosting <semihosting>`_ GDB and OpenOCD generates a ``gmon.out`` file
in the OpenOCD current working directory. This file, combined with the ``ELF`` binary build in the
IDE and loaded through GDB, can produce profiler output using
.. code::
$ /path/to/arm-none-eabi/bin/arm-none-eabi-gprof /path/to/sketch.ino.elf /path/to/gmon.out
See the ``rp2040/Profiling.ino`` example for more details.
+59
View File
@@ -0,0 +1,59 @@
Semihosting Support
===================
Using special debugger breakpoints and commands, the Pico can read and write to the debugging console as well
as read and write files on a development PC. The ``Semihosting`` library allows applications to use the
semihosting support as a normal filesystem or serial port.
**NOTE** Semihosting only works when connected to an OpenOCD + GDB debug session. Running an application
compiled for Semihosting without the debugger will cause a panic and hang the chip.
As of now, only ARM has support for Semihosting.
Running Semihosting on the Development Host
-------------------------------------------
Start OpenOCD normally from inside a directory that you can read and write files within (i.e. do not run from
``C:\\Program Files\\..`` on Windows where general users aren't allowed to write). The starting
directory will be where the Pico will read and write files using the ``SemiFS`` class.
Be sure to keep the terminal window you ran OpenOCD in open, because all ``SerialSemi`` input and output
will go to **that** terminal and not ``gdb``'s.
Start GDB normally and connect to the OpenOCD debugger and enable semihosting support
.. code::
(gdb) target extended-remote localhost:3333
(gdb) monitor arm semihosting enable
At this point load and run your ``ELF`` application as normal. Again, all ``SerialSemi`` output will go
to the **OpenOCD** window, not GDB.
See the ``hellosemi`` example in the ``Semihosting`` library.
SerialSemi - Serial over Semihosting
------------------------------------
Simply include ``<Semihosting.h>`` in your application and use ``SerialSemi`` as you would any other
``Serial`` port with the following limitations:
* Baud rate, bit width, etc. are all ignored
* Input is limited because ``read`` may hang indefinitely in the host and ``available`` is not part of the spec
``SerialSemi`` can also be selected as the debug output port in the IDE, in which case ``::printf`` will write
to the debugger directly.
SemiFS - Host filesystem access through Semihosting
---------------------------------------------------
Use ``SemiFS`` the same way as any other file system. Note that only file creation and renaming are supported, with
no provision for iterating over directories or listing files. In most cases simply opening a ``File`` and writing out
a debug dump is all that's needed:
.. code::
SemiFS.begin();
File f = SemiFS.open("debug.dmp", "w");
f.write(buffer, size);
f.close();
SerialSemi.printf("Debug dump now available on host.\n");
+7
View File
@@ -14,6 +14,7 @@ File KEYWORD1
timeval KEYWORD1
time_t KEYWORD1
SoftwareSerial KEYWORD1
AudioOutputBase KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
@@ -65,6 +66,9 @@ getUsedPSRAMHeap KEYWORD2
getTotalPSRAMHeap KEYWORD2
getTotalPSRAM KEYWORD2
getProfileMemoryUsage KEYWORD2
writeProfiling KEYWORD2
getChipID KEYWORD2
hwrand32 KEYWORD2
@@ -83,6 +87,9 @@ digitalReadFast KEYWORD2
enableDoubleResetBootloader KEYWORD2
SerialSemi KEYWORD2
SemiFS KEYWORD2
openDir KEYWORD2
next KEYWORD2
getLastWrite KEYWORD2
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
@@ -42,6 +42,8 @@ AudioBufferManager::AudioBufferManager(size_t bufferCount, size_t bufferWords, i
_dmaSize = dmaSize;
_overunderflow = false;
_callback = nullptr;
_callbackCB = nullptr;
_useData = false;
_userOff = 0;
// Create the silence buffer, fill with appropriate value
@@ -101,11 +103,16 @@ AudioBufferManager::~AudioBufferManager() {
void AudioBufferManager::setCallback(void (*fn)()) {
_callback = fn;
_useData = false;
}
void AudioBufferManager::setCallback(void (*fn)(void *), void *cbData) {
_callbackCB = fn;
_callbackData = cbData;
_useData = true;
}
bool AudioBufferManager::begin(int dreq, volatile void *pioFIFOAddr) {
_running = true;
// Get ping and pong DMA channels
for (auto i = 0; i < 2; i++) {
_channelDMA[i] = dma_claim_unused_channel(false);
@@ -117,6 +124,8 @@ bool AudioBufferManager::begin(int dreq, volatile void *pioFIFOAddr) {
}
}
_running = true;
// Need to know both channels to set up ping-pong, so do in 2 stages
for (auto i = 0; i < 2; i++) {
dma_channel_config c = dma_channel_get_default_config(_channelDMA[i]);
@@ -179,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;
@@ -265,7 +307,9 @@ void __not_in_flash_func(AudioBufferManager::_dmaIRQ)(int channel) {
}
dma_channel_set_trans_count(channel, _wordsPerBuffer * (_dmaSize == DMA_SIZE_16 ? 2 : 1), false);
dma_channel_acknowledge_irq0(channel);
if (_callback) {
if (_callbackCB) {
_callbackCB(_callbackData);
} else if (_callback) {
_callback();
}
}
@@ -29,10 +29,12 @@ public:
~AudioBufferManager();
void setCallback(void (*fn)());
void setCallback(void (*fn)(void *), void *cbData);
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();
@@ -86,14 +88,17 @@ private:
delete ab;
}
int _bitsPerSample;
size_t _wordsPerBuffer;
size_t _bufferCount;
enum dma_channel_transfer_size _dmaSize;
bool _isOutput;
int _channelDMA[2];
bool _useData;
void (*_callback)();
void (*_callbackCB)(void *);
void *_callbackData;
bool _overunderflow;
@@ -220,6 +220,8 @@ void A2DPSource::clearPairing() {
size_t A2DPSource::write(const uint8_t *buffer, size_t size) {
BluetoothLock b;
size = std::min((size_t)availableForWrite(), size);
size_t count = 0;
size /= 2;
@@ -260,6 +262,7 @@ int A2DPSource::availableForWrite() {
} else {
avail = _pcmBufferSize - _pcmWriter + _pcmReader - 1;
}
avail /= sizeof(uint32_t); // availableForWrite always 32b sample pairs in this core...
return avail;
}
+22 -5
View File
@@ -24,17 +24,20 @@
#include <BluetoothHCI.h>
#include <BluetoothLock.h>
#include "BluetoothMediaConfigurationSBC.h"
#include <AudioOutputBase.h>
#include <functional>
#include <list>
#include <memory>
class A2DPSource : public Stream {
class A2DPSource : public Stream, public AudioOutputBase {
public:
A2DPSource() {
}
bool setFrequency(uint32_t rate) {
virtual ~A2DPSource() { }
virtual bool setFrequency(int rate) override {
if (_running || ((rate != 44100) && (rate != 48000))) {
return false;
}
@@ -51,7 +54,14 @@ public:
return true;
}
void onTransmit(void (*cb)(void *), void *cbData = nullptr) {
virtual bool setBitsPerSample(int bps) override {
return bps == 16;
}
virtual bool setStereo(bool stereo = true) override {
return stereo;
}
virtual void onTransmit(void (*cb)(void *), void *cbData = nullptr) override {
_transmitCB = cb;
_transmitData = cbData;
}
@@ -84,7 +94,11 @@ public:
return true;
}
bool getUnderflow() {
virtual bool setBuffers(size_t buffers, size_t bufferWords, int32_t silenceSample = 0) override {
return setBufferSize(buffers * bufferWords * sizeof(int32_t));
}
virtual bool getUnderflow() override {
BluetoothLock b;
if (!_running) {
return false;
@@ -102,7 +116,10 @@ public:
}
}
bool begin();
virtual bool begin() override;
virtual bool end() override {
return false; // We can't actually stop bluetooth on this device
}
std::vector<BTDeviceInfo> scan(uint32_t mask = BluetoothHCI::speaker_cod, int scanTimeSec = 5, bool async = false) {
return _hci.scan(mask, scanTimeSec, async);
@@ -19,14 +19,14 @@
#include <I2S.h>
// Create the I2S port using a PIO state machine
I2S i2s(OUTPUT);
// GPIO pin numbers
#define pBCLK 20
#define pWS (pBCLK+1)
#define pDOUT 22
// Create the I2S port using a PIO state machine
I2S i2s(OUTPUT, pBCLK, pDOUT);
const int frequency = 440; // frequency of square wave in Hz
const int amplitude = 500; // amplitude of square wave
const int sampleRate = 16000; // minimum for UDA1334A
@@ -43,8 +43,6 @@ void setup() {
Serial.begin(115200);
Serial.println("I2S simple tone");
i2s.setBCLK(pBCLK);
i2s.setDATA(pDOUT);
i2s.setBitsPerSample(16);
// start I2S at the sample rate with 16-bits per sample
+35 -24
View File
@@ -24,14 +24,14 @@
#include <pico/stdlib.h>
I2S::I2S(PinMode direction) {
I2S::I2S(PinMode direction, pin_size_t bclk, pin_size_t data, pin_size_t mclk) {
_running = false;
_bps = 16;
_writtenHalf = false;
_isOutput = direction == OUTPUT;
_pinBCLK = 26;
_pinDOUT = 28;
_pinMCLK = 25;
_pinBCLK = bclk;
_pinDOUT = data;
_pinMCLK = mclk;
_MCLKenabled = false;
#ifdef PIN_I2S_BCLK
_pinBCLK = PIN_I2S_BCLK;
@@ -51,6 +51,7 @@ I2S::I2S(PinMode direction) {
_freq = 48000;
_arb = nullptr;
_cb = nullptr;
_cbd = nullptr;
_buffers = 6;
_bufferWords = 0;
_silenceSample = 0;
@@ -122,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;
}
@@ -185,6 +184,16 @@ void I2S::onTransmit(void(*fn)(void)) {
}
}
void I2S::onTransmit(void(*fn)(void *), void *cbData) {
if (_isOutput) {
_cbd = fn;
_cbdata = cbData;
if (_running) {
_arb->setCallback(_cbd, _cbdata);
}
}
}
void I2S::onReceive(void(*fn)(void)) {
if (!_isOutput) {
_cb = fn;
@@ -194,6 +203,16 @@ void I2S::onReceive(void(*fn)(void)) {
}
}
void I2S::onReceive(void(*fn)(void *), void *cbData) {
if (!_isOutput) {
_cbd = fn;
_cbdata = cbData;
if (_running) {
_arb->setCallback(_cbd, _cbdata);
}
}
}
void I2S::MCLKbegin() {
int off = 0;
_i2sMCLK = new PIOProgram(&pio_i2s_mclk_program);
@@ -256,13 +275,17 @@ bool I2S::begin() {
_i2s = nullptr;
return false;
}
_arb->setCallback(_cb);
if (_cbd) {
_arb->setCallback(_cbd, _cbdata);
} else {
_arb->setCallback(_cb);
}
pio_sm_set_enabled(_pio, _sm, true);
return true;
}
void I2S::end() {
bool I2S::end() {
if (_running) {
if (_MCLKenabled) {
pio_sm_set_enabled(_pioMCLK, _smMCLK, false);
@@ -276,6 +299,7 @@ void I2S::end() {
delete _i2s;
_i2s = nullptr;
}
return true;
}
int I2S::available() {
@@ -476,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() {
+21 -9
View File
@@ -21,19 +21,23 @@
#pragma once
#include <Arduino.h>
#include "AudioBufferManager.h"
#include <AudioBufferManager.h>
#include <AudioOutputBase.h>
class I2S : public Stream {
class I2S : public Stream, public AudioOutputBase {
public:
I2S(PinMode direction = OUTPUT);
I2S(PinMode direction = OUTPUT, pin_size_t bclk = 26, pin_size_t data = 28, pin_size_t mclk = 25);
virtual ~I2S();
bool setBCLK(pin_size_t pin);
bool setDATA(pin_size_t pin);
bool setMCLK(pin_size_t pin);
bool setBitsPerSample(int bps);
bool setBuffers(size_t buffers, size_t bufferWords, int32_t silenceSample = 0);
bool setFrequency(int newFreq);
virtual bool setBitsPerSample(int bps) override;
virtual bool setBuffers(size_t buffers, size_t bufferWords, int32_t silenceSample = 0) override;
virtual bool setFrequency(int newFreq) override;
virtual bool setStereo(bool stereo = true) override {
return stereo;
}
bool setLSBJFormat();
bool setTDMFormat();
bool setTDMChannels(int channels);
@@ -46,8 +50,11 @@ public:
return begin();
}
bool begin();
void end();
virtual bool begin() override;
virtual bool end() override;
virtual bool getUnderflow() override {
return getOverUnderflow();
}
// from Stream
virtual int available() override;
@@ -110,7 +117,9 @@ public:
// Note that these callback are called from **INTERRUPT CONTEXT** and hence
// should be in RAM, not FLASH, and should be quick to execute.
void onTransmit(void(*)(void));
void onTransmit(void(*)(void *), void *);
void onReceive(void(*)(void));
void onReceive(void(*)(void *), void *);
private:
pin_size_t _pinBCLK;
@@ -142,6 +151,9 @@ private:
int _isHolding = 0;
void (*_cb)();
void (*_cbd)(void *);
void *_cbdata;
void MCLKbegin();
AudioBufferManager *_arb;
@@ -151,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
};
+42 -8
View File
@@ -20,6 +20,7 @@
*/
#include <Arduino.h>
#include "PWMAudio.h"
#include "PWMAudioPrecalc.h"
#include <hardware/pwm.h>
@@ -40,7 +41,8 @@ PWMAudio::~PWMAudio() {
end();
}
bool PWMAudio::setBuffers(size_t buffers, size_t bufferWords) {
bool PWMAudio::setBuffers(size_t buffers, size_t bufferWords, int32_t silence) {
(void) silence;
if (_running || (buffers < 3) || (bufferWords < 8)) {
return false;
}
@@ -95,13 +97,18 @@ bool PWMAudio::setPWMFrequency(int newFreq) {
}
bool PWMAudio::setFrequency(int frequency) {
if (frequency == _sampleRate) {
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*/
// 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
find_pacer_fraction(frequency, &_pacer_D, &_pacer_N);
dma_timer_set_fraction(_pacer, _pacer_N, _pacer_D);
_sampleRate = frequency;
return true;
}
@@ -112,6 +119,14 @@ void PWMAudio::onTransmit(void(*fn)(void)) {
}
}
void PWMAudio::onTransmit(void(*fn)(void *), void *cbData) {
_cbd = fn;
_cbdata = cbData;
if (_running) {
_arb->setCallback(_cbd, _cbdata);
}
}
bool PWMAudio::begin() {
if (_running) {
return false;
@@ -132,15 +147,16 @@ bool PWMAudio::begin() {
setPWMFrequency(_freq);
/*Calculate and set the DMA pacer timer. This timer will pull data on a fixed sample rate. So the actual PWM frequency can be higher or lower.*/
// Calculate and set the DMA pacer timer. This timer will pull data on a fixed sample rate. So the actual PWM frequency can be higher or lower.
_pacer = dma_claim_unused_timer(false);
/*When no unused timer is found, return*/
// When no unused timer is found, return
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*/
// 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
find_pacer_fraction(_sampleRate, &_pacer_D, &_pacer_N);
dma_timer_set_fraction(_pacer, _pacer_N, _pacer_D);
int _pacer_dreq = dma_get_timer_dreq(_pacer);
@@ -155,12 +171,16 @@ bool PWMAudio::begin() {
return false;
}
_arb->setCallback(_cb);
if (_cbd) {
_arb->setCallback(_cbd, _cbdata);
} else {
_arb->setCallback(_cb);
}
return true;
}
void PWMAudio::end() {
bool PWMAudio::end() {
if (_running) {
_running = false;
pinMode(_pin, OUTPUT);
@@ -173,6 +193,7 @@ void PWMAudio::end() {
dma_timer_unclaim(_pacer);
_pacer = -1;
}
return true;
}
int PWMAudio::available() {
@@ -263,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;
+22 -7
View File
@@ -21,20 +21,24 @@
#pragma once
#include <Arduino.h>
#include "AudioBufferManager.h"
#include <AudioOutputBase.h>
#include <AudioBufferManager.h>
class PWMAudio : public Stream {
class PWMAudio : public Stream, public AudioOutputBase {
public:
PWMAudio(pin_size_t pin = 0, bool stereo = false);
virtual ~PWMAudio();
bool setBuffers(size_t buffers, size_t bufferWords);
virtual bool setBuffers(size_t buffers, size_t bufferWords, int32_t silenceSample = 0) override;
/*Sets the frequency of the PWM in hz*/
bool setPWMFrequency(int newFreq);
/*Sets the sample rate frequency in hz*/
bool setFrequency(int frequency);
virtual bool setFrequency(int frequency) override;
bool setPin(pin_size_t pin);
bool setStereo(bool stereo = true);
virtual bool setStereo(bool stereo = true) override;
virtual bool setBitsPerSample(int bits) override {
return bits == 16;
}
bool begin(long sampleRate) {
_sampleRate = sampleRate;
@@ -47,8 +51,8 @@ public:
return begin();
}
bool begin();
void end();
virtual bool begin() override;
virtual bool end() override;
// from Stream
virtual int available() override;
@@ -73,6 +77,15 @@ public:
// Note that these callback are called from **INTERRUPT CONTEXT** and hence
// should be in RAM, not FLASH, and should be quick to execute.
void onTransmit(void(*)(void));
void onTransmit(void(*)(void *), void *data);
bool getUnderflow() {
if (!_running) {
return false;
} else {
return _arb->getOverUnderflow();
}
}
private:
pin_size_t _pin;
@@ -94,6 +107,8 @@ private:
uint32_t _holdWord;
void (*_cb)();
void (*_cbd)(void *);
void *_cbdata;
AudioBufferManager *_arb;
+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
+69 -6
View File
@@ -42,7 +42,7 @@ bool SimpleMDNS::begin(const char *hostname, unsigned int ttl) {
}
void SimpleMDNS::enableArduino(unsigned int port, bool passwd) {
if (!_running) {
if (!_running || _arduinoAdded) {
return;
}
struct netif *n = netif_list;
@@ -50,20 +50,28 @@ void SimpleMDNS::enableArduino(unsigned int port, bool passwd) {
mdns_resp_add_service(n, _hostname, "_arduino", DNSSD_PROTO_TCP, port, _arduinoGetTxt, (void *)passwd);
n = n->next;
}
_arduinoAdded = true;
}
void SimpleMDNS::addService(const char *service, const char *proto, unsigned int port) {
hMDNSService SimpleMDNS::addService(const char *service, const char *proto, unsigned int port) {
if (!_running) {
return;
return nullptr;
}
if (_svcMap.find(service) != _svcMap.end()) {
// Duplicates = error
return nullptr;
}
char s[128];
snprintf(s, sizeof(s), "_%s", service);
s[sizeof(s) - 1] = 0;
SimpleMDNSService *svc = new SimpleMDNSService();
_svcMap.insert({strdup(service), svc});
struct netif *n = netif_list;
while (n) {
mdns_resp_add_service(n, _hostname, s, !strcasecmp("tcp", proto) ? DNSSD_PROTO_TCP : DNSSD_PROTO_UDP, port, _nullGetTxt, nullptr);
mdns_resp_add_service(n, _hostname, s, !strcasecmp("tcp", proto) ? DNSSD_PROTO_TCP : DNSSD_PROTO_UDP, port, SimpleMDNSService::callback, (void *)svc);
n = n->next;
}
return (hMDNSService*) service;
}
void SimpleMDNS::update() {
@@ -89,10 +97,65 @@ void SimpleMDNS::_arduinoGetTxt(struct mdns_service *service, void *txt_userdata
_addServiceTxt(service, (bool)txt_userdata ? "auth_upload=yes" : "auth_upload=no");
}
void SimpleMDNS::_nullGetTxt(struct mdns_service *service, void *txt_userdata) {
/* nop */
SimpleMDNSService::SimpleMDNSService() {
}
void SimpleMDNSService::callback(struct mdns_service *service, void *txt_userdata) {
SimpleMDNSService *obj = (SimpleMDNSService *)txt_userdata;
for (auto s : obj->txt) {
mdns_resp_add_service_txtitem(service, s, strlen(s));
}
}
hMDNSTxt SimpleMDNSService::add(const char *key, const char *value) {
char s[128];
snprintf(s, sizeof(s), "%s=%s", key, value);
s[sizeof(s) - 1] = 0;
char *copy = strdup(s);
txt.push_back(copy);
return (void *)copy; // Do not use...
};
// Add a (static) MDNS TXT item ('key' = 'value') to the service
hMDNSTxt SimpleMDNS::addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, const char* p_pcValue) {
const char *s = (const char *)p_hService;
auto o = _svcMap.find(s);
if (o != _svcMap.end()) {
return o->second->add(p_pcKey, p_pcValue);
}
return nullptr;
}
hMDNSTxt SimpleMDNS::addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, uint32_t p_u32Value) {
char s[16];
sprintf(s, "%lu", p_u32Value);
return addServiceTxt(p_hService, p_pcKey, s);
}
hMDNSTxt SimpleMDNS::addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, uint16_t p_u16Value) {
return addServiceTxt(p_hService, p_pcKey, (uint32_t)p_u16Value);
}
hMDNSTxt SimpleMDNS::addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, uint8_t p_u8Value) {
return addServiceTxt(p_hService, p_pcKey, (uint32_t)p_u8Value);
}
hMDNSTxt SimpleMDNS::addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, int32_t p_i32Value) {
char s[16];
sprintf(s, "%ld", p_i32Value);
return addServiceTxt(p_hService, p_pcKey, s);
}
hMDNSTxt SimpleMDNS::addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, int16_t p_i16Value) {
return addServiceTxt(p_hService, p_pcKey, (int32_t)p_i16Value);
}
hMDNSTxt SimpleMDNS::addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, int8_t p_i8Value) {
return addServiceTxt(p_hService, p_pcKey, (int32_t)p_i8Value);
}
const char *SimpleMDNS::_hostname = nullptr;
SimpleMDNS MDNS;
+34 -2
View File
@@ -21,13 +21,44 @@
#pragma once
#include <Arduino.h>
#include <string>
#include <map>
#include <vector>
typedef void* hMDNSTxt; // Unusable in SimpleMDNS, for signature compatibility only
class SimpleMDNSService {
public:
SimpleMDNSService();
static void callback(struct mdns_service *service, void *txt_userdata);
hMDNSTxt add(const char *key, const char *val);
private:
std::vector<const char *> txt;
};
// hMDNSService (opaque handle to access the service)
typedef const void* hMDNSService;
class SimpleMDNS {
public:
bool begin(const char *hostname, unsigned int ttl = 60);
void enableArduino(unsigned int port, bool passwd = false);
void addService(const char *service, const char *proto, unsigned int port);
hMDNSService addService(const char *service, const char *proto, unsigned int port);
hMDNSService addService(const char *name, const char *service, const char *proto, unsigned int port) {
(void) name; // Ignored
return addService(service, proto, port);
}
// Add a (static) MDNS TXT item ('key' = 'value') to the service
hMDNSTxt addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, const char* p_pcValue);
hMDNSTxt addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, uint32_t p_u32Value);
hMDNSTxt addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, uint16_t p_u16Value);
hMDNSTxt addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, uint8_t p_u8Value);
hMDNSTxt addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, int32_t p_i32Value);
hMDNSTxt addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, int16_t p_i16Value);
hMDNSTxt addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, int8_t p_i8Value);
// No-ops here
void end();
@@ -37,10 +68,11 @@ private:
static void _statusCB(struct netif *netif);
static void _addServiceTxt(struct mdns_service *service, const char *str);
static void _arduinoGetTxt(struct mdns_service *service, void *txt_userdata);
static void _nullGetTxt(struct mdns_service *service, void *txt_userdata);
bool _running = false;
static const char *_hostname;
std::map<std::string, SimpleMDNSService*> _svcMap;
bool _arduinoAdded = false;
};
extern SimpleMDNS MDNS;
+10 -7
View File
@@ -160,16 +160,14 @@ uint8_t WiFiClass::beginAP(const char *ssid) {
}
uint8_t WiFiClass::beginAP(const char *ssid, uint8_t channel) {
(void) channel;
return beginAP(ssid, nullptr);
}
uint8_t WiFiClass::beginAP(const char *ssid, const char* passphrase, uint8_t channel) {
(void) channel;
return beginAP(ssid, passphrase);
return beginAP(ssid, nullptr, channel);
}
uint8_t WiFiClass::beginAP(const char *ssid, const char* passphrase) {
return beginAP(ssid, passphrase, 0);
}
uint8_t WiFiClass::beginAP(const char *ssid, const char* passphrase, uint8_t channel) {
end();
_ssid = ssid;
@@ -177,6 +175,11 @@ uint8_t WiFiClass::beginAP(const char *ssid, const char* passphrase) {
_wifi.setAP();
_wifi.setSSID(_ssid.c_str());
_wifi.setPassword(passphrase);
#if defined(PICO_CYW43_SUPPORTED)
if (channel > 0) {
cyw43_wifi_ap_set_channel(&cyw43_state, channel);
}
#endif
_wifi.setTimeout(_timeout);
_apMode = true;
IPAddress gw = _wifi.gatewayIP();
@@ -0,0 +1,104 @@
// This example should be run with profiling enabled from the IDE and
// under GDB/OpenOCD. It uses semihosting to write a gmon.out file
// the host system with the profiled application results.
//
// Semihosting **ONLY** works with an OpenOCD and GDB setup. If you build
// and run a semihosting app without GDB connected, it **WILL CRASH**
//
// Start OpenOCD normally, but leave the terminal window visible because
// is it OpenOCD, not GDB, which will display the semihosting output.
// OpenOCD will also create files in the current working directory, so
// be sure it is a place you can find and write to.
//
// In GDB,connect to OpenOCD and then enable semihosting
// (gdb) target extended-remote localhost:3333
// (gdb) monitor arm semihosting enable
// (gdb) file /path/to/sketch.ino.elf
// (gdb) load
//
// Run the app from GDB and watch OpenOCD, it will display messages when
// the app is done and "gmon.out" is on the host system.
//
// (gdb) run
// .. pop to OpenOCD window
// [OpenOCD] BEGIN
// [OpenOCD] Result = 2417697592
// [OpenOCD] Writing GMON.OUT
// [OpenOCD] END
//
// From command line, decode the gmon.out using the ELF and gprof tool
//
// $ /path/to/arm-none-eabi/bin/arm-none-eabi-gprof /path/to/sketch.ino.elf /path/to/gmon.out | less
// Flat profile:
//
// Each sample counts as 0.0001 seconds.
// % cumulative self self total
// time seconds seconds calls ms/call ms/call name
// 50.56 1.74 1.74 3500020 0.00 0.00 __wrap___getreent
// 24.05 2.57 0.83 rand
// 8.32 2.86 0.29 5 57.36 57.36 fcn1(unsigned long)
// ...
// index % time self children called name
// <spontaneous>
// [1] 74.6 0.83 1.74 rand [1]
// 1.74 0.00 3500000/3500020 __wrap___getreent [2]
// -----------------------------------------------
// 0.00 0.00 1/3500020 realloc [106]
// 0.00 0.00 3/3500020 vsnprintf [54]
// 0.00 0.00 7/3500020 srand [7]
// 0.00 0.00 9/3500020 malloc [105]
// 1.74 0.00 3500000/3500020 rand [1]
// ...
#ifndef __PROFILE
void setup() {
Serial.printf("Enable profiling to run this example.\n");
}
void loop() {
}
#else
#ifdef __riscv
void setup() {
// No semihosting for RISCV yet
}
void loop() {
}
#else
#include <SemiFS.h>
uint32_t fcn1(uint32_t st) {
srand(st);
for (int i = 0; i < 500000; i++) {
st += rand();
}
return st;
}
uint32_t fcn2(uint32_t st) {
srand(st * st);
for (int i = 0; i < 500000; i++) {
st += rand();
}
return st;
}
void setup() {
SerialSemi.printf("BEGIN\n");
SerialSemi.printf("Result = %lu\n", fcn2(fcn2(fcn1(3)) * fcn1(fcn1(fcn1(fcn1(2))))));
SerialSemi.printf("Writing GMON.OUT\n");
SemiFS.begin();
File gmon = SemiFS.open("gmon.out", "w");
rp2040.writeProfiling(&gmon);
gmon.close();
SerialSemi.printf("END\n");
}
void loop() {
}
#endif
#endif // !__PROFILE
@@ -0,0 +1,43 @@
// This example uses semihosting to send serial output to the OpenOcD screen
// and write binary files to the host system.
//
// Semihosting **ONLY** works with an OpenOCD and GDB setup. If you build
// and run a semihosting app without GDB connected, it **WILL CRASH**
//
// Start OpenOCD normally, but leave the terminal window visible because
// is it OpenOCD, not GDB, which will display the semihosting output.
// OpenOCD will also create files in the current working directory, so
// be sure it is a place you can find and write to.
//
// In GDB,connect to OpenOCD and then enable semihosting
// (gdb) target extended-remote localhost:3333
// (gdb) monitor arm semihosting enable
#ifdef __riscv
void setup() {
// No semihosting for RISCV yet
}
void loop() {
}
#else
#include <SemiFS.h> // For SemiFS.open()
int c = 0;
void setup() {
SerialSemi.begin();
SerialSemi.printf("HELLO, GDB!\n");
SemiFS.begin();
File f = SemiFS.open("out.bin", "w");
f.printf("I made a file!\n");
f.close();
SerialSemi.printf("Just wrote a file 'out.bin'\n");
}
void loop() {
SerialSemi.printf("SH Loop Count: %d\n", c++);
Serial.printf("USB Loop Count: %d\n", c++);
delay(1000);
}
#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.40301.0",
"version": "1.40401.0",
"description": "Arduino Wiring-based Framework (RPi Pico RP2040, RP2350)",
"keywords": [
"framework",
+9 -3
View File
@@ -215,6 +215,9 @@
{
"name": "METE HOCA Akana R1"
},
{
"name": "MyMakers RP2040"
},
{
"name": "Neko Systems BL2040 Mini"
},
@@ -245,6 +248,9 @@
{
"name": "Pimoroni Plasma2040"
},
{
"name": "Pimoroni Plasma2350"
},
{
"name": "Pimoroni Tiny2040"
},
@@ -293,9 +299,6 @@
{
"name": "SparkFun Thing Plus RP2350"
},
{
"name": "uPesy RP2040 DevKit"
},
{
"name": "Seeed INDICATOR RP2040"
},
@@ -305,6 +308,9 @@
{
"name": "Seeed XIAO RP2350"
},
{
"name": "uPesy RP2040 DevKit"
},
{
"name": "VCC-GND YD RP2040"
},
+3 -2
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.3.1
version=4.4.1
# Required discoveries and monitors
# ---------------------------------
@@ -64,7 +64,7 @@ compiler.c.elf.flags={compiler.warning_flags} {compiler.defines} {compiler.flags
compiler.S.cmd={build.toolchain}-gcc
compiler.S.flags=-c {compiler.warning_flags} {compiler.defines} -g -x assembler-with-cpp -MMD {compiler.includes} {build.toolchainopts} -g
compiler.cpp.cmd={build.toolchain}-g++
compiler.cpp.flags=-c {compiler.warning_flags} {compiler.defines} {compiler.flags} -MMD {compiler.includes} {build.flags.rtti} -std=gnu++17 -g -pipe
compiler.cpp.flags=-c {compiler.warning_flags} {compiler.defines} {compiler.flags} -MMD {compiler.includes} {build.flags.rtti} {build.flags.profile} -std=gnu++17 -g -pipe
compiler.ar.cmd={build.toolchain}-ar
compiler.ar.flags=rcs
@@ -98,6 +98,7 @@ build.psram_freq=
build.eeprom_start=
build.flags.optimize=-Os
build.flags.rtti=-fno-rtti
build.flags.profile=
build.fs_start=
build.fs_end=
build.usbstack_flags=
+1 -1
View File
@@ -17,7 +17,7 @@ for dir in ./cores/rp2040 ./libraries/EEPROM ./libraries/I2S ./libraries/SingleF
./libraries/SPISlave ./libraries/lwIP_ESPHost ./libraries/FatFS\
./libraries/FatFSUSB ./libraries/BluetoothAudio ./libraries/BluetoothHCI \
./libraries/BluetoothHIDMaster ./libraries/NetBIOS ./libraries/Ticker \
./libraries/VFS ./libraries/rp2350 ./libraries/SimpleMDNS; do
./libraries/VFS ./libraries/rp2350 ./libraries/SimpleMDNS ; do
find $dir -type f \( -name "*.c" -o -name "*.h" -o -name "*.cpp" \) -a \! -path '*api*' -exec astyle --suffix=none --options=./tests/astyle_core.conf \{\} \;
find $dir -type f -name "*.ino" -exec astyle --suffix=none --options=./tests/astyle_examples.conf \{\} \;
done
+2
View File
@@ -72,6 +72,8 @@ def compile(tmp_dir, sketch, cache, tools_dir, hardware_dir, ide_path, f, args):
fqbn = fqbn.replace("rpipico", "rpipicow")
if ('/BT' in sketch) or ('/BLE' in sketch) or ('/Bluetooth' in sketch):
fqbn = fqbn + ",ipbtstack=ipv4btcble"
if '/Profiling' in sketch:
fqbn = fqbn + ",profile=Enabled"
cmd += [fqbn]
cmd += ['-built-in-libraries', ide_path + '/libraries']
cmd += ['-ide-version=10607']
+55
View File
@@ -0,0 +1,55 @@
{
"build": {
"arduino": {
"earlephilhower": {
"boot2_source": "boot2_generic_03h_4_padded_checksum.S",
"usb_vid": "0x2E8A",
"usb_pid": "0x000A"
}
},
"core": "earlephilhower",
"cpu": "cortex-m0plus",
"extra_flags": "-DARDUINO_MyRP_2040 -DARDUINO_ARCH_RP2040 -DUSBD_MAX_POWER_MA=250 ",
"f_cpu": "133000000L",
"hwids": [
[
"0x2E8A",
"0x00C0"
],
[
"0x2E8A",
"0x000A"
]
],
"mcu": "rp2040",
"variant": "MyRP_bot"
},
"debug": {
"jlink_device": "RP2040_M0_0",
"openocd_target": "rp2040.cfg",
"svd_path": "rp2040.svd"
},
"frameworks": [
"arduino"
],
"name": "RP2040",
"upload": {
"maximum_ram_size": 262144,
"maximum_size": 2097152,
"require_upload_port": true,
"native_usb": true,
"use_1200bps_touch": true,
"wait_for_upload_port": false,
"protocol": "picotool",
"protocols": [
"blackmagic",
"cmsis-dap",
"jlink",
"raspberrypi-swd",
"picotool",
"picoprobe"
]
},
"url": "https://www.raspberrypi.org/products/raspberry-pi-pico/",
"vendor": "MyMakers"
}
+1 -1
View File
@@ -2,7 +2,7 @@
"build": {
"arduino": {
"earlephilhower": {
"boot2_source": "boot2_generic_03h_2_padded_checksum.S",
"boot2_source": "boot2_generic_03h_4_padded_checksum.S",
"usb_vid": "0x2E8A",
"usb_pid": "0xF00A"
}
+55
View File
@@ -0,0 +1,55 @@
{
"build": {
"arduino": {
"earlephilhower": {
"boot2_source": "none.S",
"usb_vid": "0x2E8A",
"usb_pid": "0x10A5"
}
},
"core": "earlephilhower",
"cpu": "cortex-m33",
"extra_flags": "-DARDUINO_PIMORONI_PLASMA2040 -DARDUINO_ARCH_RP2040 -DUSBD_MAX_POWER_MA=500 ",
"f_cpu": "150000000L",
"hwids": [
[
"0x2E8A",
"0x00C0"
],
[
"0x2E8A",
"0x10A5"
]
],
"mcu": "rp2350",
"variant": "pimoroni_plasma2350"
},
"debug": {
"jlink_device": "RP2350_0",
"openocd_target": "rp2350.cfg",
"svd_path": "rp2350.svd"
},
"frameworks": [
"arduino"
],
"name": "Plasma2350",
"upload": {
"maximum_ram_size": 524288,
"maximum_size": 2097152,
"require_upload_port": true,
"native_usb": true,
"use_1200bps_touch": true,
"wait_for_upload_port": false,
"protocol": "picotool",
"protocols": [
"blackmagic",
"cmsis-dap",
"jlink",
"raspberrypi-swd",
"picotool",
"picoprobe"
]
},
"url": "https://www.raspberrypi.org/products/raspberry-pi-pico/",
"vendor": "Pimoroni"
}
+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()
+27 -8
View File
@@ -27,7 +27,7 @@ def BuildFlashMenu(name, chip, flashsize, fssizelist):
def BuildDebugPort(name):
print("%s.menu.dbgport.Disabled=Disabled" % (name))
print("%s.menu.dbgport.Disabled.build.debug_port=" % (name))
for p in ["Serial", "Serial1", "Serial2"]:
for p in ["Serial", "Serial1", "Serial2", "SerialSemi"]:
print("%s.menu.dbgport.%s=%s" % (name, p, p))
print("%s.menu.dbgport.%s.build.debug_port=-DDEBUG_RP2040_PORT=%s" % (name, p, p))
@@ -56,6 +56,8 @@ def BuildArch(name):
print("%s.menu.arch.arm.build.toolchainpkg=pqt-gcc" % (name))
print("%s.menu.arch.arm.build.toolchainopts=-mcpu=cortex-m33 -mthumb -march=armv8-m.main+fp+dsp -mfloat-abi=softfp -mcmse" % (name))
print("%s.menu.arch.arm.build.uf2family=--family rp2350-arm-s --abs-block" % (name))
print("%s.menu.arch.arm.build.mcu=cortex-m33" % (name))
# RISC-V Hazard3
print("%s.menu.arch.riscv=RISC-V" % (name))
print("%s.menu.arch.riscv.build.chip=%s" % (name, "rp2350-riscv"))
@@ -63,6 +65,7 @@ def BuildArch(name):
print("%s.menu.arch.riscv.build.toolchainpkg=pqt-gcc-riscv" % (name))
print("%s.menu.arch.riscv.build.toolchainopts=-march=rv32imac_zicsr_zifencei_zba_zbb_zbs_zbkb -mabi=ilp32" % (name))
print("%s.menu.arch.riscv.build.uf2family=--family rp2350-riscv --abs-block" % (name))
print("%s.menu.arch.riscv.build.mcu=rv32imac" % (name))
def BuildPSRAM(name):
for s in [ 0, 2, 4, 8]:
@@ -88,10 +91,17 @@ def BuildRP2350Variant(name):
def BuildOptimize(name):
for l in [ ("Small", "Small", "-Os", " (standard)"), ("Optimize", "Optimize", "-O", ""), ("Optimize2", "Optimize More", "-O2", ""),
("Optimize3", "Optimize Even More", "-O3", ""), ("Fast", "Fast", "-Ofast", " (maybe slower)"), ("Debug", "Debug", "-Og", "") ]:
("Optimize3", "Optimize Even More", "-O3", ""), ("Fast", "Fast", "-Ofast", " (maybe slower)"), ("Debug", "Debug", "-Og", ""),
("Disabled", "Disabled", "-O0", "") ]:
print("%s.menu.opt.%s=%s (%s)%s" % (name, l[0], l[1], l[2], l[3]))
print("%s.menu.opt.%s.build.flags.optimize=%s" % (name, l[0], l[2]))
def BuildProfile(name):
print("%s.menu.profile.Disabled=Disabled" % (name))
print("%s.menu.profile.Disabled.build.flags.profile=" % (name))
print("%s.menu.profile.Enabled=Enabled" % (name))
print("%s.menu.profile.Enabled.build.flags.profile=-pg -D__PROFILE" % (name))
def BuildRTTI(name):
print("%s.menu.rtti.Disabled=Disabled" % (name))
print("%s.menu.rtti.Disabled.build.flags.rtti=-fno-rtti" % (name))
@@ -237,8 +247,9 @@ def BuildHeader(name, chip, chaintuple, chipoptions, vendor_name, product_name,
print("%s.build.usbpid=-DUSBD_PID=%s" % (name, main_pid))
print("%s.build.usbpwr=-DUSBD_MAX_POWER_MA=%s" % (name, pwr))
print("%s.build.board=%s" % (name, boarddefine))
# print("%s.build.mcu=cortex-m0plus" % (name))
if chip == "rp2040": # RP2350 has menu for this later on
print("%s.build.mcu=cortex-m0plus" % (name))
print("%s.build.chip=%s" % (name, chip))
print("%s.build.toolchain=%s" % (name, chaintuple))
print("%s.build.toolchainpkg=%s" % (name, "pqt-gcc"))
@@ -259,6 +270,7 @@ def BuildHeader(name, chip, chaintuple, chipoptions, vendor_name, product_name,
print('%s.build.usb_product="%s"' % (name, product_name))
if ((chip == "rp2350") or (chip == "rp2350-riscv")) and (name != "generic_rp2350"):
print("%s.build.psram_length=0x%d00000" % (name, psramsize))
if extra != None:
m_extra = ''
for m_item in extra:
@@ -281,6 +293,7 @@ def BuildGlobalMenuList():
print("menu.freq=CPU Speed")
print("menu.arch=CPU Architecture")
print("menu.opt=Optimize")
print("menu.profile=Profiling")
print("menu.rtti=RTTI")
print("menu.stackprotect=Stack Protector")
print("menu.exceptions=C++ Exceptions")
@@ -352,6 +365,7 @@ def MakeBoard(name, chip, vendor_name, product_name, vid, pid, pwr, boarddefine,
else:
BuildFreq(name, 133)
BuildOptimize(name)
BuildProfile(name)
BuildRTTI(name)
BuildStackProtect(name)
BuildExceptions(name)
@@ -503,7 +517,7 @@ MakeBoard("adafruit_kb2040", "rp2040", "Adafruit", "KB2040", "0x239a", "0x8105",
MakeBoard("adafruit_feather_rp2350_hstx", "rp2350", "Adafruit", "Feather RP2350 HSTX", "0x239a", "0x814f", 250, "ADAFRUIT_FEATHER_RP2350_HSTX", 8, 0, "none")
MakeBoard("adafruit_floppsy", "rp2040", "Adafruit", "Floppsy", "0x239a", "0x8151", 250, "ADAFRUIT_FLOPPSY_RP2040", 16, 0, "boot2_w25q080_2_padded_checksum")
#Amken
# Amken
MakeBoard("amken_bunny", "rp2040","Amken","BunnyBoard","0x2770",["0x7303"],250,"AMKEN_BB",128,0,"boot2_w25q128jvxq_4_padded_checksum","","https://www.amken3d.com")
MakeBoard("amken_revelop", "rp2040","Amken","Revelop","0x2770",["0x7304"],250,"AMKEN_REVELOP",32,0,"boot2_W25Q32JVxQ_4_padded_checksum","","https://www.amken3d.com")
MakeBoard("amken_revelop_plus", "rp2040","Amken","Revelop Plus","0x2770",["0x7305"],250,"AMKEN_REVELOP_PLUS",32,0,"boot2_W25Q32JVxQ_4_padded_checksum","","https://www.amken3d.com")
@@ -553,7 +567,7 @@ MakeBoard("DudesCab", "rp2040", "L'atelier d'Arnoz", "DudesCab", "0x2e8a", "0x10
MakeBoard("electroniccats_huntercat_nfc", "rp2040", "ElectronicCats", "HunterCat NFC RP2040", "0x2E8A", "0x1037", 500, "ELECTRONICCATS_HUNTERCAT_NFC", 2, 0, "boot2_w25q080_2_padded_checksum")
# EVN
MakeBoard("evn_alpha", "rp2040", "EVN", "Alpha", "0x2e8a", "0xf00a", 500, "EVN_ALPHA", 16, 0, "boot2_generic_03h_2_padded_checksum", board_url="https://coresg.tech/evn")
MakeBoard("evn_alpha", "rp2040", "EVN", "Alpha", "0x2e8a", "0xf00a", 500, "EVN_ALPHA", 16, 0, "boot2_generic_03h_4_padded_checksum", board_url="https://coresg.tech/evn")
# ExtremeElectronics
MakeBoard("extelec_rc2040", "rp2040", "ExtremeElectronics", "RC2040", "0x2e8a", "0xee20", 250, "EXTREMEELEXTRONICS_RC2040", 2, 0, "boot2_w25q080_2_padded_checksum")
@@ -584,6 +598,9 @@ MakeBoard("melopero_shake_rp2040", "rp2040", "Melopero", "Shake RP2040", "0x2e8a
# Mete Hoca
MakeBoard("akana_r1", "rp2040", "METE HOCA", "Akana R1", "0x2e8a", "0x3001", 500, "METEHOCA_AKANA_R1", 16, 0, "boot2_generic_03h_4_padded_checksum", board_url="https://www.metehoca.com/")
# MyMakers
MakeBoard("MyRP_bot", "rp2040", "MyMakers", "RP2040", "0x2e8a", "0x000a", 250, "MyRP_2040", 2, 0, "boot2_generic_03h_4_padded_checksum")
# Neko Systems
MakeBoard("nekosystems_bl2040_mini", "rp2040", "Neko Systems", "BL2040 Mini", "0x2e8a", "0x000a", 500, "NEKOSYSTEMS_BL2040_MINI", 4, 0, "boot2_generic_03h_2_padded_checksum")
@@ -603,6 +620,7 @@ MakeBoard("pimoroni_pga2350", "rp2350", "Pimoroni", "PGA2350", "0x2e8a", "0x1018
MakeBoard("pimoroni_pico_plus_2", "rp2350", "Pimoroni", "PicoPlus2", "0x2e8a", "0x100a", 500, "PIMORONI_PICO_PLUS_2", 16, 8, "none")
MakeBoard("pimoroni_pico_plus_2w", "rp2350", "Pimoroni", "PicoPlus2W", "0x2e8a", "0x100a", 500, "PIMORONI_PICO_PLUS_2W", 16, 8, "none", ["PICO_CYW43_SUPPORTED=1", "CYW43_PIN_WL_DYNAMIC=1"])
MakeBoard("pimoroni_plasma2040", "rp2040", "Pimoroni", "Plasma2040", "0x2e8a", "0x100a", 500, "PIMORONI_PLASMA2040", 2, 0, "boot2_w25q080_2_padded_checksum")
MakeBoard("pimoroni_plasma2350", "rp2350", "Pimoroni", "Plasma2350", "0x2e8a", "0x10a5", 500, "PIMORONI_PLASMA2040", 2, 0, "none")
MakeBoard("pimoroni_tiny2040", "rp2040", "Pimoroni", "Tiny2040", "0x2e8a", "0x100a", 500, "PIMORONI_TINY2040", 2, 0, "boot2_w25q64jv_4_padded_checksum")
MakeBoard("pimoroni_tiny2350", "rp2350", "Pimoroni", "Tiny2350", "0x2e8a", "0x100b", 500, "PIMORONI_TINY2350", 4, 0, "none")
@@ -634,14 +652,14 @@ MakeBoard("sparkfun_promicrorp2350", "rp2350", "SparkFun", "ProMicro RP2350", "0
MakeBoard("sparkfun_thingplusrp2040", "rp2040", "SparkFun", "Thing Plus RP2040", "0x1b4f", "0x0026", 250, "SPARKFUN_THINGPLUS_RP2040", 16, 0, "boot2_w25q080_2_padded_checksum")
MakeBoard("sparkfun_thingplusrp2350", "rp2350", "SparkFun", "Thing Plus RP2350", "0x1b4f", "0x0038", 250, "SPARKFUN_THINGPLUS_RP2350", 16, 8, "none", ["PICO_CYW43_SUPPORTED=1", "CYW43_PIN_WL_DYNAMIC=1"])
# Upesy
MakeBoard("upesy_rp2040_devkit", "rp2040", "uPesy", "RP2040 DevKit", "0x2e8a", "0x1007", 250, "UPESY_RP2040_DEVKIT", 2, 0, "boot2_w25q080_2_padded_checksum")
# Seeed
MakeBoard("seeed_indicator_rp2040", "rp2040", "Seeed", "INDICATOR RP2040", "0x2886", "0x0050", 250, "SEEED_INDICATOR_RP2040", 2, 0, "boot2_w25q080_2_padded_checksum")
MakeBoard("seeed_xiao_rp2040", "rp2040", "Seeed", "XIAO RP2040", "0x2e8a", "0x000a", 250, "SEEED_XIAO_RP2040", 2, 0, "boot2_w25q080_2_padded_checksum")
MakeBoard("seeed_xiao_rp2350", "rp2350", "Seeed", "XIAO RP2350", "0x2886", "0x0058", 250, "SEEED_XIAO_RP2350", 16, 8, "none", None, "https://www.seeedstudio.com/Seeed-XIAO-RP2350-p-5944.html")
# Upesy
MakeBoard("upesy_rp2040_devkit", "rp2040", "uPesy", "RP2040 DevKit", "0x2e8a", "0x1007", 250, "UPESY_RP2040_DEVKIT", 2, 0, "boot2_w25q080_2_padded_checksum")
# VCC-GND YD-2040 - Use generic SPI/4 because boards seem to come with varied flash modules but same name
MakeBoard('vccgnd_yd_rp2040', "rp2040", "VCC-GND", "YD RP2040", "0x2e8a", "0x800a", 500, "YD_RP2040", 16, 0, "boot2_generic_03h_4_padded_checksum")
@@ -670,6 +688,7 @@ MakeBoard("wiznet_55rp20_evb_pico", "rp2040", "WIZnet", "W55RP20-EVB-Pico", "0x2
MakeBoard("generic", "rp2040", "Generic", "RP2040", "0x2e8a", "0xf00a", 250, "GENERIC_RP2040", 16, 0, "boot2_generic_03h_4_padded_checksum")
MakeBoard("generic_rp2350", "rp2350", "Generic", "RP2350", "0x2e8a", "0xf00f", 250, "GENERIC_RP2350", 16, 8, "none")
sys.stdout.close()
with open(os.path.abspath(os.path.dirname(__file__)) + '/../package/package_pico_index.template.json', 'w', newline='\n') as f:
f.write(json.dumps(pkgjson, indent=3))
+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);
}
+39
View File
@@ -0,0 +1,39 @@
#pragma once
// Pin definitions taken from:
// https://datasheets.raspberrypi.org/pico/pico-datasheet.pdf
// LEDs
#define PIN_LED (25u)
// Serial
#define PIN_SERIAL1_TX (30u)
#define PIN_SERIAL1_RX (1u)
#define PIN_SERIAL2_TX (30u)
#define PIN_SERIAL2_RX (30u)
// SPI
#define PIN_SPI0_MISO (12u)
#define PIN_SPI0_MOSI (3u)
#define PIN_SPI0_SCK (2u)
#define PIN_SPI0_SS (13u)
#define PIN_SPI1_MISO (12u)
#define PIN_SPI1_MOSI (30u)
#define PIN_SPI1_SCK (31u)
#define PIN_SPI1_SS (5u)
// Wire
#define PIN_WIRE0_SDA (16u)
#define PIN_WIRE0_SCL (17u)
#define PIN_WIRE1_SDA (5u)
#define PIN_WIRE1_SCL (4u)
#define SERIAL_HOWMANY (3u)
#define SPI_HOWMANY (2u)
#define WIRE_HOWMANY (2u)
#include "../generic/common.h"
@@ -120,4 +120,7 @@ static const uint8_t SCK = PIN_SPI0_SCK;
#define CRYPTO_WIRE Wire
#define USB_MAX_POWER (500)
#ifdef __cplusplus
#include "nina_pins.h"
#endif
+2
View File
@@ -1,6 +1,8 @@
#pragma once
#ifdef __cplusplus
#include <Ilabs2040WiFiClass.h>
#endif
#define PINS_COUNT (26u)
#define NUM_DIGITAL_PINS (26u)
+2 -2
View File
@@ -64,7 +64,7 @@ static const uint8_t A3 = (31u);
//#define SPI_SCK (PIN_SPI1_SCK)
// Wire
#define __WIRE0_DEVICE (i2c1)
#define __WIRE0_DEVICE (i2c0)
#define PIN_WIRE0_SDA (20u)
#define PIN_WIRE0_SCL (21u)
#define SDA PIN_WIRE0_SDA
@@ -73,7 +73,7 @@ static const uint8_t A3 = (31u);
#define I2C_SCL (SCL)
// Wire1 not pinned out
#define __WIRE1_DEVICE (i2c0)
#define __WIRE1_DEVICE (i2c1)
#define PIN_WIRE1_SDA (31u)
#define PIN_WIRE1_SCL (31u)
@@ -0,0 +1,84 @@
#pragma once
// Pin definitions taken from:
// https://github.com/rp-rs/rp-hal-boards/blob/main/boards/pimoroni-plasma-2040/src/lib.rs
// LEDs
#define PIN_LED (16u)
#define PIN_LED_R (16u)
#define PIN_LED_G (17u)
#define PIN_LED_B (18u)
#define LED_BUILTIN PIN_LED
// Digital pins
#if 0
static const uint8_t D0 = (26u);
static const uint8_t D1 = (27u);
static const uint8_t D2 = (28u);
static const uint8_t D3 = (29u);
static const uint8_t D4 = (6u);
static const uint8_t D5 = (7u);
static const uint8_t D6 = (0u);
static const uint8_t D7 = (1u);
static const uint8_t D8 = (2u);
static const uint8_t D9 = (4u);
static const uint8_t D10 = (3u);
#endif
// Analog pins
static const uint8_t A0 = (26u);
static const uint8_t A1 = (27u);
static const uint8_t A2 = (28u);
static const uint8_t A3 = (31u);
#define ADC_RESOLUTION 12
// NeoPixel
#define PIN_NEOPIXEL (15u)
//#define NEOPIXEL_POWER (11u)
// Serial1
#define PIN_SERIAL1_TX (31u)
#define PIN_SERIAL1_RX (31u)
// Serial2 not pinned out
#define PIN_SERIAL2_TX (31u)
#define PIN_SERIAL2_RX (31u)
// SPI
#define PIN_SPI0_MISO (31u)
#define PIN_SPI0_MOSI (31u)
#define PIN_SPI0_SCK (31u)
#define PIN_SPI0_SS (31u) // not pinned out
//static const uint8_t SS = PIN_SPI0_SS; // SPI Slave SS not used. Set here only for reference.
//static const uint8_t MOSI = PIN_SPI0_MOSI;
//static const uint8_t MISO = PIN_SPI0_MISO;
//static const uint8_t SCK = PIN_SPI0_SCK;
//
// #define SS PIN_SPI0_SS
// Not pinned out
#define PIN_SPI1_MISO (31u)
#define PIN_SPI1_MOSI (31u)
#define PIN_SPI1_SCK (31u)
#define PIN_SPI1_SS (31u)
//#define SPI_MISO (PIN_SPI1_MISO)
//#define SPI_MOSI (PIN_SPI1_MOSI)
//#define SPI_SCK (PIN_SPI1_SCK)
// Wire
#define __WIRE0_DEVICE (i2c0)
#define PIN_WIRE0_SDA (20u)
#define PIN_WIRE0_SCL (21u)
#define SDA PIN_WIRE0_SDA
#define SCL PIN_WIRE0_SCL
#define I2C_SDA (SDA)
#define I2C_SCL (SCL)
// Wire1 not pinned out
#define __WIRE1_DEVICE (i2c1)
#define PIN_WIRE1_SDA (31u)
#define PIN_WIRE1_SCL (31u)
#define SERIAL_HOWMANY (0u)
#define SPI_HOWMANY (0u)
#define WIRE_HOWMANY (1u)
@@ -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)