Compare commits
@@ -0,0 +1,42 @@
|
||||
# Run whenever it is manually triggered, a pull request or a push is done that modifes the libpico configuration
|
||||
|
||||
name: libpico Builder
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- tools/libpico/**
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- tools/libpico/**
|
||||
jobs:
|
||||
build-libpico:
|
||||
name: Build libpico precompiled libraries
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: false
|
||||
- name: Get submodules for pico-sdk
|
||||
run: cd pico-sdk && git submodule update --init --recursive
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install cmake make build-essential wget
|
||||
# Automatically get correct toolchain
|
||||
cd tools && python3 get.py && cd ..
|
||||
# add to PATH
|
||||
echo "$GITHUB_WORKSPACE/system/riscv32-unknown-elf/bin" >> "$GITHUB_PATH"
|
||||
echo "$GITHUB_WORKSPACE/system/arm-none-eabi/bin" >> "$GITHUB_PATH"
|
||||
- name: Build libpico
|
||||
run: |
|
||||
cd tools/libpico
|
||||
./make-libpico.sh
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: libpico
|
||||
path: |
|
||||
tools/libpico/build-rp2040/*.a
|
||||
tools/libpico/build-rp2350/*.a
|
||||
tools/libpico/build-rp2350-riscv/*.a
|
||||
@@ -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
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
@@ -16,9 +16,11 @@ Read the [Contributing Guide](https://github.com/earlephilhower/arduino-pico/blo
|
||||
* Raspberry Pi Pico
|
||||
* Raspberry Pi Pico W
|
||||
* Raspberry Pi Pico 2
|
||||
* Raspberry Pi Pico 2W
|
||||
* 0xCB Helios
|
||||
* Adafruit Feather RP2040
|
||||
* Adafruit Feather RP2040 SCORPIO
|
||||
* Adafruit Floppsy RP2040
|
||||
* Adafruit ItsyBitsy RP2040
|
||||
* Adafruit KB2040
|
||||
* Adafruit Macropad RP2040
|
||||
@@ -66,13 +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
|
||||
@@ -91,6 +96,7 @@ Read the [Contributing Guide](https://github.com/earlephilhower/arduino-pico/blo
|
||||
* SparkFun ProMicro RP2040
|
||||
* SparkFun ProMicro RP2350
|
||||
* SparkFun Thing Plus RP2040
|
||||
* SparkFun Thing Plus RP2350
|
||||
* uPesy RP2040 DevKit
|
||||
* VCC-GND YD-RP2040
|
||||
* Viyalab Mizu RP2040
|
||||
@@ -102,7 +108,10 @@ Read the [Contributing Guide](https://github.com/earlephilhower/arduino-pico/blo
|
||||
* Waveshare RP2040 Matrix
|
||||
* Waveshare RP2040 PiZero
|
||||
* WIZnet W5100S-EVB-Pico
|
||||
* WIZnet W5100S-EVB-Pico2
|
||||
* WIZnet W5500-EVB-Pico
|
||||
* WIZnet W5500-EVB-Pico2
|
||||
* WIZnet W55RP20-EVB-Pico
|
||||
* WIZnet WizFi360-EVB-Pico
|
||||
* Generic RP2040 (configurable flash, I/O pins)
|
||||
* Generic RP2350 (configurable flash, I/O pins)
|
||||
@@ -130,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
|
||||
|
||||
+3650
-227
File diff suppressed because it is too large
Load Diff
@@ -126,6 +126,7 @@ extern const String emptyString;
|
||||
#endif
|
||||
|
||||
#include "SerialUART.h"
|
||||
#include "SerialSemi.h"
|
||||
#include "RP2040Support.h"
|
||||
#include "SerialPIO.h"
|
||||
#include "Bootsel.h"
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -19,6 +19,10 @@
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <pico/runtime.h>
|
||||
|
||||
#ifdef PICO_RP2040
|
||||
|
||||
#include <hardware/structs/psm.h>
|
||||
|
||||
extern "C" void boot_double_tap_check();
|
||||
@@ -31,3 +35,19 @@ void RP2040::enableDoubleResetBootloader() {
|
||||
boot_double_tap_check();
|
||||
}
|
||||
}
|
||||
|
||||
#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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -351,7 +360,9 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef PICO_RP2040
|
||||
static void enableDoubleResetBootloader();
|
||||
#endif
|
||||
|
||||
void wdt_begin(uint32_t delay_ms) {
|
||||
watchdog_enable(delay_ms, 1);
|
||||
@@ -463,7 +474,7 @@ public:
|
||||
}
|
||||
|
||||
bool isPicoW() {
|
||||
#if !defined(ARDUINO_RASPBERRY_PI_PICO_W)
|
||||
#if !defined(PICO_CYW43_SUPPORTED)
|
||||
return false;
|
||||
#else
|
||||
extern bool __isPicoW;
|
||||
@@ -471,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() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#define ARDUINO_PICO_MAJOR 4
|
||||
#define ARDUINO_PICO_MINOR 2
|
||||
#define ARDUINO_PICO_MINOR 4
|
||||
#define ARDUINO_PICO_REVISION 0
|
||||
#define ARDUINO_PICO_VERSION_STR "4.2.0"
|
||||
#define ARDUINO_PICO_VERSION_STR "4.4.0"
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,6 @@
|
||||
#include "Semihosting.h"
|
||||
#include "SerialSemi.h"
|
||||
#include "SemiFS.h"
|
||||
|
||||
SerialSemiClass SerialSemi;
|
||||
FS SemiFS = FS(FSImplPtr(new semifs::SemiFSImpl()));
|
||||
@@ -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
|
||||
@@ -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, ¶m);
|
||||
return 1;
|
||||
}
|
||||
|
||||
using Print::write;
|
||||
|
||||
operator bool() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool _peeked = false;
|
||||
uint8_t _peekedChar;
|
||||
};
|
||||
|
||||
extern SerialSemiClass SerialSemi;
|
||||
+23
-19
@@ -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
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
|
||||
#if defined(PICO_CYW43_SUPPORTED)
|
||||
|
||||
#include <lwip/netif.h>
|
||||
extern "C" {
|
||||
@@ -25,6 +25,12 @@ extern "C" {
|
||||
#include <cyw43_stats.h>
|
||||
}
|
||||
#include <pico/cyw43_arch.h>
|
||||
#include <pico/cyw43_driver.h>
|
||||
#include <pico/lwip_nosys.h>
|
||||
#include <hardware/resets.h>
|
||||
#include <hardware/gpio.h>
|
||||
#include <hardware/adc.h>
|
||||
#include <hardware/clocks.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
// From cyw43_ctrl.c
|
||||
@@ -101,4 +107,100 @@ extern "C" void __wrap_cyw43_cb_tcpip_deinit(cyw43_t *self, int itf) {
|
||||
(void) itf;
|
||||
}
|
||||
|
||||
#ifndef WIFICC
|
||||
#define WIFICC CYW43_COUNTRY_WORLDWIDE
|
||||
#endif
|
||||
|
||||
// Taken from https://datasheets.raspberrypi.com/picow/connecting-to-the-internet-with-pico-w.pdf
|
||||
// also discussion in https://github.com/earlephilhower/arduino-pico/issues/849
|
||||
static bool CheckPicoW() {
|
||||
#ifdef PICO_RP2040
|
||||
adc_init();
|
||||
auto dir = gpio_get_dir(29);
|
||||
auto fnc = gpio_get_function(29);
|
||||
adc_gpio_init(29);
|
||||
adc_select_input(3);
|
||||
auto adc29 = adc_read();
|
||||
gpio_set_function(29, fnc);
|
||||
gpio_set_dir(29, dir);
|
||||
|
||||
dir = gpio_get_dir(25);
|
||||
fnc = gpio_get_function(25);
|
||||
gpio_init(25);
|
||||
gpio_set_dir(25, GPIO_IN);
|
||||
auto gp25 = gpio_get(25);
|
||||
gpio_set_function(25, fnc);
|
||||
gpio_set_dir(25, dir);
|
||||
|
||||
if (gp25) {
|
||||
return true; // Can't tell, so assume yes
|
||||
} else if (adc29 < 200) {
|
||||
return true; // PicoW
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool __isPicoW = true;
|
||||
|
||||
extern "C" void init_cyw43_wifi() {
|
||||
__isPicoW = CheckPicoW();
|
||||
if (__isPicoW) {
|
||||
// Fix for overclocked CPU: SPI communication breaks down with default "div by 2" speed
|
||||
// So, divide clock by 4 for anything including and above 250MHz CPU frequency.
|
||||
if (clock_get_hz(clk_sys) >= 250000000) {
|
||||
cyw43_set_pio_clock_divisor(4, 0); // div by 4.0
|
||||
}
|
||||
cyw43_arch_init_with_country(WIFICC);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void __lockBluetooth() {
|
||||
async_context_acquire_lock_blocking(cyw43_arch_async_context());
|
||||
}
|
||||
|
||||
extern "C" void __unlockBluetooth() {
|
||||
async_context_release_lock(cyw43_arch_async_context());
|
||||
}
|
||||
|
||||
extern "C" void __pinMode(pin_size_t pin, PinMode mode);
|
||||
extern "C" void __digitalWrite(pin_size_t pin, PinStatus val);
|
||||
extern "C" PinStatus __digitalRead(pin_size_t pin);
|
||||
|
||||
extern "C" void cyw43_pinMode(pin_size_t pin, PinMode mode) {
|
||||
if (!__isPicoW && (pin == PIN_LED)) {
|
||||
pin = 25; // Silently swap in the Pico's LED
|
||||
}
|
||||
if (pin < 64) {
|
||||
__pinMode(pin, mode);
|
||||
} else {
|
||||
// TBD - There is no GPIO direction control in the driver
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void cyw43_digitalWrite(pin_size_t pin, PinStatus val) {
|
||||
if (!__isPicoW && (pin == PIN_LED)) {
|
||||
pin = 25; // Silently swap in the Pico's LED
|
||||
}
|
||||
if (pin < 64) {
|
||||
__digitalWrite(pin, val);
|
||||
} else {
|
||||
cyw43_arch_gpio_put(pin - 64, val == HIGH ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" PinStatus cyw43_digitalRead(pin_size_t pin) {
|
||||
if (!__isPicoW && (pin == PIN_LED)) {
|
||||
pin = 25; // Silently swap in the Pico's LED
|
||||
}
|
||||
if (pin < 64) {
|
||||
return __digitalRead(pin);
|
||||
} else {
|
||||
return cyw43_arch_gpio_get(pin - 64) ? HIGH : LOW;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
CYW43 TCP/Ethernet wrappers
|
||||
Copyright (c) 2023 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
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 <Arduino.h>
|
||||
#include <pico/cyw43_driver.h>
|
||||
|
||||
extern bool __isPicoW;
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void init_cyw43_wifi();
|
||||
void __lockBluetooth();
|
||||
void __unlockBluetooth();
|
||||
void cyw43_pinMode(pin_size_t pin, PinMode mode);
|
||||
void cyw43_digitalWrite(pin_size_t pin, PinStatus val);
|
||||
PinStatus cyw43_digitalRead(pin_size_t pin);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
Flash wrappers to protect PSRAM access on the RP2350
|
||||
|
||||
Copyright (c) 2024 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
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
|
||||
*/
|
||||
#include <Arduino.h>
|
||||
#include <hardware/flash.h>
|
||||
|
||||
#ifdef PICO_RP2350
|
||||
#include <hardware/structs/qmi.h>
|
||||
#endif
|
||||
|
||||
#if defined(PICO_RP2350) && defined(RP2350_PSRAM_CS)
|
||||
static void __no_inline_not_in_flash_func(flushcache)() {
|
||||
// From https://forums.raspberrypi.com/viewtopic.php?t=378249#p2263677
|
||||
// Perform clean-by-set/way on all lines
|
||||
for (uint32_t i = 0; i < 2048; ++i) {
|
||||
// Use the upper 16k of the maintenance space (0x1bffc000 through 0x1bffffff):
|
||||
*(volatile uint8_t*)(XIP_SRAM_BASE + (XIP_MAINTENANCE_BASE - XIP_BASE) + i * 8u + 0x1u) = 0;
|
||||
}
|
||||
}
|
||||
#elif defined(PICO_RP2350)
|
||||
static void __no_inline_not_in_flash_func(flushcache)() {
|
||||
// Null
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
extern "C" {
|
||||
extern void __real_flash_range_erase(uint32_t flash_offs, size_t count);
|
||||
void __wrap_flash_range_erase(uint32_t flash_offs, size_t count) {
|
||||
#ifdef PICO_RP2350
|
||||
auto s = qmi_hw->m[1];
|
||||
flushcache();
|
||||
#endif
|
||||
__real_flash_range_erase(flash_offs, count);
|
||||
#ifdef PICO_RP2350
|
||||
qmi_hw->m[1] = s;
|
||||
__compiler_memory_barrier();
|
||||
#endif
|
||||
}
|
||||
|
||||
extern void __real_flash_range_program(uint32_t flash_offs, const uint8_t *data, size_t count);
|
||||
void __wrap_flash_range_program(uint32_t flash_offs, const uint8_t *data, size_t count) {
|
||||
#ifdef PICO_RP2350
|
||||
auto s = qmi_hw->m[1];
|
||||
flushcache();
|
||||
#endif
|
||||
__real_flash_range_program(flash_offs, data, count);
|
||||
#ifdef PICO_RP2350
|
||||
qmi_hw->m[1] = s;
|
||||
__compiler_memory_barrier();
|
||||
#endif
|
||||
}
|
||||
|
||||
extern void __real_flash_get_unique_id(uint8_t *id_out);
|
||||
void __wrap_flash_get_unique_id(uint8_t *id_out) {
|
||||
#ifdef PICO_RP2350
|
||||
auto s = qmi_hw->m[1];
|
||||
flushcache();
|
||||
#endif
|
||||
__real_flash_get_unique_id(id_out);
|
||||
#ifdef PICO_RP2350
|
||||
qmi_hw->m[1] = s;
|
||||
__compiler_memory_barrier();
|
||||
#endif
|
||||
}
|
||||
|
||||
extern void __real_flash_do_cmd(const uint8_t *txbuf, uint8_t *rxbuf, size_t count);
|
||||
void __wrap_flash_do_cmd(const uint8_t *txbuf, uint8_t *rxbuf, size_t count) {
|
||||
#ifdef PICO_RP2350
|
||||
auto s = qmi_hw->m[1];
|
||||
flushcache();
|
||||
#endif
|
||||
__real_flash_do_cmd(txbuf, rxbuf, count);
|
||||
#ifdef PICO_RP2350
|
||||
qmi_hw->m[1] = s;
|
||||
__compiler_memory_barrier();
|
||||
#endif
|
||||
}
|
||||
};
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -26,9 +26,7 @@
|
||||
#include <lwip/dns.h>
|
||||
#include <lwip/raw.h>
|
||||
#include <lwip/timeouts.h>
|
||||
#ifdef PICO_RP2040
|
||||
#include <pico/cyw43_arch.h>
|
||||
#endif
|
||||
#include <pico/mutex.h>
|
||||
#include <sys/lock.h>
|
||||
#include "_xoshiro.h"
|
||||
@@ -46,7 +44,7 @@ public:
|
||||
if (ethernet_arch_lwip_gpio_mask) {
|
||||
ethernet_arch_lwip_gpio_mask();
|
||||
}
|
||||
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
|
||||
#if defined(PICO_CYW43_SUPPORTED)
|
||||
if (rp2040.isPicoW()) {
|
||||
cyw43_arch_lwip_begin();
|
||||
return;
|
||||
@@ -60,7 +58,7 @@ public:
|
||||
}
|
||||
|
||||
~LWIPMutex() {
|
||||
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
|
||||
#if defined(PICO_CYW43_SUPPORTED)
|
||||
if (rp2040.isPicoW()) {
|
||||
cyw43_arch_lwip_end();
|
||||
} else {
|
||||
@@ -70,7 +68,7 @@ public:
|
||||
} else {
|
||||
recursive_mutex_exit(&__lwipMutex);
|
||||
}
|
||||
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
|
||||
#if defined(PICO_CYW43_SUPPORTED)
|
||||
}
|
||||
#endif
|
||||
if (ethernet_arch_lwip_gpio_unmask) {
|
||||
|
||||
@@ -76,10 +76,13 @@ extern "C" void *pcalloc(size_t count, size_t size) {
|
||||
#else
|
||||
// No PSRAM, always fail
|
||||
extern "C" void *pmalloc(size_t size) {
|
||||
(void) size;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
extern "C" void *pcalloc(size_t count, size_t size) {
|
||||
(void) count;
|
||||
(void) size;
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include <CoreMutex.h>
|
||||
#include <hardware/gpio.h>
|
||||
#include <hardware/sync.h>
|
||||
#include <map>
|
||||
#include "_freertos.h"
|
||||
|
||||
|
||||
@@ -59,58 +58,34 @@ extern "C" void noInterrupts() {
|
||||
}
|
||||
}
|
||||
|
||||
auto_init_mutex(_irqMutex);
|
||||
static uint64_t _gpioIrqEnabled = 0; // Sized to work with RP2350B, 48 GPIOs
|
||||
static uint64_t _gpioIrqUseParam;
|
||||
void *_gpioIrqCB[__GPIOCNT];
|
||||
void *_gpioIrqCBParam[__GPIOCNT];
|
||||
|
||||
// Only 1 GPIO IRQ callback for all pins, so we need to look at the pin it's for and
|
||||
// dispatch to the real callback manually
|
||||
auto_init_mutex(_irqMutex);
|
||||
class CBInfo {
|
||||
public:
|
||||
CBInfo(voidFuncPtr cb) : _cb(cb), _useParam(false), _param(nullptr) { };
|
||||
CBInfo(voidFuncPtrParam cbParam, void *param) : _cbParam(cbParam), _useParam(true), _param(param) { };
|
||||
void callback() {
|
||||
if (_useParam && _cbParam) {
|
||||
_cbParam(_param);
|
||||
} else if (_cb) {
|
||||
_cb();
|
||||
}
|
||||
}
|
||||
private:
|
||||
union {
|
||||
voidFuncPtr _cb;
|
||||
voidFuncPtrParam _cbParam;
|
||||
};
|
||||
bool _useParam;
|
||||
void *_param;
|
||||
};
|
||||
|
||||
|
||||
static std::map<pin_size_t, CBInfo> _map;
|
||||
|
||||
void _gpioInterruptDispatcher(uint gpio, uint32_t events) {
|
||||
(void) events;
|
||||
|
||||
// Only need to lock around the std::map check, not the whole IRQ callback
|
||||
CBInfo *cb;
|
||||
{
|
||||
CoreMutex m(&_irqMutex);
|
||||
if (m) {
|
||||
auto irq = _map.find(gpio);
|
||||
if (irq == _map.end()) {
|
||||
return;
|
||||
}
|
||||
cb = &irq->second;
|
||||
uint64_t mask = 1LL << gpio;
|
||||
if (_gpioIrqEnabled & mask) {
|
||||
if (_gpioIrqUseParam & mask) {
|
||||
voidFuncPtr cb = (voidFuncPtr)_gpioIrqCB[gpio];
|
||||
cb();
|
||||
} else {
|
||||
return;
|
||||
voidFuncPtrParam cb = (voidFuncPtrParam)_gpioIrqCB[gpio];
|
||||
cb(_gpioIrqCBParam[gpio]);
|
||||
}
|
||||
}
|
||||
cb->callback();
|
||||
}
|
||||
|
||||
// To be called when appropriately protected w/IRQ and mutex protects
|
||||
static void _detachInterruptInternal(pin_size_t pin) {
|
||||
auto irq = _map.find(pin);
|
||||
if (irq != _map.end()) {
|
||||
uint64_t mask = 1LL << pin;
|
||||
if (_gpioIrqEnabled & mask) {
|
||||
gpio_set_irq_enabled(pin, 0x0f /* all */, false);
|
||||
_map.erase(pin);
|
||||
_gpioIrqEnabled &= ~mask;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +94,7 @@ extern "C" void attachInterrupt(pin_size_t pin, voidFuncPtr callback, PinStatus
|
||||
if (!m) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t mask = 1LL << pin;
|
||||
uint32_t events;
|
||||
switch (mode) {
|
||||
case LOW: events = 1; break;
|
||||
@@ -131,8 +106,9 @@ extern "C" void attachInterrupt(pin_size_t pin, voidFuncPtr callback, PinStatus
|
||||
}
|
||||
noInterrupts();
|
||||
_detachInterruptInternal(pin);
|
||||
CBInfo cb(callback);
|
||||
_map.insert({pin, cb});
|
||||
_gpioIrqEnabled |= mask;
|
||||
_gpioIrqUseParam &= ~mask; // No parameter
|
||||
_gpioIrqCB[pin] = (void *)callback;
|
||||
gpio_set_irq_enabled_with_callback(pin, events, true, _gpioInterruptDispatcher);
|
||||
interrupts();
|
||||
}
|
||||
@@ -142,7 +118,7 @@ void attachInterruptParam(pin_size_t pin, voidFuncPtrParam callback, PinStatus m
|
||||
if (!m) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t mask = 1LL << pin;
|
||||
uint32_t events;
|
||||
switch (mode) {
|
||||
case LOW: events = 1; break;
|
||||
@@ -154,8 +130,10 @@ void attachInterruptParam(pin_size_t pin, voidFuncPtrParam callback, PinStatus m
|
||||
}
|
||||
noInterrupts();
|
||||
_detachInterruptInternal(pin);
|
||||
CBInfo cb(callback, param);
|
||||
_map.insert({pin, cb});
|
||||
_gpioIrqEnabled |= mask;
|
||||
_gpioIrqUseParam &= ~mask; // No parameter
|
||||
_gpioIrqCB[pin] = (void *)callback;
|
||||
_gpioIrqCBParam[pin] = param;
|
||||
gpio_set_irq_enabled_with_callback(pin, events, true, _gpioInterruptDispatcher);
|
||||
interrupts();
|
||||
}
|
||||
|
||||
+2
-2
@@ -54,9 +54,9 @@ author = u'Earle F. Philhower, III'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = u'4.2.0'
|
||||
version = u'4.4.0'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = u'4.2.0'
|
||||
release = u'4.4.0'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
+19
@@ -135,6 +135,25 @@ 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
|
||||
|
||||
@@ -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>
|
||||
|
||||
+1
-1
@@ -235,7 +235,7 @@ Requirements
|
||||
Implementation Overview
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Updates with a web browser are implemented using ``HTTPUpdateServer`` class together with ``WebServer`` and ``LEAmDNS`` classes. The following code is required to get it work:
|
||||
Updates with a web browser are implemented using ``HTTPUpdateServer`` class together with ``WebServer`` and ``LEAmDNS`` or ``SimpleMDNS`` classes. The following code is required to get it work:
|
||||
|
||||
setup()
|
||||
|
||||
|
||||
@@ -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.
|
||||
+2
-2
@@ -95,8 +95,8 @@ doing any WiFi calls.
|
||||
Bootloader
|
||||
----------
|
||||
|
||||
void rp2040.enableDoubleResetBootloader()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
void rp2040.enableDoubleResetBootloader() (Pico/RP2040 only)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Add a call anywhere in the sketch to ``rp2040.enableDoubleResetBootloader()`` and
|
||||
the core will check for a double-tap on reset, and if found will start the USB
|
||||
bootloader.
|
||||
|
||||
@@ -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");
|
||||
+1
-1
@@ -44,7 +44,7 @@ Please note that WiFi on the Pico W is a work-in-progress and there are some imp
|
||||
|
||||
* FreeRTOS is supported only on core 0 and from within ``setup`` and ``loop``, not tasks, due to the requirement for a very different LWIP implementation. PRs always appreciated!
|
||||
|
||||
* LEAmDNS (``MDNS``) is not supported in FreeRTOS due to internal IRQ-time memory allocations.
|
||||
* LEAmDNS (``MDNS``) is not supported in FreeRTOS due to internal IRQ-time memory allocations. Instead, use the SimpleMDNS library ( ``#include <SimpleMDNS.h>`` ) which has no such allocations.
|
||||
|
||||
The WiFi library borrows much work from the `ESP8266 Arduino Core <https://github.com/esp8266/Arduino>`__ , especially the ``WiFiClient`` and ``WiFiServer`` classes.
|
||||
|
||||
|
||||
+45
-39
@@ -21,50 +21,56 @@ extern unsigned long __lwip_rand(void);
|
||||
// Common settings used in most of the pico_w examples
|
||||
// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html for details)
|
||||
|
||||
#define NO_SYS 1
|
||||
#define LWIP_SOCKET 0
|
||||
#define MEM_LIBC_MALLOC 0
|
||||
#define NO_SYS 1
|
||||
#define LWIP_SOCKET 0
|
||||
#define MEM_LIBC_MALLOC 0
|
||||
|
||||
#define MEM_ALIGNMENT 4
|
||||
#define MEM_SIZE (__LWIP_MEMMULT * 16384)
|
||||
#define MEMP_NUM_TCP_SEG (32)
|
||||
#define MEMP_NUM_ARP_QUEUE (10)
|
||||
#define PBUF_POOL_SIZE (__LWIP_MEMMULT > 1 ? 32 : 24)
|
||||
#define LWIP_ARP 2
|
||||
#define LWIP_ETHERNET 1
|
||||
#define LWIP_ICMP 1
|
||||
#define LWIP_RAW 1
|
||||
#define TCP_WND (8 * TCP_MSS)
|
||||
#define TCP_MSS 1460
|
||||
#define TCP_SND_BUF (8 * TCP_MSS)
|
||||
#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS))
|
||||
#define LWIP_NETIF_STATUS_CALLBACK 1
|
||||
#define LWIP_NETIF_LINK_CALLBACK 1
|
||||
#define LWIP_NETIF_HOSTNAME 1
|
||||
#define LWIP_NETCONN 0
|
||||
#define LWIP_STATS 0
|
||||
#define LWIP_STATS_DISPLAY 0
|
||||
#define MEM_STATS 0
|
||||
#define SYS_STATS 0
|
||||
#define MEMP_STATS 0
|
||||
#define LINK_STATS 0
|
||||
#define LWIP_CHKSUM_ALGORITHM 0
|
||||
#define LWIP_DHCP 1
|
||||
#define LWIP_IPV4 1
|
||||
#define LWIP_TCP 1
|
||||
#define LWIP_UDP 1
|
||||
#define LWIP_DNS 1
|
||||
#define LWIP_TCP_KEEPALIVE 1
|
||||
#define LWIP_NETIF_TX_SINGLE_PBUF 1
|
||||
#define DHCP_DOES_ARP_CHECK 0
|
||||
#define LWIP_DHCP_DOES_ACD_CHECK 0
|
||||
#define MEM_ALIGNMENT 4
|
||||
#define MEM_SIZE (__LWIP_MEMMULT * 16384)
|
||||
#define MEMP_NUM_TCP_SEG (32)
|
||||
#define MEMP_NUM_ARP_QUEUE (10)
|
||||
#define PBUF_POOL_SIZE (__LWIP_MEMMULT > 1 ? 32 : 24)
|
||||
#define LWIP_ARP 7
|
||||
#define LWIP_ETHERNET 1
|
||||
#define LWIP_ICMP 1
|
||||
#define LWIP_RAW 1
|
||||
#define TCP_WND (8 * TCP_MSS)
|
||||
#define TCP_MSS 1460
|
||||
#define TCP_SND_BUF (8 * TCP_MSS)
|
||||
#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS))
|
||||
#define LWIP_NETIF_STATUS_CALLBACK 1
|
||||
#define LWIP_NETIF_LINK_CALLBACK 1
|
||||
#define LWIP_NETIF_HOSTNAME 1
|
||||
#define LWIP_NUM_NETIF_CLIENT_DATA 5
|
||||
#define LWIP_NETCONN 0
|
||||
#define LWIP_STATS 0
|
||||
#define LWIP_STATS_DISPLAY 0
|
||||
#define MEM_STATS 0
|
||||
#define SYS_STATS 0
|
||||
#define MEMP_STATS 0
|
||||
#define LINK_STATS 0
|
||||
#define LWIP_CHKSUM_ALGORITHM 0
|
||||
#define LWIP_DHCP 1
|
||||
#define LWIP_IPV4 1
|
||||
#define LWIP_TCP 1
|
||||
#define LWIP_UDP 1
|
||||
#define LWIP_DNS 1
|
||||
#define LWIP_DNS_SUPPORT_MDNS_QUERIES 1
|
||||
#define LWIP_TCP_KEEPALIVE 1
|
||||
#define LWIP_NETIF_TX_SINGLE_PBUF 1
|
||||
#define DHCP_DOES_ARP_CHECK 0
|
||||
#define LWIP_DHCP_DOES_ACD_CHECK 0
|
||||
#define LWIP_IGMP 1
|
||||
#define LWIP_MDNS_RESPONDER 1
|
||||
#define MDNS_MAX_SERVICES 4
|
||||
|
||||
// See #1285
|
||||
#define MEMP_NUM_UDP_PCB (__LWIP_MEMMULT * 6)
|
||||
#define MEMP_NUM_TCP_PCB (__LWIP_MEMMULT * 5)
|
||||
#define MEMP_NUM_UDP_PCB (__LWIP_MEMMULT * 7)
|
||||
#define MEMP_NUM_TCP_PCB (__LWIP_MEMMULT * 5)
|
||||
|
||||
#if LWIP_IPV6
|
||||
#define LWIP_IPV6_DHCP6 1
|
||||
#define LWIP_IPV6_DHCP6 1
|
||||
#define LWIP_IPV6_MLD 1
|
||||
#endif
|
||||
|
||||
// NTP
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
#define _PICO_VERSION_H
|
||||
|
||||
#define PICO_SDK_VERSION_MAJOR 2
|
||||
#define PICO_SDK_VERSION_MINOR 0
|
||||
#define PICO_SDK_VERSION_MINOR 1
|
||||
#define PICO_SDK_VERSION_REVISION 0
|
||||
#define PICO_SDK_VERSION_STRING "2.0.0"
|
||||
#define PICO_SDK_VERSION_STRING "2.1.0"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
#define _PICO_VERSION_H
|
||||
|
||||
#define PICO_SDK_VERSION_MAJOR 2
|
||||
#define PICO_SDK_VERSION_MINOR 0
|
||||
#define PICO_SDK_VERSION_MINOR 1
|
||||
#define PICO_SDK_VERSION_REVISION 0
|
||||
#define PICO_SDK_VERSION_STRING "2.0.0"
|
||||
#define PICO_SDK_VERSION_STRING "2.1.0"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -65,6 +65,9 @@ getUsedPSRAMHeap KEYWORD2
|
||||
getTotalPSRAMHeap KEYWORD2
|
||||
getTotalPSRAM KEYWORD2
|
||||
|
||||
getProfileMemoryUsage KEYWORD2
|
||||
writeProfiling KEYWORD2
|
||||
|
||||
getChipID KEYWORD2
|
||||
|
||||
hwrand32 KEYWORD2
|
||||
@@ -83,6 +86,9 @@ digitalReadFast KEYWORD2
|
||||
|
||||
enableDoubleResetBootloader KEYWORD2
|
||||
|
||||
SerialSemi KEYWORD2
|
||||
SemiFS KEYWORD2
|
||||
|
||||
openDir KEYWORD2
|
||||
next KEYWORD2
|
||||
getLastWrite KEYWORD2
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
-iwithprefixbefore/pico-sdk/src/common/pico_util/include
|
||||
-iwithprefixbefore/pico-sdk/src/common/pico_stdlib_headers/include
|
||||
-iwithprefixbefore/pico-sdk/src/common/pico_usb_reset_interface_headers/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/boot_bootrom_headers/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/cmsis/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/cmsis/stub/CMSIS/Core/Include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_adc/include
|
||||
@@ -47,6 +48,7 @@
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_bootrom/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_double/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_fix/rp2040_usb_device_enumeration/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_flash/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_float/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_int64_ops/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_lwip/include
|
||||
|
||||
@@ -67,8 +67,3 @@
|
||||
-Wl,--wrap=cyw43_tcpip_link_status
|
||||
-Wl,--wrap=cyw43_cb_tcpip_init
|
||||
-Wl,--wrap=cyw43_cb_tcpip_deinit
|
||||
|
||||
-Wl,--wrap=flash_range_erase
|
||||
-Wl,--wrap=flash_range_program
|
||||
-Wl,--wrap=flash_get_unique_id
|
||||
-Wl,--wrap=flash_do_cmd
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,5 +1,6 @@
|
||||
-DPICO_CYW43_ARCH_THREADSAFE_BACKGROUND=1
|
||||
-DCYW43_LWIP=1
|
||||
-DCYW43_PIO_CLOCK_DIV_DYNAMIC=1
|
||||
-DCFG_TUSB_MCU=OPT_MCU_RP2040
|
||||
-DARM_MATH_CM0_FAMILY
|
||||
-DARM_MATH_CM0_PLUS
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
source [find interface/cmsis-dap.cfg]
|
||||
adapter speed 5000
|
||||
source [find target/rp2350-riscv.cfg]
|
||||
@@ -1,3 +1,6 @@
|
||||
-DPICO_CYW43_ARCH_THREADSAFE_BACKGROUND=1
|
||||
-DCYW43_LWIP=1
|
||||
-DCYW43_PIO_CLOCK_DIV_DYNAMIC=1
|
||||
-DCFG_TUSB_MCU=OPT_MCU_RP2040
|
||||
-DCFG_TUSB_OS=OPT_OS_PICO
|
||||
-DLIB_BOOT_STAGE2_HEADERS=1
|
||||
|
||||
@@ -8,3 +8,11 @@
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_hazard3/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_sha256/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_sha256/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_btstack/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_cyw43_arch/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_cyw43_driver/include
|
||||
-iwithprefixbefore/pico-sdk/lib/cyw43-driver/src
|
||||
-iwithprefixbefore/pico-sdk/lib/btstack/src
|
||||
-iwithprefixbefore/pico-sdk/lib/btstack/3rd-party/bluedroid/decoder/include
|
||||
-iwithprefixbefore/pico-sdk/lib/btstack/3rd-party/bluedroid/encoder/include
|
||||
-iwithprefixbefore/pico-sdk/lib/btstack/platform/embedded
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,3 +1,6 @@
|
||||
-DPICO_CYW43_ARCH_THREADSAFE_BACKGROUND=1
|
||||
-DCYW43_LWIP=1
|
||||
-DCYW43_PIO_CLOCK_DIV_DYNAMIC=1
|
||||
-DCFG_TUSB_MCU=OPT_MCU_RP2040
|
||||
-DCFG_TUSB_OS=OPT_OS_PICO
|
||||
-DLIB_BOOT_STAGE2_HEADERS=1
|
||||
|
||||
@@ -6,3 +6,11 @@
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/cmsis/stub/CMSIS/Device/RP2350/Include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_sha256/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_sha256/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_btstack/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_cyw43_arch/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_cyw43_driver/include
|
||||
-iwithprefixbefore/pico-sdk/lib/cyw43-driver/src
|
||||
-iwithprefixbefore/pico-sdk/lib/btstack/src
|
||||
-iwithprefixbefore/pico-sdk/lib/btstack/3rd-party/bluedroid/decoder/include
|
||||
-iwithprefixbefore/pico-sdk/lib/btstack/3rd-party/bluedroid/encoder/include
|
||||
-iwithprefixbefore/pico-sdk/lib/btstack/platform/embedded
|
||||
|
||||
Submodule libraries/Adafruit_TinyUSB_Arduino updated: a529a74e4f...f5211f4226
@@ -1,5 +1,5 @@
|
||||
#include <WiFi.h>
|
||||
#include <LEAmDNS.h>
|
||||
#include <SimpleMDNS.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <ArduinoOTA.h>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <WiFi.h>
|
||||
#include <LEAmDNS.h>
|
||||
#include <SimpleMDNS.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <ArduinoOTA.h>
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <LEAmDNS.h>
|
||||
#include <SimpleMDNS.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <ArduinoOTA.h>
|
||||
|
||||
|
||||
@@ -28,10 +28,7 @@
|
||||
|
||||
#include <lwip/udp.h>
|
||||
#include <include/UdpContext.h>
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS)
|
||||
#include <LEAmDNS.h>
|
||||
#endif
|
||||
#include <SimpleMDNS.h>
|
||||
|
||||
#ifdef DEBUG_RP2040_CORE
|
||||
#ifdef DEBUG_RP2040_PORT
|
||||
|
||||
@@ -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]);
|
||||
@@ -265,7 +274,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,6 +29,7 @@ public:
|
||||
~AudioBufferManager();
|
||||
|
||||
void setCallback(void (*fn)());
|
||||
void setCallback(void (*fn)(void *), void *cbData);
|
||||
|
||||
bool begin(int dreq, volatile void *pioFIFOAddr);
|
||||
|
||||
@@ -86,14 +87,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -53,7 +53,7 @@ void setup() {
|
||||
TaskHandle_t blinkTask;
|
||||
Serial.begin(115200);
|
||||
xTaskCreate(blink, "BLINK", 256, nullptr, 1, &blinkTask);
|
||||
#ifdef ARDUINO_RASPBERRY_PI_PICO_W
|
||||
#if defined(PICO_CYW43_SUPPORTED)
|
||||
// The PicoW WiFi chip controls the LED, and only core 0 can make calls to it safely
|
||||
vTaskCoreAffinitySet(blinkTask, 1 << 0);
|
||||
#endif
|
||||
|
||||
@@ -47,12 +47,12 @@ void setup() {
|
||||
xSemaphore = xSemaphoreCreateMutexStatic( &xMutexBuffer );
|
||||
|
||||
ledOnTask = xTaskCreateStatic(led_ON, "led_ON", STACK_SIZE, NULL, configMAX_PRIORITIES - 1, xStack_A, &xTaskBuffer_A);
|
||||
#ifdef ARDUINO_RASPBERRY_PI_PICO_W
|
||||
#if defined(PICO_CYW43_SUPPORTED)
|
||||
// The PicoW WiFi chip controls the LED, and only core 0 can make calls to it safely
|
||||
vTaskCoreAffinitySet(ledOnTask, 1 << 0);
|
||||
#endif
|
||||
ledOffTask = xTaskCreateStatic(led_OFF, "led_OFF", STACK_SIZE, NULL, configMAX_PRIORITIES - 1, xStack_B, &xTaskBuffer_B);
|
||||
#ifdef ARDUINO_RASPBERRY_PI_PICO_W
|
||||
#if defined(PICO_CYW43_SUPPORTED)
|
||||
// The PicoW WiFi chip controls the LED, and only core 0 can make calls to it safely
|
||||
vTaskCoreAffinitySet(ledOffTask, 1 << 0);
|
||||
#endif
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WebServer.h>
|
||||
#include <LEAmDNS.h>
|
||||
#include <SimpleMDNS.h>
|
||||
#include <HTTPUpdateServer.h>
|
||||
|
||||
#ifndef STASSID
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -185,6 +186,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 +205,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 +277,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 +301,7 @@ void I2S::end() {
|
||||
delete _i2s;
|
||||
_i2s = nullptr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int I2S::available() {
|
||||
|
||||
+20
-8
@@ -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;
|
||||
|
||||
@@ -162,13 +162,13 @@ right1:
|
||||
left1:
|
||||
in pins, 1 side 0b01
|
||||
jmp x--, left1 side 0b00
|
||||
in pins, 1 side 0b11 ; Last bit of left has WCLK change per I2S spec
|
||||
|
||||
in pins, 1 side 0b01 ; 2584 LRCK stays low until BCLK goes low
|
||||
; Last bit of left has WCLK change per I2S spec
|
||||
mov x, y side 0b10
|
||||
right1:
|
||||
in pins, 1 side 0b11
|
||||
jmp x--, right1 side 0b10
|
||||
in pins, 1 side 0b01 ; Last bit of right also has WCLK change
|
||||
in pins, 1 side 0b11 ; 2584 LRCK stays high until BCLK goes low
|
||||
; Loop back to beginning...
|
||||
|
||||
|
||||
@@ -184,13 +184,13 @@ right1:
|
||||
left1:
|
||||
in pins, 1 side 0b10
|
||||
jmp x--, left1 side 0b00
|
||||
in pins, 1 side 0b11 ; Last bit of left has WCLK change per I2S spec
|
||||
in pins, 1 side 0b10 ;2584 LRCK stays low until BCLK goes low
|
||||
|
||||
mov x, y side 0b01
|
||||
right1:
|
||||
in pins, 1 side 0b11
|
||||
jmp x--, right1 side 0b01
|
||||
in pins, 1 side 0b10 ; Last bit of right also has WCLK change
|
||||
in pins, 1 side 0b11 ; 2584 LRCK stays high until BCLK goes low
|
||||
; Loop back to beginning...
|
||||
|
||||
|
||||
|
||||
@@ -286,11 +286,11 @@ static const uint16_t pio_i2s_in_program_instructions[] = {
|
||||
0xa022, // 0: mov x, y side 0
|
||||
0x4801, // 1: in pins, 1 side 1
|
||||
0x0041, // 2: jmp x--, 1 side 0
|
||||
0x5801, // 3: in pins, 1 side 3
|
||||
0x4801, // 3: in pins, 1 side 1
|
||||
0xb022, // 4: mov x, y side 2
|
||||
0x5801, // 5: in pins, 1 side 3
|
||||
0x1045, // 6: jmp x--, 5 side 2
|
||||
0x4801, // 7: in pins, 1 side 1
|
||||
0x5801, // 7: in pins, 1 side 3
|
||||
// .wrap
|
||||
};
|
||||
|
||||
@@ -326,11 +326,11 @@ static const uint16_t pio_i2s_in_swap_program_instructions[] = {
|
||||
0xa022, // 0: mov x, y side 0
|
||||
0x5001, // 1: in pins, 1 side 2
|
||||
0x0041, // 2: jmp x--, 1 side 0
|
||||
0x5801, // 3: in pins, 1 side 3
|
||||
0x5001, // 3: in pins, 1 side 2
|
||||
0xa822, // 4: mov x, y side 1
|
||||
0x5801, // 5: in pins, 1 side 3
|
||||
0x0845, // 6: jmp x--, 5 side 1
|
||||
0x5001, // 7: in pins, 1 side 2
|
||||
0x5801, // 7: in pins, 1 side 3
|
||||
// .wrap
|
||||
};
|
||||
|
||||
|
||||
@@ -1348,3 +1348,8 @@ protected:
|
||||
} // namespace esp8266
|
||||
|
||||
#include <ESP8266mDNS.h>
|
||||
|
||||
#define __LEAMDNS_H 1
|
||||
#ifdef __SIMPLEMDNS_H
|
||||
#error SimpleMDNS and LeaMDNS both included. Only one allowed at a time.
|
||||
#endif
|
||||
|
||||
@@ -40,7 +40,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 +96,17 @@ 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;
|
||||
}
|
||||
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 +117,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 +145,15 @@ 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 +168,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 +190,7 @@ void PWMAudio::end() {
|
||||
dma_timer_unclaim(_pacer);
|
||||
_pacer = -1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int PWMAudio::available() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# SimpleMDNS Library for Arduino-Pico
|
||||
|
||||
This library implements a very basic MDNS responder (xxx.local) for the
|
||||
Pico to enable things like local name resolution and Arduino IDE OTA
|
||||
connections. It uses the LWIP-provided simplified MDNS application.
|
||||
|
||||
Unlike LEAmDNS, this library only supports very simple configurations.
|
||||
They should be sufficient for OTA and name resolution and simple web
|
||||
servers, but for more complicated needs please use LEAmDNS.
|
||||
|
||||
The benefit of this simplicity is that it is low code and has no runtime
|
||||
memory allocations. This means it can be run under FreeRTOS (which LEAmDNS
|
||||
does not presently support).
|
||||
|
||||
This should be a drop-in replacement, just replace `#include <LEAmDNS.h>`
|
||||
with `#include <SimpleMDNS.h>`.
|
||||
|
||||
Be sure to `MDNS.begin()` after enabling WiFi/Ethernet.begin().
|
||||
|
||||
-Earle F. Philhower, III
|
||||
<earlephilhower@yahoo.com>
|
||||
@@ -0,0 +1,23 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
SimpleMDNS KEYWORD1
|
||||
MDNS KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
begin KEYWORD2
|
||||
addService KEYWORD2
|
||||
enableArduino KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
name=SimpleMDNS
|
||||
version=1.0
|
||||
author=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
sentence=Creates a simple mDNS responder using LWIP features
|
||||
paragraph=Creates a mDNS responder to ensure host domain uniqueness in local networks and to allow for mDNS service discovery and announcement.
|
||||
category=Communication
|
||||
url=https://github.com/earlephilhower/arduino-pico
|
||||
architectures=rp2040
|
||||
dot_a_linkage=true
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
SimpleMDNS for Rasperry Pi Pico
|
||||
Implements a basic MDNS responder (xxx.local)
|
||||
|
||||
Copyright (c) 2024 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "SimpleMDNS.h"
|
||||
#include <LwipEthernet.h>
|
||||
#include <lwip/apps/mdns.h>
|
||||
|
||||
bool SimpleMDNS::begin(const char *hostname, unsigned int ttl) {
|
||||
if (_running) {
|
||||
return false;
|
||||
}
|
||||
mdns_resp_init();
|
||||
struct netif *n = netif_list;
|
||||
while (n) {
|
||||
mdns_resp_add_netif(n, hostname);
|
||||
n = n->next;
|
||||
}
|
||||
__setStateChangeCallback(_statusCB);
|
||||
_hostname = strdup(hostname);
|
||||
_running = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SimpleMDNS::enableArduino(unsigned int port, bool passwd) {
|
||||
if (!_running || _arduinoAdded) {
|
||||
return;
|
||||
}
|
||||
struct netif *n = netif_list;
|
||||
while (n) {
|
||||
mdns_resp_add_service(n, _hostname, "_arduino", DNSSD_PROTO_TCP, port, _arduinoGetTxt, (void *)passwd);
|
||||
n = n->next;
|
||||
}
|
||||
_arduinoAdded = true;
|
||||
}
|
||||
|
||||
hMDNSService SimpleMDNS::addService(const char *service, const char *proto, unsigned int port) {
|
||||
if (!_running) {
|
||||
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, SimpleMDNSService::callback, (void *)svc);
|
||||
n = n->next;
|
||||
}
|
||||
return (hMDNSService*) service;
|
||||
}
|
||||
|
||||
void SimpleMDNS::update() {
|
||||
/* No-op */
|
||||
}
|
||||
|
||||
void SimpleMDNS::end() {
|
||||
/* No-op */
|
||||
}
|
||||
|
||||
void SimpleMDNS::_statusCB(struct netif *netif) {
|
||||
mdns_resp_netif_settings_changed(netif);
|
||||
}
|
||||
|
||||
void SimpleMDNS::_addServiceTxt(struct mdns_service *service, const char *str) {
|
||||
mdns_resp_add_service_txtitem(service, str, strlen(str));
|
||||
}
|
||||
|
||||
void SimpleMDNS::_arduinoGetTxt(struct mdns_service *service, void *txt_userdata) {
|
||||
_addServiceTxt(service, "tcp_check=no");
|
||||
_addServiceTxt(service, "ssh_upload=no");
|
||||
_addServiceTxt(service, "board=" ARDUINO_VARIANT);
|
||||
_addServiceTxt(service, (bool)txt_userdata ? "auth_upload=yes" : "auth_upload=no");
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
SimpleMDNS for Rasperry Pi Pico
|
||||
Implements a basic MDNS responder (xxx.local)
|
||||
|
||||
Copyright (c) 2024 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
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 <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);
|
||||
|
||||
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();
|
||||
void update();
|
||||
|
||||
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);
|
||||
|
||||
bool _running = false;
|
||||
static const char *_hostname;
|
||||
std::map<std::string, SimpleMDNSService*> _svcMap;
|
||||
bool _arduinoAdded = false;
|
||||
};
|
||||
|
||||
extern SimpleMDNS MDNS;
|
||||
|
||||
#define __SIMPLEMDNS_H 1
|
||||
#ifdef __LEAMDNS_H
|
||||
#error SimpleMDNS and LeaMDNS both included. Only one allowed at a time.
|
||||
#endif
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
// This is the real WiFi network object, we just tickle it to do our magic
|
||||
#include <LwipEthernet.h>
|
||||
#ifdef ARDUINO_RASPBERRY_PI_PICO_W
|
||||
#if defined(PICO_CYW43_SUPPORTED)
|
||||
#include <pico/cyw43_arch.h>
|
||||
static CYW43lwIP _wifi(1);
|
||||
#elif defined(ESPHOSTSPI)
|
||||
@@ -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();
|
||||
@@ -204,7 +207,7 @@ uint8_t WiFiClass::beginAP(const char *ssid, const char* passphrase) {
|
||||
return WL_CONNECTED;
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_RASPBERRY_PI_PICO_W
|
||||
#if defined(PICO_CYW43_SUPPORTED)
|
||||
uint8_t WiFiClass::softAPgetStationNum() {
|
||||
if (!_apMode || !_wifiHWInitted) {
|
||||
return 0;
|
||||
@@ -324,7 +327,7 @@ void WiFiClass::end(void) {
|
||||
return: pointer to uint8_t array with length WL_MAC_ADDR_LENGTH
|
||||
*/
|
||||
uint8_t* WiFiClass::macAddress(uint8_t* mac) {
|
||||
#ifdef ARDUINO_RASPBERRY_PI_PICO_W
|
||||
#if defined(PICO_CYW43_SUPPORTED)
|
||||
if (!_wifiHWInitted) {
|
||||
_apMode = false;
|
||||
cyw43_wifi_set_up(&cyw43_state, _apMode ? 1 : 0, true, CYW43_COUNTRY_WORLDWIDE);
|
||||
@@ -423,7 +426,7 @@ uint8_t WiFiClass::encryptionType() {
|
||||
int8_t WiFiClass::scanNetworks(bool async) {
|
||||
if (!_wifiHWInitted) {
|
||||
_apMode = false;
|
||||
#ifdef ARDUINO_RASPBERRY_PI_PICO_W
|
||||
#if defined(PICO_CYW43_SUPPORTED)
|
||||
cyw43_arch_enable_sta_mode();
|
||||
#endif
|
||||
_wifiHWInitted = true;
|
||||
@@ -519,7 +522,7 @@ unsigned long WiFiClass::getTime() {
|
||||
return millis();
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_RASPBERRY_PI_PICO_W
|
||||
#if defined(PICO_CYW43_SUPPORTED)
|
||||
void WiFiClass::aggressiveLowPowerMode() {
|
||||
cyw43_wifi_pm(&cyw43_state, CYW43_AGGRESSIVE_PM);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#ifdef ARDUINO_RASPBERRY_PI_PICO_W
|
||||
#if defined(PICO_CYW43_SUPPORTED)
|
||||
#include <lwIP_CYW43.h>
|
||||
#elif defined(ESPHOSTSPI)
|
||||
#include <lwIP_ESPHost.h>
|
||||
@@ -158,7 +158,7 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_RASPBERRY_PI_PICO_W
|
||||
#if defined(PICO_CYW43_SUPPORTED)
|
||||
uint8_t softAPgetStationNum();
|
||||
#endif
|
||||
|
||||
@@ -405,7 +405,7 @@ public:
|
||||
|
||||
unsigned long getTime();
|
||||
|
||||
#ifdef ARDUINO_RASPBERRY_PI_PICO_W
|
||||
#if defined(PICO_CYW43_SUPPORTED)
|
||||
void aggressiveLowPowerMode();
|
||||
void defaultLowPowerMode();
|
||||
#endif
|
||||
|
||||
@@ -38,7 +38,7 @@ template<>
|
||||
WiFiUDP* SList<WiFiUDP>::_s_first = 0;
|
||||
|
||||
/* Constructor */
|
||||
WiFiUDP::WiFiUDP() : _ctx(0), _multicast(false) {
|
||||
WiFiUDP::WiFiUDP() : _ctx(0), _multicast(false), _dirty(false) {
|
||||
WiFiUDP::_add(this);
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ WiFiUDP::WiFiUDP(const WiFiUDP& other) {
|
||||
}
|
||||
WiFiUDP::_add(this);
|
||||
_multicast = other._multicast;
|
||||
_dirty = other._dirty;
|
||||
}
|
||||
|
||||
WiFiUDP& WiFiUDP::operator=(const WiFiUDP& rhs) {
|
||||
@@ -149,6 +150,7 @@ int WiFiUDP::beginPacket(IPAddress ip, uint16_t port) {
|
||||
_ctx->setMulticastInterface(IP_ADDR_ANY);
|
||||
_ctx->setMulticastTTL(255);
|
||||
}
|
||||
_dirty = false;
|
||||
return ret;
|
||||
|
||||
}
|
||||
@@ -164,6 +166,7 @@ int WiFiUDP::beginPacketMulticast(IPAddress multicastAddress, uint16_t port,
|
||||
}
|
||||
_ctx->setMulticastInterface(interfaceAddress);
|
||||
_ctx->setMulticastTTL(ttl);
|
||||
_dirty = false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -171,6 +174,7 @@ int WiFiUDP::endPacket() {
|
||||
if (!_ctx) {
|
||||
return 0;
|
||||
}
|
||||
_dirty = false;
|
||||
|
||||
return (_ctx->send()) ? 1 : 0;
|
||||
}
|
||||
@@ -183,7 +187,7 @@ size_t WiFiUDP::write(const uint8_t *buffer, size_t size) {
|
||||
if (!_ctx) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
_dirty = true;
|
||||
return _ctx->append(reinterpret_cast<const char*>(buffer), size);
|
||||
}
|
||||
|
||||
@@ -224,7 +228,9 @@ int WiFiUDP::peek() {
|
||||
}
|
||||
|
||||
void WiFiUDP::flush() {
|
||||
endPacket();
|
||||
if (_dirty) {
|
||||
endPacket();
|
||||
}
|
||||
}
|
||||
|
||||
IPAddress WiFiUDP::remoteIP() {
|
||||
|
||||
@@ -115,4 +115,5 @@ public:
|
||||
|
||||
private:
|
||||
bool _multicast;
|
||||
bool _dirty;
|
||||
};
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include <lwip/timeouts.h>
|
||||
#include <lwip/dns.h>
|
||||
#include <pico/mutex.h>
|
||||
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
|
||||
#if defined(PICO_CYW43_SUPPORTED)
|
||||
#include <pico/cyw43_arch.h>
|
||||
#endif
|
||||
#include <pico/async_context_threadsafe_background.h>
|
||||
@@ -41,7 +41,7 @@ static async_context_t *_context = nullptr;
|
||||
static std::map<int, std::function<void(void)>> _handlePacketList;
|
||||
|
||||
void ethernet_arch_lwip_begin() {
|
||||
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
|
||||
#if defined(PICO_CYW43_SUPPORTED)
|
||||
if (rp2040.isPicoW()) {
|
||||
cyw43_arch_lwip_begin();
|
||||
return;
|
||||
@@ -51,7 +51,7 @@ void ethernet_arch_lwip_begin() {
|
||||
}
|
||||
|
||||
void ethernet_arch_lwip_end() {
|
||||
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
|
||||
#if defined(PICO_CYW43_SUPPORTED)
|
||||
if (rp2040.isPicoW()) {
|
||||
cyw43_arch_lwip_end();
|
||||
return;
|
||||
@@ -187,7 +187,7 @@ static void ethernet_timeout_reached(__unused async_context_t *context, __unused
|
||||
for (auto handlePacket : _handlePacketList) {
|
||||
handlePacket.second();
|
||||
}
|
||||
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
|
||||
#if defined(PICO_CYW43_SUPPORTED)
|
||||
if (!rp2040.isPicoW()) {
|
||||
sys_check_timeouts();
|
||||
}
|
||||
@@ -207,7 +207,7 @@ void __startEthernetContext() {
|
||||
if (__ethernetContextInitted) {
|
||||
return;
|
||||
}
|
||||
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
|
||||
#if defined(PICO_CYW43_SUPPORTED)
|
||||
if (rp2040.isPicoW()) {
|
||||
_context = cyw43_arch_async_context();
|
||||
} else {
|
||||
@@ -229,3 +229,8 @@ void lwipPollingPeriod(int ms) {
|
||||
_pollingPeriod = ms;
|
||||
}
|
||||
}
|
||||
|
||||
std::function<void(struct netif *)> _scb;
|
||||
void __setStateChangeCallback(std::function<void(struct netif *)> s) {
|
||||
_scb = s;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user