Compare commits
@@ -6,6 +6,10 @@ name: Arduino-Pico CI
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
TRAVIS_BUILD_DIR: ${{ github.workspace }}
|
||||
TRAVIS_TAG: ${{ github.ref }}
|
||||
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -20,7 +24,7 @@ jobs:
|
||||
- name: Run codespell
|
||||
uses: codespell-project/actions-codespell@master
|
||||
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
|
||||
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
|
||||
ignore_words_list: ser,dout
|
||||
|
||||
# Consistent style
|
||||
@@ -46,10 +50,6 @@ jobs:
|
||||
./tests/restyle.sh
|
||||
# If anything changed, GIT should return an error and fail the test
|
||||
git diff --exit-code
|
||||
# - name: Check Arduino API copy is clean
|
||||
# run: |
|
||||
# git submodule update --init ./ArduinoCore-API
|
||||
# diff -r ./cores/rp2040/api ./ArduinoCore-API/api
|
||||
|
||||
# Build all examples on linux (core and Arduino IDE)
|
||||
build-linux:
|
||||
@@ -62,19 +62,17 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v2
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Cache Linux toolchain
|
||||
id: cache-linux
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ./tools/dist
|
||||
key: ${{ runner.os }}-${{ hashFiles('package/package_pico_index.template.json', 'tests/common.sh') }}
|
||||
- name: Build Sketches
|
||||
env:
|
||||
TRAVIS_BUILD_DIR: ${{ github.workspace }}
|
||||
TRAVIS_TAG: ${{ github.ref }}
|
||||
BUILD_PARITY: custom
|
||||
mod: 6
|
||||
rem: ${{ matrix.chunk }}
|
||||
@@ -92,19 +90,17 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v2
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Cache Linux toolchain
|
||||
id: cache-linux
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ./tools/dist
|
||||
key: ${{ runner.os }}-${{ hashFiles('package/package_pico_index.template.json', 'tests/common.sh') }}
|
||||
- name: Build Sketches
|
||||
env:
|
||||
TRAVIS_BUILD_DIR: ${{ github.workspace }}
|
||||
TRAVIS_TAG: ${{ github.ref }}
|
||||
BUILD_PARITY: custom
|
||||
run: |
|
||||
cd pico-sdk
|
||||
@@ -120,19 +116,17 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v2
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Cache Windows toolchain
|
||||
id: cache-windows
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ./tools/dist
|
||||
key: ${{ runner.os }}-${{ hashFiles('package/package_pico_index.template.json', 'tests/common.sh') }}
|
||||
- name: Build Sketch
|
||||
env:
|
||||
TRAVIS_BUILD_DIR: ${{ github.workspace }}
|
||||
TRAVIS_TAG: ${{ github.ref }}
|
||||
WINDOWS: 1
|
||||
BUILD_PARITY: custom
|
||||
mod: 500
|
||||
@@ -150,24 +144,22 @@ jobs:
|
||||
# Single build under macOS to ensure the Mac toolchain is good.
|
||||
build-mac:
|
||||
name: Mac
|
||||
runs-on: macOS-latest
|
||||
runs-on: macOS-12
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v2
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Cache Mac toolchain
|
||||
id: cache-mac
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ./tools/dist
|
||||
key: ${{ runner.os }}-${{ hashFiles('package/package_pico_index.template.json', 'tests/common.sh') }}
|
||||
- name: Build Sketch
|
||||
env:
|
||||
TRAVIS_BUILD_DIR: ${{ github.workspace }}
|
||||
TRAVIS_TAG: ${{ github.ref }}
|
||||
MACOSX: 1
|
||||
BUILD_PARITY: custom
|
||||
mod: 500
|
||||
@@ -194,19 +186,21 @@ jobs:
|
||||
git submodule update --init
|
||||
cd ../..
|
||||
- name: Cache pip
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
- name: Cache PlatformIO
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install PlatformIO
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
@@ -223,3 +217,5 @@ jobs:
|
||||
run: pio ci --board=rpipicow -O "platform_packages=framework-arduinopico@symlink:///home/runner/work/arduino-pico/arduino-pico" libraries/WiFi/examples/ScanNetworks/ScanNetworks.ino
|
||||
- name: Build Signed OTA Example
|
||||
run: pio ci --board=rpipicow -O "platform_packages=framework-arduinopico@symlink:///home/runner/work/arduino-pico/arduino-pico" libraries/ArduinoOTA/examples/SignedOTA/SignedOTA.ino
|
||||
- name: Build Bluetooth Example
|
||||
run: pio ci --board=rpipicow -O "platform_packages=framework-arduinopico@symlink:///home/runner/work/arduino-pico/arduino-pico" -O "build_flags=-DPIO_FRAMEWORK_ARDUINO_ENABLE_BLUETOOTH" libraries/MouseBLE/examples/BLECircle/BLECircle.ino
|
||||
|
||||
@@ -9,28 +9,28 @@ jobs:
|
||||
name: Update master JSON file
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- name: Cache pip
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
- name: Cache PlatformIO
|
||||
uses: actions/cache@v2
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install PlatformIO
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install --upgrade platformio
|
||||
python-version: '3.x'
|
||||
# - name: Cache PlatformIO
|
||||
# uses: actions/cache@v3
|
||||
# with:
|
||||
# path: ~/.platformio
|
||||
# key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
|
||||
# - name: Install PlatformIO
|
||||
# run: |
|
||||
# python -m pip install --upgrade pip
|
||||
# pip install --upgrade platformio
|
||||
- name: Deploy updated JSON
|
||||
env:
|
||||
TRAVIS_BUILD_DIR: ${{ github.workspace }}
|
||||
@@ -43,5 +43,5 @@ jobs:
|
||||
curl -L -o package_rp2040_index.json "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/releases/download/$TAG/package_rp2040_index.json"
|
||||
./package/update_release.py --token ${CI_GITHUB_API_KEY} --repo "$GITHUB_REPOSITORY" --tag global package_rp2040_index.json
|
||||
# Upload to Platform.IO
|
||||
curl -LO $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/releases/download/$TAG/rp2040-$TAG.zip
|
||||
pio package publish rp2040-$TAG.zip --non-interactive
|
||||
# curl -LO $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/releases/download/$TAG/rp2040-$TAG.zip
|
||||
# pio package publish rp2040-$TAG.zip --non-interactive
|
||||
|
||||
@@ -15,11 +15,11 @@ jobs:
|
||||
name: Package
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-python@v2
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Build package JSON
|
||||
|
||||
+7
-7
@@ -3,7 +3,7 @@
|
||||
url = https://github.com/earlephilhower/ArduinoCore-API.git
|
||||
[submodule "pico-sdk"]
|
||||
path = pico-sdk
|
||||
url = https://github.com/earlephilhower/pico-sdk.git
|
||||
url = https://github.com/raspberrypi/pico-sdk.git
|
||||
[submodule "system/pyserial"]
|
||||
path = tools/pyserial
|
||||
url = https://github.com/pyserial/pyserial.git
|
||||
@@ -14,14 +14,14 @@
|
||||
path = libraries/ESP8266SdFat
|
||||
url = https://github.com/earlephilhower/ESP8266SdFat.git
|
||||
[submodule "libraries/Keyboard"]
|
||||
path = libraries/Keyboard
|
||||
url = https://github.com/earlephilhower/Keyboard
|
||||
path = libraries/HID_Keyboard
|
||||
url = https://github.com/earlephilhower/Keyboard.git
|
||||
[submodule "libraries/Mouse"]
|
||||
path = libraries/Mouse
|
||||
url = https://github.com/earlephilhower/Mouse
|
||||
path = libraries/HID_Mouse
|
||||
url = https://github.com/earlephilhower/Mouse.git
|
||||
[submodule "libraries/Joystick"]
|
||||
path = libraries/Joystick
|
||||
url = https://github.com/benjaminaigner/Joystick
|
||||
path = libraries/HID_Joystick
|
||||
url = https://github.com/earlephilhower/Joystick.git
|
||||
[submodule "libraries/Adafruit_TinyUSB_Arduino"]
|
||||
path = libraries/Adafruit_TinyUSB_Arduino
|
||||
url = https://github.com/adafruit/Adafruit_TinyUSB_Arduino.git
|
||||
|
||||
+1
-1
Submodule ArduinoCore-API updated: d955d75747...ece6e68f29
@@ -4,55 +4,102 @@
|
||||
|
||||
Raspberry Pi Pico Arduino core, for all RP2040 boards
|
||||
|
||||
This is a port of the RP2040 (Raspberry Pi Pico processor) to the Arduino ecosystem. It uses the bare Raspberry Pi Pico SDK and a custom GCC 10.3/Newlib 4.0 toolchain.
|
||||
This is a port of the RP2040 (Raspberry Pi Pico processor) to the Arduino ecosystem. It uses the bare Raspberry Pi Pico SDK and a custom GCC 12.3/Newlib 4.0 toolchain.
|
||||
|
||||
# Documentation
|
||||
See https://arduino-pico.readthedocs.io/en/latest/ along with the examples for more detailed usage information.
|
||||
|
||||
# Contributing
|
||||
Read the [Contributing Guide](https://github.com/earlephilhower/arduino-pico/blob/master/docs/contrib.rst) for more information on submitting pull requests and porting libraries or sketches to this core.
|
||||
|
||||
# Supported Boards
|
||||
* Raspberry Pi Pico
|
||||
* Raspberry Pi Pico W
|
||||
* 0xCB Helios
|
||||
* Adafruit Feather RP2040
|
||||
* Adafruit Feather RP2040 SCORPIO
|
||||
* Adafruit ItsyBitsy RP2040
|
||||
* Adafruit KB2040
|
||||
* Adafruit Macropad RP2040
|
||||
* Adafruit Metro RP2040
|
||||
* Adafruit QTPy RP2040
|
||||
* Adafruit STEMMA Friend RP2040
|
||||
* Adafruit Trinkey RP2040 QT
|
||||
* Arduino Nano RP2040 Connect
|
||||
* ArtronShop RP2 Nano
|
||||
* BridgeTek IDM2040-7A
|
||||
* Cytron Maker Pi RP2040
|
||||
* Cytron Maker Nano RP2040
|
||||
* DatanoiseTV PicoADK+
|
||||
* DeRuiLab FlyBoard2040 Core
|
||||
* DFRobot Beetle RP2040
|
||||
* ElectronicCats Hunter Cat NFC
|
||||
* ExtremeElectronics RC2040
|
||||
* Invector Labs Challenger RP2040 WiFi
|
||||
* Invector Labs Challenger RP2040 WiFi/BLE
|
||||
* Invector Labs Challenger RP2040 WiFi6/BLE
|
||||
* Invector Labs Challenger NB RP2040 WiFi
|
||||
* Invector Labs Challenger RP2040 LTE
|
||||
* Invector Labs Challenger RP2040 LoRa
|
||||
* Invector Labs Challenger RP2040 SubGHz
|
||||
* Invector Labs Challenger RP2040 SD/RTC
|
||||
* Invector Labs Challenger RP2040 UWB
|
||||
* Invector Labs RPICO32
|
||||
* Melopero Cookie RP2040
|
||||
* Melopero Shake RP2040
|
||||
* Neko Systems BL2040 Mini
|
||||
* nullbits Bit-C PRO
|
||||
* Pimoroni PGA2040
|
||||
* Pimoroni Plasma2040
|
||||
* Pimoroni Tiny2040
|
||||
* Seeed Indicator RP2040
|
||||
* Seeed XIAO RP2040
|
||||
* Silicognition RP2040-Shim
|
||||
* Solder Party RP2040 Stamp
|
||||
* SparkFun ProMicro RP2040
|
||||
* SparkFun Thing Plus RP2040
|
||||
* uPesy RP2040 DevKit
|
||||
* VCC-GND YD-RP2040
|
||||
* Viyalab Mizu RP2040
|
||||
* Waveshare RP2040 Zero
|
||||
* Waveshare RP2040 One
|
||||
* Waveshare RP2040 Plus
|
||||
* Waveshare RP2040 LCD 0.96
|
||||
* Waveshare RP2040 LCD 1.28
|
||||
* WIZnet W5100S-EVB-Pico
|
||||
* WIZnet W5500-EVB-Pico
|
||||
* WIZnet WizFi360-EVB-Pico
|
||||
* Redscorp RP2040-ProMini
|
||||
* Generic (configurable flash, I/O pins)
|
||||
|
||||
# Installing via Arduino Boards Manager
|
||||
**Windows Users**: Please do not use the Windows Store version of the actual Arduino application
|
||||
## Windows-specific Notes
|
||||
Please do not use the Windows Store version of the actual Arduino application
|
||||
because it has issues detecting attached Pico boards. Use the "Windows ZIP" or plain "Windows"
|
||||
executable (EXE) download direct from https://arduino.cc. and allow it to install any device
|
||||
drivers it suggests. Otherwise the Pico board may not be detected. Also, if trying out the
|
||||
2.0 beta Arduino please install the release 1.8 version beforehand to ensure needed device drivers
|
||||
are present. (See #20 for more details.)
|
||||
|
||||
## Linux-specific Notes
|
||||
Installing Arduino using flatpak (often used by "App Stores" in various Linux
|
||||
distributions) will mean it has restricted access to the host. This might cause uploads to fail
|
||||
with error messages such as the following:
|
||||
|
||||
```
|
||||
Scanning for RP2040 devices
|
||||
...
|
||||
No drive to deploy.
|
||||
```
|
||||
|
||||
If you encounter this, you will need to either install Arduino in a different manner, or override
|
||||
the flatpak sandboxing feature using the following command, then restarting Arduino.
|
||||
|
||||
```
|
||||
flatpak override --user --filesystem=host:ro cc.arduino.IDE2
|
||||
```
|
||||
|
||||
## Installation
|
||||
Open up the Arduino IDE and go to File->Preferences.
|
||||
|
||||
In the dialog that pops up, enter the following URL in the "Additional Boards Manager URLs" field:
|
||||
@@ -70,6 +117,12 @@ Type "pico" in the search box and select "Add":
|
||||

|
||||
|
||||
# Installing via GIT
|
||||
|
||||
**Windows Users:** Before installing via `git` on Windows, please read and follow the directions in
|
||||
[this link](https://arduino-pico.readthedocs.io/en/latest/platformio.html#important-steps-for-windows-users-before-installing).
|
||||
If Win32 long paths are not enabled, and `git` not configured to use them then there
|
||||
may be errors when attempting to clone the submodules.
|
||||
|
||||
To install via GIT (for latest and greatest versions):
|
||||
````
|
||||
mkdir -p ~/Arduino/hardware/pico
|
||||
@@ -146,7 +199,8 @@ The installed tools include a version of OpenOCD (in the pqt-openocd directory)
|
||||
|
||||
# Features
|
||||
* Adafruit TinyUSB Arduino (USB mouse, keyboard, flash drive, generic HID, CDC Serial, MIDI, WebUSB, others)
|
||||
* Generic Arduino USB Serial, Keyboard, and Mouse emulation
|
||||
* Bluetooth on the PicoW (Classic and BLE) with Keyboard, Mouse, Joystick, and Virtual Serial
|
||||
* Generic Arduino USB Serial, Keyboard, Joystick, and Mouse emulation
|
||||
* WiFi (Pico W)
|
||||
* HTTP client and server (WebServer)
|
||||
* SSL/TLS/HTTPS
|
||||
@@ -156,6 +210,9 @@ The installed tools include a version of OpenOCD (in the pqt-openocd directory)
|
||||
* FreeRTOS SMP support
|
||||
* Overclocking and underclocking from the menus
|
||||
* digitalWrite/Read, shiftIn/Out, tone, analogWrite(PWM)/Read, temperature
|
||||
* Analog stereo audio in using DMA and the built-in ADC
|
||||
* Analog stereo audio out using PWM hardware
|
||||
* USB drive mode for data loggers (SingleFileDrive)
|
||||
* Peripherals: SPI master, Wire(I2C) master/slave, dual UART, emulated EEPROM, I2S audio input, I2S audio output, Servo
|
||||
* printf (i.e. debug) output over USB serial
|
||||
|
||||
@@ -174,6 +231,9 @@ Here are some links to coverage and additional tutorials for using `arduino-pico
|
||||
* Adafruit Feather RP2040 running LCD + TMP117 - https://www.youtube.com/watch?v=fKDeqZiIwHg
|
||||
* Demonstration of Servos and I2C in Korean - https://cafe.naver.com/arduinoshield/1201
|
||||
* Home Assistant Pico W integration starter project using Arduino - https://github.com/daniloc/PicoW_HomeAssistant_Starter
|
||||
* Tutorials for the Raspberry Pi Pico / uPesy RP2040 DevKit board
|
||||
- English version: https://www.upesy.com/blogs/tutorials/best-tutorials-for-rpi-pi-pico-with-arduino-code
|
||||
- French version: https://www.upesy.fr/blogs/tutorials/best-tutorials-for-rpi-pi-pico-with-arduino-code
|
||||
|
||||
# Contributing
|
||||
If you want to contribute or have bugfixes, drop me a note at <earlephilhower@yahoo.com> or open an issue/PR here.
|
||||
|
||||
+9444
-13715
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
||||
// Padded and checksummed version of: generated\Winbond\W25Q128JVxQ\boot2.bin by @maxgerhardt
|
||||
|
||||
.cpu cortex-m0plus
|
||||
.thumb
|
||||
|
||||
.section .boot2, "ax"
|
||||
|
||||
.byte 0xf7, 0xb5, 0x73, 0x46, 0x21, 0x22, 0x02, 0x26, 0x01, 0x93, 0x29, 0x4b, 0xc0, 0x24, 0x5a, 0x60
|
||||
.byte 0x9a, 0x68, 0x00, 0x27, 0xb2, 0x43, 0xda, 0x60, 0x9a, 0x60, 0x1a, 0x61, 0x5a, 0x61, 0x04, 0x23
|
||||
.byte 0x01, 0x25, 0x64, 0x05, 0xa7, 0x60, 0x63, 0x61, 0x22, 0x4b, 0x30, 0x00, 0x1d, 0x60, 0xe0, 0x23
|
||||
.byte 0xdb, 0x02, 0x23, 0x60, 0x35, 0x23, 0xa5, 0x60, 0x23, 0x66, 0x23, 0x66, 0x00, 0xf0, 0x4a, 0xf8
|
||||
.byte 0xc0, 0xb2, 0xb0, 0x42, 0x12, 0xd0, 0x06, 0x23, 0x28, 0x00, 0x23, 0x66, 0x00, 0xf0, 0x42, 0xf8
|
||||
.byte 0x25, 0x66, 0x03, 0x20, 0x27, 0x66, 0x26, 0x66, 0x00, 0xf0, 0x3c, 0xf8, 0x03, 0x36, 0x26, 0x66
|
||||
.byte 0x02, 0x20, 0x26, 0x66, 0x00, 0xf0, 0x36, 0xf8, 0x28, 0x42, 0xf8, 0xd1, 0x00, 0x25, 0x12, 0x4b
|
||||
.byte 0xa5, 0x60, 0x12, 0x4f, 0x23, 0x60, 0x12, 0x4b, 0x65, 0x60, 0x01, 0x26, 0x3b, 0x60, 0xeb, 0x23
|
||||
.byte 0xa6, 0x60, 0x23, 0x66, 0x4b, 0x3b, 0x23, 0x66, 0x02, 0x20, 0x00, 0xf0, 0x23, 0xf8, 0x0d, 0x4b
|
||||
.byte 0xa5, 0x60, 0x3b, 0x60, 0xa6, 0x60, 0x01, 0x9b, 0xab, 0x42, 0x08, 0xd1, 0x0a, 0x4b, 0x0b, 0x4a
|
||||
.byte 0x13, 0x60, 0x1b, 0x68, 0x83, 0xf3, 0x08, 0x88, 0x09, 0x4b, 0x1b, 0x68, 0x18, 0x47, 0xf7, 0xbd
|
||||
.byte 0x00, 0x00, 0x02, 0x40, 0xf0, 0x00, 0x00, 0x18, 0x00, 0x03, 0x5f, 0x00, 0xf4, 0x00, 0x00, 0x18
|
||||
.byte 0x21, 0x22, 0x00, 0x00, 0x22, 0x20, 0x00, 0xa0, 0x00, 0x01, 0x00, 0x10, 0x08, 0xed, 0x00, 0xe0
|
||||
.byte 0x04, 0x01, 0x00, 0x10, 0xc0, 0x22, 0x03, 0x00, 0x04, 0x21, 0x52, 0x05, 0x90, 0x6a, 0x08, 0x42
|
||||
.byte 0xfc, 0xd0, 0x01, 0x21, 0x90, 0x6a, 0x08, 0x42, 0xfc, 0xd1, 0x01, 0x3b, 0xdb, 0xb2, 0x10, 0x6e
|
||||
.byte 0x00, 0x2b, 0xfa, 0xd1, 0x70, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xa0, 0xf0, 0x2f
|
||||
@@ -0,0 +1,23 @@
|
||||
// Padded and checksummed version of: generated\Winbond\W25Q16JVxQ\boot2.bin
|
||||
|
||||
.cpu cortex-m0plus
|
||||
.thumb
|
||||
|
||||
.section .boot2, "ax"
|
||||
|
||||
.byte 0xf7, 0xb5, 0x73, 0x46, 0x21, 0x22, 0x02, 0x26, 0x01, 0x93, 0x29, 0x4b, 0xc0, 0x24, 0x5a, 0x60
|
||||
.byte 0x9a, 0x68, 0x00, 0x27, 0xb2, 0x43, 0xda, 0x60, 0x9a, 0x60, 0x1a, 0x61, 0x5a, 0x61, 0x04, 0x23
|
||||
.byte 0x01, 0x25, 0x64, 0x05, 0xa7, 0x60, 0x63, 0x61, 0x22, 0x4b, 0x30, 0x00, 0x1d, 0x60, 0xe0, 0x23
|
||||
.byte 0xdb, 0x02, 0x23, 0x60, 0x35, 0x23, 0xa5, 0x60, 0x23, 0x66, 0x23, 0x66, 0x00, 0xf0, 0x4a, 0xf8
|
||||
.byte 0xc0, 0xb2, 0xb0, 0x42, 0x12, 0xd0, 0x06, 0x23, 0x28, 0x00, 0x23, 0x66, 0x00, 0xf0, 0x42, 0xf8
|
||||
.byte 0x25, 0x66, 0x03, 0x20, 0x27, 0x66, 0x26, 0x66, 0x00, 0xf0, 0x3c, 0xf8, 0x03, 0x36, 0x26, 0x66
|
||||
.byte 0x02, 0x20, 0x26, 0x66, 0x00, 0xf0, 0x36, 0xf8, 0x28, 0x42, 0xf8, 0xd1, 0x00, 0x25, 0x12, 0x4b
|
||||
.byte 0xa5, 0x60, 0x12, 0x4f, 0x23, 0x60, 0x12, 0x4b, 0x65, 0x60, 0x01, 0x26, 0x3b, 0x60, 0xeb, 0x23
|
||||
.byte 0xa6, 0x60, 0x23, 0x66, 0x4b, 0x3b, 0x23, 0x66, 0x02, 0x20, 0x00, 0xf0, 0x23, 0xf8, 0x0d, 0x4b
|
||||
.byte 0xa5, 0x60, 0x3b, 0x60, 0xa6, 0x60, 0x01, 0x9b, 0xab, 0x42, 0x08, 0xd1, 0x0a, 0x4b, 0x0b, 0x4a
|
||||
.byte 0x13, 0x60, 0x1b, 0x68, 0x83, 0xf3, 0x08, 0x88, 0x09, 0x4b, 0x1b, 0x68, 0x18, 0x47, 0xf7, 0xbd
|
||||
.byte 0x00, 0x00, 0x02, 0x40, 0xf0, 0x00, 0x00, 0x18, 0x00, 0x03, 0x5f, 0x00, 0xf4, 0x00, 0x00, 0x18
|
||||
.byte 0x21, 0x22, 0x00, 0x00, 0x22, 0x20, 0x00, 0xa0, 0x00, 0x01, 0x00, 0x10, 0x08, 0xed, 0x00, 0xe0
|
||||
.byte 0x04, 0x01, 0x00, 0x10, 0xc0, 0x22, 0x03, 0x00, 0x04, 0x21, 0x52, 0x05, 0x90, 0x6a, 0x08, 0x42
|
||||
.byte 0xfc, 0xd0, 0x01, 0x21, 0x90, 0x6a, 0x08, 0x42, 0xfc, 0xd1, 0x01, 0x3b, 0xdb, 0xb2, 0x10, 0x6e
|
||||
.byte 0x00, 0x2b, 0xfa, 0xd1, 0x70, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xa0, 0xf0, 0x2f
|
||||
@@ -0,0 +1,23 @@
|
||||
// Padded and checksummed version of: boot2_w25q64jv.4.bin
|
||||
|
||||
.cpu cortex-m0plus
|
||||
.thumb
|
||||
|
||||
.section .boot2, "ax"
|
||||
|
||||
.byte 0xf7, 0xb5, 0x73, 0x46, 0x21, 0x22, 0x02, 0x25, 0x01, 0x93, 0x29, 0x4b, 0xc0, 0x24, 0x5a, 0x60
|
||||
.byte 0x9a, 0x68, 0x01, 0x26, 0xaa, 0x43, 0xda, 0x60, 0x9a, 0x60, 0x1a, 0x61, 0x5a, 0x61, 0x00, 0x23
|
||||
.byte 0x64, 0x05, 0xa3, 0x60, 0x04, 0x33, 0x63, 0x61, 0x22, 0x4b, 0x28, 0x00, 0x1e, 0x60, 0xe0, 0x23
|
||||
.byte 0xdb, 0x02, 0x23, 0x60, 0x35, 0x23, 0xa6, 0x60, 0x23, 0x66, 0x23, 0x66, 0x00, 0xf0, 0x4a, 0xf8
|
||||
.byte 0xc0, 0xb2, 0xa8, 0x42, 0x12, 0xd0, 0x06, 0x23, 0x30, 0x00, 0x23, 0x66, 0x00, 0xf0, 0x42, 0xf8
|
||||
.byte 0x31, 0x23, 0x28, 0x00, 0x23, 0x66, 0x25, 0x66, 0x00, 0xf0, 0x3c, 0xf8, 0x03, 0x35, 0x25, 0x66
|
||||
.byte 0x02, 0x20, 0x25, 0x66, 0x00, 0xf0, 0x36, 0xf8, 0x30, 0x42, 0xf8, 0xd1, 0x00, 0x25, 0x12, 0x4b
|
||||
.byte 0xa5, 0x60, 0x12, 0x4f, 0x23, 0x60, 0x12, 0x4b, 0x65, 0x60, 0x01, 0x26, 0x3b, 0x60, 0xeb, 0x23
|
||||
.byte 0xa6, 0x60, 0x23, 0x66, 0x4b, 0x3b, 0x23, 0x66, 0x02, 0x20, 0x00, 0xf0, 0x23, 0xf8, 0x0d, 0x4b
|
||||
.byte 0xa5, 0x60, 0x3b, 0x60, 0xa6, 0x60, 0x01, 0x9b, 0xab, 0x42, 0x08, 0xd1, 0x0a, 0x4b, 0x0b, 0x4a
|
||||
.byte 0x13, 0x60, 0x1b, 0x68, 0x83, 0xf3, 0x08, 0x88, 0x09, 0x4b, 0x1b, 0x68, 0x18, 0x47, 0xf7, 0xbd
|
||||
.byte 0x00, 0x00, 0x02, 0x40, 0xf0, 0x00, 0x00, 0x18, 0x00, 0x03, 0x5f, 0x00, 0xf4, 0x00, 0x00, 0x18
|
||||
.byte 0x21, 0x22, 0x00, 0x00, 0x22, 0x20, 0x00, 0xa0, 0x00, 0x01, 0x00, 0x10, 0x08, 0xed, 0x00, 0xe0
|
||||
.byte 0x04, 0x01, 0x00, 0x10, 0xc0, 0x23, 0x02, 0x00, 0x04, 0x21, 0x5b, 0x05, 0x98, 0x6a, 0x08, 0x42
|
||||
.byte 0xfc, 0xd0, 0x01, 0x21, 0x98, 0x6a, 0x08, 0x42, 0xfc, 0xd1, 0x18, 0x6e, 0x01, 0x2a, 0x00, 0xd0
|
||||
.byte 0x18, 0x6e, 0x70, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x2d, 0x68, 0xca
|
||||
+25
-5
@@ -28,7 +28,7 @@
|
||||
#include "api/ArduinoAPI.h"
|
||||
#include "api/itoa.h" // ARM toolchain doesn't provide itoa etc, provide them
|
||||
#include <pins_arduino.h>
|
||||
#include "hardware/gpio.h" // Required for the port*Register macros
|
||||
#include <hardware/gpio.h> // Required for the port*Register macros
|
||||
#include "debug_internal.h"
|
||||
#include <RP2040.h> // CMSIS
|
||||
|
||||
@@ -68,23 +68,29 @@ void noInterrupts();
|
||||
#define portOutputRegister(port) ((volatile uint32_t*) sio_hw->gpio_out)
|
||||
#define portInputRegister(port) ((volatile uint32_t*) sio_hw->gpio_in)
|
||||
#define portModeRegister(port) ((volatile uint32_t*) sio_hw->gpio_oe)
|
||||
#define digitalWriteFast(pin, val) (val ? sio_hw->gpio_set = (1 << pin) : sio_hw->gpio_clr = (1 << pin))
|
||||
#define digitalReadFast(pin) ((1 << pin) & sio_hw->gpio_in)
|
||||
#define sei() interrupts()
|
||||
#define cli() noInterrupts()
|
||||
|
||||
// ADC RP2040-specific calls
|
||||
void analogReadResolution(int bits);
|
||||
float analogReadTemp(); // Returns core temp in Centigrade
|
||||
#ifdef __cplusplus
|
||||
float analogReadTemp(float vref = 3.3); // Returns core temp in Centigrade
|
||||
#endif
|
||||
|
||||
// PWM RP2040-specific calls
|
||||
void analogWriteFreq(uint32_t freq);
|
||||
void analogWriteRange(uint32_t range);
|
||||
void analogWriteResolution(int res);
|
||||
|
||||
// FreeRTOS potential calls
|
||||
extern bool __isFreeRTOS;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
// FreeRTOS potential calls
|
||||
extern bool __isFreeRTOS;
|
||||
|
||||
// Ancient AVR defines
|
||||
#define HAVE_HWSERIAL0
|
||||
#define HAVE_HWSERIAL1
|
||||
@@ -101,6 +107,9 @@ extern bool __isFreeRTOS;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
// emptyString is an ESP-ism, a constant string with ""
|
||||
extern const String emptyString;
|
||||
|
||||
#ifdef USE_TINYUSB
|
||||
// Needed for declaring Serial
|
||||
#include "Adafruit_USBD_CDC.h"
|
||||
@@ -120,3 +129,14 @@ constexpr uint32_t __bitset(const int (&a)[N], size_t i = 0U) {
|
||||
return i < N ? (1L << a[i]) | __bitset(a, i + 1) : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Warn users trying to use Pico SDK's STDIO implementation
|
||||
#include <pico/stdio.h> // Ensure it won't be re-included elsewhere
|
||||
#undef stdio_uart_init
|
||||
#define stdio_uart_init(...) static_assert(0, "stdio_uart_init is not supported or needed. Either use Serial.printf() or set the debug port in the IDE to Serial/1/2 and use printf(). See https://github.com/earlephilhower/arduino-pico/issues/1433#issuecomment-1540354673 and https://github.com/earlephilhower/arduino-pico/issues/1433#issuecomment-1546783109")
|
||||
#undef stdio_init_all
|
||||
#define stdio_init_all(...) static_assert(0, "stdio_init_all is not supported or needed. Either use Serial.printf() or set the debug port in the IDE to Serial/1/2 and use printf(). See https://github.com/earlephilhower/arduino-pico/issues/1433#issuecomment-1540354673 and https://github.com/earlephilhower/arduino-pico/issues/1433#issuecomment-1546783109")
|
||||
#undef stdio_usb_init
|
||||
#define stdio_usb_init(...) static_assert(0, "stdio_usb_init is not supported or needed. Either use Serial.printf() or set the debug port in the IDE to Serial/1/2 and use printf(). See https://github.com/earlephilhower/arduino-pico/issues/1433#issuecomment-1540354673 and https://github.com/earlephilhower/arduino-pico/issues/1433#issuecomment-1546783109")
|
||||
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#include <Arduino.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/sync.h"
|
||||
#include "hardware/structs/ioqspi.h"
|
||||
#include "hardware/structs/sio.h"
|
||||
#include <pico/stdlib.h>
|
||||
#include <hardware/gpio.h>
|
||||
#include <hardware/sync.h>
|
||||
#include <hardware/structs/ioqspi.h>
|
||||
#include <hardware/structs/sio.h>
|
||||
|
||||
// This example blinks the Pico LED when the BOOTSEL button is pressed.
|
||||
//
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
CoreMutex for the Raspberry Pi Pico RP2040
|
||||
|
||||
Implements a deadlock-safe multicore mutex for sharing things like the
|
||||
USB or UARTs.
|
||||
|
||||
Copyright (c) 2021 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 "CoreMutex.h"
|
||||
|
||||
CoreMutex::CoreMutex(mutex_t *mutex, uint8_t option) {
|
||||
_mutex = mutex;
|
||||
_acquired = false;
|
||||
_option = option;
|
||||
_pxHigherPriorityTaskWoken = 0; // pdFALSE
|
||||
if (__isFreeRTOS) {
|
||||
auto m = __get_freertos_mutex_for_ptr(mutex);
|
||||
|
||||
if (__freertos_check_if_in_isr()) {
|
||||
if (!__freertos_mutex_take_from_isr(m, &_pxHigherPriorityTaskWoken)) {
|
||||
return;
|
||||
}
|
||||
// At this point we have the mutex in ISR
|
||||
} else {
|
||||
// Grab the mutex normally, possibly waking other tasks to get it
|
||||
__freertos_mutex_take(m);
|
||||
}
|
||||
} else {
|
||||
uint32_t owner;
|
||||
if (!mutex_try_enter(_mutex, &owner)) {
|
||||
if (owner == get_core_num()) { // Deadlock!
|
||||
if (_option & DebugEnable) {
|
||||
DEBUGCORE("CoreMutex - Deadlock detected!\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
mutex_enter_blocking(_mutex);
|
||||
}
|
||||
}
|
||||
_acquired = true;
|
||||
}
|
||||
|
||||
CoreMutex::~CoreMutex() {
|
||||
if (_acquired) {
|
||||
if (__isFreeRTOS) {
|
||||
auto m = __get_freertos_mutex_for_ptr(_mutex);
|
||||
if (__freertos_check_if_in_isr()) {
|
||||
__freertos_mutex_give_from_isr(m, &_pxHigherPriorityTaskWoken);
|
||||
} else {
|
||||
__freertos_mutex_give(m);
|
||||
}
|
||||
} else {
|
||||
mutex_exit(_mutex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,42 +23,17 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pico/mutex.h"
|
||||
#include <pico/mutex.h>
|
||||
#include "_freertos.h"
|
||||
|
||||
enum {
|
||||
DebugEnable = 1
|
||||
};
|
||||
|
||||
class CoreMutex {
|
||||
public:
|
||||
CoreMutex(mutex_t *mutex, bool debugEnable = true) {
|
||||
_mutex = mutex;
|
||||
_acquired = false;
|
||||
if (__isFreeRTOS) {
|
||||
auto m = __get_freertos_mutex_for_ptr(mutex);
|
||||
__freertos_mutex_take(m);
|
||||
} else {
|
||||
uint32_t owner;
|
||||
if (!mutex_try_enter(_mutex, &owner)) {
|
||||
if (owner == get_core_num()) { // Deadlock!
|
||||
if (debugEnable) {
|
||||
DEBUGCORE("CoreMutex - Deadlock detected!\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
mutex_enter_blocking(_mutex);
|
||||
}
|
||||
}
|
||||
_acquired = true;
|
||||
}
|
||||
|
||||
~CoreMutex() {
|
||||
if (_acquired) {
|
||||
if (__isFreeRTOS) {
|
||||
auto m = __get_freertos_mutex_for_ptr(_mutex);
|
||||
__freertos_mutex_give(m);
|
||||
} else {
|
||||
mutex_exit(_mutex);
|
||||
}
|
||||
}
|
||||
}
|
||||
CoreMutex(mutex_t *mutex, uint8_t option = DebugEnable);
|
||||
~CoreMutex();
|
||||
|
||||
operator bool() {
|
||||
return _acquired;
|
||||
@@ -67,4 +42,6 @@ public:
|
||||
private:
|
||||
mutex_t *_mutex;
|
||||
bool _acquired;
|
||||
uint8_t _option;
|
||||
BaseType_t _pxHigherPriorityTaskWoken;
|
||||
};
|
||||
|
||||
+6
-7
@@ -192,13 +192,12 @@ File File::openNextFile() {
|
||||
String File::readString() {
|
||||
String ret;
|
||||
ret.reserve(size() - position());
|
||||
char temp[256 + 1];
|
||||
int countRead = readBytes(temp, sizeof(temp) - 1);
|
||||
while (countRead > 0) {
|
||||
temp[countRead] = 0;
|
||||
ret += temp;
|
||||
countRead = readBytes(temp, sizeof(temp) - 1);
|
||||
}
|
||||
uint8_t temp[256];
|
||||
int countRead;
|
||||
do {
|
||||
countRead = read(temp, sizeof(temp));
|
||||
ret.concat(temp, countRead);
|
||||
} while (countRead > 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
RP2040 PIO utility class
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "PIOProgram.h"
|
||||
#include <map>
|
||||
|
||||
static std::map<const pio_program_t *, int> __pioMap[2];
|
||||
auto_init_mutex(_pioMutex);
|
||||
|
||||
|
||||
PIOProgram::PIOProgram(const pio_program_t *pgm) {
|
||||
_pgm = pgm;
|
||||
_pio = nullptr;
|
||||
_sm = -1;
|
||||
}
|
||||
|
||||
// We leave the INSN loaded in INSN RAM
|
||||
PIOProgram::~PIOProgram() {
|
||||
if (_pio) {
|
||||
pio_sm_unclaim(_pio, _sm);
|
||||
}
|
||||
}
|
||||
|
||||
// Possibly load into a PIO and allocate a SM
|
||||
bool PIOProgram::prepare(PIO *pio, int *sm, int *offset) {
|
||||
CoreMutex m(&_pioMutex);
|
||||
PIO pi[2] = { pio0, pio1 };
|
||||
|
||||
// If it's already loaded into PIO IRAM, try and allocate in that specific PIO
|
||||
for (int o = 0; o < 2; o++) {
|
||||
auto p = __pioMap[o].find(_pgm);
|
||||
if (p != __pioMap[o].end()) {
|
||||
int idx = pio_claim_unused_sm(pi[o], false);
|
||||
if (idx >= 0) {
|
||||
_pio = pi[o];
|
||||
_sm = idx;
|
||||
*pio = pi[o];
|
||||
*sm = idx;
|
||||
*offset = p->second;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Not in any PIO IRAM, so try and add
|
||||
for (int o = 0; o < 2; o++) {
|
||||
if (pio_can_add_program(pi[o], _pgm)) {
|
||||
int idx = pio_claim_unused_sm(pi[o], false);
|
||||
if (idx >= 0) {
|
||||
int off = pio_add_program(pi[o], _pgm);
|
||||
__pioMap[o].insert({_pgm, off});
|
||||
_pio = pi[o];
|
||||
_sm = idx;
|
||||
*pio = pi[o];
|
||||
*sm = idx;
|
||||
*offset = off;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Nope, no room either for SMs or INSNs
|
||||
return false;
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
/*
|
||||
WiFiMutex.h - Ensure the timer-driven sys_check_timeouts doesn't
|
||||
get executed while we're in the user-level TCP stack.
|
||||
Copyright (c) 2022 Earle F. Philhower, III. All rights reserved.
|
||||
RP2040 PIO utility class
|
||||
|
||||
Implements the API defined by the Arduino WiFiNINA library,
|
||||
copyright (c) 2018 Arduino SA. All rights reserved.
|
||||
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
|
||||
@@ -23,19 +20,18 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
extern "C" volatile bool __inLWIP;
|
||||
#include <hardware/pio.h>
|
||||
|
||||
class LWIPMutex {
|
||||
// Wrapper class for PIO programs, abstracting common operations out
|
||||
class PIOProgram {
|
||||
public:
|
||||
LWIPMutex() {
|
||||
__inLWIP = true;
|
||||
_ref++;
|
||||
}
|
||||
~LWIPMutex() {
|
||||
if (0 == --_ref) {
|
||||
__inLWIP = false;
|
||||
}
|
||||
}
|
||||
PIOProgram(const pio_program_t *pgm);
|
||||
~PIOProgram();
|
||||
// Possibly load into a PIO and allocate a SM
|
||||
bool prepare(PIO *pio, int *sm, int *offset);
|
||||
|
||||
private:
|
||||
static int _ref;
|
||||
const pio_program_t *_pgm;
|
||||
PIO _pio;
|
||||
int _sm;
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
RP2040 utility class
|
||||
|
||||
Copyright (c) 2021 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/structs/psm.h>
|
||||
|
||||
extern "C" void boot_double_tap_check();
|
||||
|
||||
// The following check will never actually execute, but it will cause the boot reset
|
||||
// checker to be linked in as part of the constructors.
|
||||
|
||||
void RP2040::enableDoubleResetBootloader() {
|
||||
if (psm_hw->done == 0) {
|
||||
boot_double_tap_check();
|
||||
}
|
||||
}
|
||||
@@ -27,8 +27,11 @@
|
||||
#include <hardware/structs/rosc.h>
|
||||
#include <hardware/structs/systick.h>
|
||||
#include <pico/multicore.h>
|
||||
#include <pico/rand.h>
|
||||
#include <pico/util/queue.h>
|
||||
#include <pico/bootrom.h>
|
||||
#include "CoreMutex.h"
|
||||
#include "PIOProgram.h"
|
||||
#include "ccount.pio.h"
|
||||
#include <malloc.h>
|
||||
|
||||
@@ -90,15 +93,14 @@ public:
|
||||
if (!_multicore) {
|
||||
return;
|
||||
}
|
||||
__holdUpPendSV = 1;
|
||||
if (__isFreeRTOS) {
|
||||
vTaskPreemptionDisable(nullptr);
|
||||
vTaskSuspendAll();
|
||||
__freertos_idle_other_core();
|
||||
} else {
|
||||
mutex_enter_blocking(&_idleMutex);
|
||||
__otherCoreIdled = false;
|
||||
multicore_fifo_push_blocking(_GOTOSLEEP);
|
||||
while (!__otherCoreIdled) { /* noop */ }
|
||||
}
|
||||
mutex_enter_blocking(&_idleMutex);
|
||||
__otherCoreIdled = false;
|
||||
multicore_fifo_push_blocking(_GOTOSLEEP);
|
||||
while (!__otherCoreIdled) { /* noop */ }
|
||||
}
|
||||
|
||||
void resumeOtherCore() {
|
||||
@@ -108,10 +110,8 @@ public:
|
||||
mutex_exit(&_idleMutex);
|
||||
__otherCoreIdled = false;
|
||||
if (__isFreeRTOS) {
|
||||
xTaskResumeAll();
|
||||
vTaskPreemptionEnable(nullptr);
|
||||
__freertos_resume_other_core();
|
||||
}
|
||||
__holdUpPendSV = 0;
|
||||
|
||||
// Other core will exit busy-loop and return to operation
|
||||
// once __otherCoreIdled == false.
|
||||
@@ -155,65 +155,9 @@ private:
|
||||
class RP2040;
|
||||
extern RP2040 rp2040;
|
||||
extern "C" void main1();
|
||||
class PIOProgram;
|
||||
|
||||
extern "C" char __StackLimit;
|
||||
extern "C" char __bss_end__;
|
||||
|
||||
// Wrapper class for PIO programs, abstracting common operations out
|
||||
// TODO - Add unload/destructor
|
||||
class PIOProgram {
|
||||
public:
|
||||
PIOProgram(const pio_program_t *pgm) {
|
||||
_pgm = pgm;
|
||||
}
|
||||
|
||||
// Possibly load into a PIO and allocate a SM
|
||||
bool prepare(PIO *pio, int *sm, int *offset) {
|
||||
extern mutex_t _pioMutex;
|
||||
CoreMutex m(&_pioMutex);
|
||||
// Is there an open slot to run in, first?
|
||||
if (!_findFreeSM(pio, sm)) {
|
||||
return false;
|
||||
}
|
||||
// Is it loaded on that PIO?
|
||||
if (_offset[pio_get_index(*pio)] < 0) {
|
||||
// Nope, need to load it
|
||||
if (!pio_can_add_program(*pio, _pgm)) {
|
||||
return false;
|
||||
}
|
||||
_offset[pio_get_index(*pio)] = pio_add_program(*pio, _pgm);
|
||||
}
|
||||
// Here it's guaranteed loaded, return values
|
||||
// PIO and SM already set
|
||||
*offset = _offset[pio_get_index(*pio)];
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
// Find an unused PIO state machine to grab, returns false when none available
|
||||
static bool _findFreeSM(PIO *pio, int *sm) {
|
||||
int idx = pio_claim_unused_sm(pio0, false);
|
||||
if (idx >= 0) {
|
||||
*pio = pio0;
|
||||
*sm = idx;
|
||||
return true;
|
||||
}
|
||||
idx = pio_claim_unused_sm(pio1, false);
|
||||
if (idx >= 0) {
|
||||
*pio = pio1;
|
||||
*sm = idx;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
int _offset[2] = { -1, -1 };
|
||||
const pio_program_t *_pgm;
|
||||
};
|
||||
|
||||
class RP2040 {
|
||||
public:
|
||||
RP2040() { /* noop */ }
|
||||
@@ -246,6 +190,11 @@ public:
|
||||
return clock_get_hz(clk_sys);
|
||||
}
|
||||
|
||||
// Get current CPU core number
|
||||
static int cpuid() {
|
||||
return sio_hw->cpuid;
|
||||
}
|
||||
|
||||
// Get CPU cycle count. Needs to do magic to extens 24b HW to something longer
|
||||
volatile uint64_t _epoch = 0;
|
||||
inline uint32_t getCycleCount() {
|
||||
@@ -314,6 +263,15 @@ public:
|
||||
reboot();
|
||||
}
|
||||
|
||||
inline void rebootToBootloader() {
|
||||
reset_usb_boot(0, 0);
|
||||
while (1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
static void enableDoubleResetBootloader();
|
||||
|
||||
void wdt_begin(uint32_t delay_ms) {
|
||||
watchdog_enable(delay_ms, 1);
|
||||
}
|
||||
@@ -323,7 +281,7 @@ public:
|
||||
}
|
||||
|
||||
const char *getChipID() {
|
||||
static char id[PICO_UNIQUE_BOARD_ID_SIZE_BYTES + 1] = { 0 };
|
||||
static char id[2 * PICO_UNIQUE_BOARD_ID_SIZE_BYTES + 1] = { 0 };
|
||||
if (!id[0]) {
|
||||
pico_get_unique_board_id_string(id, sizeof(id));
|
||||
}
|
||||
@@ -334,28 +292,20 @@ public:
|
||||
_MFIFO fifo;
|
||||
|
||||
|
||||
// TODO - Not so great HW random generator. 32-bits wide. Cryptographers somewhere are crying
|
||||
uint32_t hwrand32() {
|
||||
// Try and whiten the HW ROSC bit
|
||||
uint32_t r = 0;
|
||||
for (int k = 0; k < 32; k++) {
|
||||
unsigned long int b;
|
||||
do {
|
||||
b = rosc_hw->randombit & 1;
|
||||
if (b != (rosc_hw->randombit & 1)) {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
r <<= 1;
|
||||
r |= b;
|
||||
}
|
||||
// Stir using the cycle count LSBs. In any WiFi use case this will be a random # since the connection time is not cycle-accurate
|
||||
uint64_t rr = (((uint64_t)~r) << 32LL) | r;
|
||||
rr >>= rp2040.getCycleCount() & 32LL;
|
||||
|
||||
return (uint32_t)rr;
|
||||
return get_rand_32();
|
||||
}
|
||||
|
||||
bool isPicoW() {
|
||||
#if !defined(ARDUINO_RASPBERRY_PI_PICO_W)
|
||||
return false;
|
||||
#else
|
||||
extern bool __isPicoW;
|
||||
return __isPicoW;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
static void _SystickHandler() {
|
||||
rp2040._epoch += 1LL << 24;
|
||||
|
||||
+277
-27
@@ -25,14 +25,18 @@
|
||||
#include "CoreMutex.h"
|
||||
#include "RP2040USB.h"
|
||||
|
||||
#include "tusb.h"
|
||||
#include "class/hid/hid_device.h"
|
||||
#include "class/audio/audio.h"
|
||||
#include "class/midi/midi.h"
|
||||
#include "pico/time.h"
|
||||
#include "hardware/irq.h"
|
||||
#include "pico/mutex.h"
|
||||
#include "pico/unique_id.h"
|
||||
#include <tusb.h>
|
||||
#include <class/hid/hid_device.h>
|
||||
#include <class/audio/audio.h>
|
||||
#include <pico/time.h>
|
||||
#include <hardware/irq.h>
|
||||
#include <pico/mutex.h>
|
||||
#include <pico/unique_id.h>
|
||||
#include <pico/usb_reset_interface.h>
|
||||
#include <hardware/watchdog.h>
|
||||
#include <pico/bootrom.h>
|
||||
#include "sdkoverride/tusb_absmouse.h"
|
||||
#include <device/usbd_pvt.h>
|
||||
|
||||
// Big, global USB mutex, shared with all USB devices to make sure we don't
|
||||
// have multiple cores updating the TUSB state in parallel
|
||||
@@ -42,12 +46,11 @@ mutex_t __usb_mutex;
|
||||
#define USB_TASK_INTERVAL 1000
|
||||
static int __usb_task_irq;
|
||||
|
||||
// USB VID/PID (note that PID can change depending on the add'l interfaces)
|
||||
#ifndef USBD_VID
|
||||
#define USBD_VID (0x2E8A) // Raspberry Pi
|
||||
#endif
|
||||
|
||||
#ifdef SERIALUSB_PID
|
||||
#define USBD_PID (SERIALUSB_PID)
|
||||
#else
|
||||
#ifndef USBD_PID
|
||||
#define USBD_PID (0x000a) // Raspberry Pi Pico SDK CDC
|
||||
#endif
|
||||
|
||||
@@ -67,10 +70,18 @@ static int __usb_task_irq;
|
||||
#define USBD_STR_PRODUCT (0x02)
|
||||
#define USBD_STR_SERIAL (0x03)
|
||||
#define USBD_STR_CDC (0x04)
|
||||
|
||||
#define USBD_STR_RPI_RESET (0x05)
|
||||
|
||||
#define EPNUM_HID 0x83
|
||||
|
||||
#define USBD_MSC_EPOUT 0x03
|
||||
#define USBD_MSC_EPIN 0x84
|
||||
#define USBD_MSC_EPSIZE 64
|
||||
|
||||
#define TUD_RPI_RESET_DESCRIPTOR(_itfnum, _stridx) \
|
||||
/* Interface */\
|
||||
9, TUSB_DESC_INTERFACE, _itfnum, 0, 0, TUSB_CLASS_VENDOR_SPECIFIC, RESET_INTERFACE_SUBCLASS, RESET_INTERFACE_PROTOCOL, _stridx,
|
||||
|
||||
|
||||
const uint8_t *tud_descriptor_device_cb(void) {
|
||||
static tusb_desc_device_t usbd_desc_device = {
|
||||
@@ -89,7 +100,7 @@ const uint8_t *tud_descriptor_device_cb(void) {
|
||||
.iSerialNumber = USBD_STR_SERIAL,
|
||||
.bNumConfigurations = 1
|
||||
};
|
||||
if (__USBInstallSerial && !__USBInstallKeyboard && !__USBInstallMouse && !__USBInstallJoystick) {
|
||||
if (__USBInstallSerial && !__USBInstallKeyboard && !__USBInstallMouse && !__USBInstallAbsoluteMouse && !__USBInstallJoystick && !__USBInstallMassStorage) {
|
||||
// Can use as-is, this is the default USB case
|
||||
return (const uint8_t *)&usbd_desc_device;
|
||||
}
|
||||
@@ -97,12 +108,15 @@ const uint8_t *tud_descriptor_device_cb(void) {
|
||||
if (__USBInstallKeyboard) {
|
||||
usbd_desc_device.idProduct |= 0x8000;
|
||||
}
|
||||
if (__USBInstallMouse) {
|
||||
if (__USBInstallMouse || __USBInstallAbsoluteMouse) {
|
||||
usbd_desc_device.idProduct |= 0x4000;
|
||||
}
|
||||
if (__USBInstallJoystick) {
|
||||
usbd_desc_device.idProduct |= 0x0100;
|
||||
}
|
||||
if (__USBInstallMassStorage) {
|
||||
usbd_desc_device.idProduct ^= 0x2000;
|
||||
}
|
||||
// Set the device class to 0 to indicate multiple device classes
|
||||
usbd_desc_device.bDeviceClass = 0;
|
||||
usbd_desc_device.bDeviceSubClass = 0;
|
||||
@@ -115,15 +129,15 @@ int __USBGetKeyboardReportID() {
|
||||
}
|
||||
|
||||
int __USBGetMouseReportID() {
|
||||
return __USBInstallKeyboard ? 2 : 1;
|
||||
return __USBInstallKeyboard ? 3 : 1;
|
||||
}
|
||||
|
||||
int __USBGetJoystickReportID() {
|
||||
int i = 1;
|
||||
if (__USBInstallKeyboard) {
|
||||
i++;
|
||||
i += 2;
|
||||
}
|
||||
if (__USBInstallMouse) {
|
||||
if (__USBInstallMouse || __USBInstallAbsoluteMouse) {
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
@@ -142,8 +156,9 @@ static uint8_t *GetDescHIDReport(int *len) {
|
||||
void __SetupDescHIDReport() {
|
||||
//allocate memory for the HID report descriptors. We don't use them, but need the size here.
|
||||
uint8_t desc_hid_report_mouse[] = { TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(1)) };
|
||||
uint8_t desc_hid_report_absmouse[] = { TUD_HID_REPORT_DESC_ABSMOUSE(HID_REPORT_ID(1)) };
|
||||
uint8_t desc_hid_report_joystick[] = { TUD_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(1)) };
|
||||
uint8_t desc_hid_report_keyboard[] = { TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(1)) };
|
||||
uint8_t desc_hid_report_keyboard[] = { TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(1)), TUD_HID_REPORT_DESC_CONSUMER(HID_REPORT_ID(2)) };
|
||||
int size = 0;
|
||||
|
||||
//accumulate the size of all used HID report descriptors
|
||||
@@ -152,6 +167,8 @@ void __SetupDescHIDReport() {
|
||||
}
|
||||
if (__USBInstallMouse) {
|
||||
size += sizeof(desc_hid_report_mouse);
|
||||
} else if (__USBInstallAbsoluteMouse) {
|
||||
size += sizeof(desc_hid_report_absmouse);
|
||||
}
|
||||
if (__USBInstallJoystick) {
|
||||
size += sizeof(desc_hid_report_joystick);
|
||||
@@ -180,11 +197,19 @@ void __SetupDescHIDReport() {
|
||||
if (__USBInstallMouse) {
|
||||
//determine if we need an offset (USB keyboard is installed)
|
||||
if (__USBInstallKeyboard) {
|
||||
uint8_t desc_local[] = { TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(2)) };
|
||||
uint8_t desc_local[] = { TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(3)) };
|
||||
memcpy(__hid_report + sizeof(desc_hid_report_keyboard), desc_local, sizeof(desc_local));
|
||||
} else {
|
||||
memcpy(__hid_report, desc_hid_report_mouse, sizeof(desc_hid_report_mouse));
|
||||
}
|
||||
} else if (__USBInstallAbsoluteMouse) {
|
||||
//determine if we need an offset (USB keyboard is installed)
|
||||
if (__USBInstallKeyboard) {
|
||||
uint8_t desc_local[] = { TUD_HID_REPORT_DESC_ABSMOUSE(HID_REPORT_ID(3)) };
|
||||
memcpy(__hid_report + sizeof(desc_hid_report_keyboard), desc_local, sizeof(desc_local));
|
||||
} else {
|
||||
memcpy(__hid_report, desc_hid_report_absmouse, sizeof(desc_hid_report_absmouse));
|
||||
}
|
||||
}
|
||||
|
||||
//3.) joystick descriptor. 2 additional checks are necessary for mouse and/or keyboard
|
||||
@@ -192,12 +217,15 @@ void __SetupDescHIDReport() {
|
||||
uint8_t reportid = 1;
|
||||
int offset = 0;
|
||||
if (__USBInstallKeyboard) {
|
||||
reportid++;
|
||||
reportid += 2;
|
||||
offset += sizeof(desc_hid_report_keyboard);
|
||||
}
|
||||
if (__USBInstallMouse) {
|
||||
reportid++;
|
||||
offset += sizeof(desc_hid_report_mouse);
|
||||
} else if (__USBInstallAbsoluteMouse) {
|
||||
reportid++;
|
||||
offset += sizeof(desc_hid_report_absmouse);
|
||||
}
|
||||
uint8_t desc_local[] = { TUD_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(reportid)) };
|
||||
memcpy(__hid_report + offset, desc_local, sizeof(desc_local));
|
||||
@@ -221,9 +249,9 @@ const uint8_t *tud_descriptor_configuration_cb(uint8_t index) {
|
||||
|
||||
void __SetupUSBDescriptor() {
|
||||
if (!usbd_desc_cfg) {
|
||||
bool hasHID = __USBInstallKeyboard || __USBInstallMouse || __USBInstallJoystick;
|
||||
bool hasHID = __USBInstallKeyboard || __USBInstallMouse || __USBInstallAbsoluteMouse || __USBInstallJoystick;
|
||||
|
||||
uint8_t interface_count = (__USBInstallSerial ? 2 : 0) + (hasHID ? 1 : 0);
|
||||
uint8_t interface_count = (__USBInstallSerial ? 2 : 0) + (hasHID ? 1 : 0) + (__USBInstallMassStorage ? 1 : 0);
|
||||
|
||||
uint8_t cdc_desc[TUD_CDC_DESC_LEN] = {
|
||||
// Interface number, string index, protocol, report descriptor len, EP In & Out address, size & polling interval
|
||||
@@ -238,7 +266,20 @@ void __SetupUSBDescriptor() {
|
||||
TUD_HID_DESCRIPTOR(hid_itf, 0, HID_ITF_PROTOCOL_NONE, hid_report_len, EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 10)
|
||||
};
|
||||
|
||||
int usbd_desc_len = TUD_CONFIG_DESC_LEN + (__USBInstallSerial ? sizeof(cdc_desc) : 0) + (hasHID ? sizeof(hid_desc) : 0);
|
||||
uint8_t msd_itf = interface_count - 1;
|
||||
uint8_t msd_desc[TUD_MSC_DESC_LEN] = {
|
||||
TUD_MSC_DESCRIPTOR(msd_itf, 0, USBD_MSC_EPOUT, USBD_MSC_EPIN, USBD_MSC_EPSIZE)
|
||||
};
|
||||
|
||||
int usbd_desc_len = TUD_CONFIG_DESC_LEN + (__USBInstallSerial ? sizeof(cdc_desc) : 0) + (hasHID ? sizeof(hid_desc) : 0) + (__USBInstallMassStorage ? sizeof(msd_desc) : 0);
|
||||
|
||||
#ifdef ENABLE_PICOTOOL_USB
|
||||
uint8_t picotool_itf = interface_count++;
|
||||
uint8_t picotool_desc[] = {
|
||||
TUD_RPI_RESET_DESCRIPTOR(picotool_itf, USBD_STR_RPI_RESET)
|
||||
};
|
||||
usbd_desc_len += sizeof(picotool_desc);
|
||||
#endif
|
||||
|
||||
uint8_t tud_cfg_desc[TUD_CONFIG_DESC_LEN] = {
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
@@ -260,23 +301,34 @@ void __SetupUSBDescriptor() {
|
||||
memcpy(ptr, hid_desc, sizeof(hid_desc));
|
||||
ptr += sizeof(hid_desc);
|
||||
}
|
||||
if (__USBInstallMassStorage) {
|
||||
memcpy(ptr, msd_desc, sizeof(msd_desc));
|
||||
ptr += sizeof(msd_desc);
|
||||
}
|
||||
#ifdef ENABLE_PICOTOOL_USB
|
||||
memcpy(ptr, picotool_desc, sizeof(picotool_desc));
|
||||
ptr += sizeof(picotool_desc);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||
(void) langid;
|
||||
#define DESC_STR_MAX (20)
|
||||
#define DESC_STR_MAX (32)
|
||||
static uint16_t desc_str[DESC_STR_MAX];
|
||||
|
||||
static char idString[PICO_UNIQUE_BOARD_ID_SIZE_BYTES * 2 + 1];
|
||||
|
||||
static const char *const usbd_desc_str[] = {
|
||||
[USBD_STR_0] = "",
|
||||
[USBD_STR_MANUF] = "Raspberry Pi",
|
||||
[USBD_STR_PRODUCT] = "PicoArduino",
|
||||
[USBD_STR_MANUF] = USB_MANUFACTURER,
|
||||
[USBD_STR_PRODUCT] = USB_PRODUCT,
|
||||
[USBD_STR_SERIAL] = idString,
|
||||
[USBD_STR_CDC] = "Board CDC",
|
||||
#ifdef ENABLE_PICOTOOL_USB
|
||||
[USBD_STR_RPI_RESET] = "Reset",
|
||||
#endif
|
||||
};
|
||||
|
||||
if (!idString[0]) {
|
||||
@@ -342,9 +394,22 @@ void __USBStart() {
|
||||
}
|
||||
|
||||
|
||||
bool __USBHIDReady() {
|
||||
uint32_t start = millis();
|
||||
const uint32_t timeout = 500;
|
||||
|
||||
while (((millis() - start) < timeout) && tud_ready() && !tud_hid_ready()) {
|
||||
tud_task();
|
||||
delay(1);
|
||||
}
|
||||
return tud_hid_ready();
|
||||
}
|
||||
|
||||
|
||||
// Invoked when received GET_REPORT control request
|
||||
// Application must fill buffer report's content and return its length.
|
||||
// Return zero will cause the stack to STALL request
|
||||
extern "C" uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) __attribute__((weak));
|
||||
extern "C" uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) {
|
||||
// TODO not implemented
|
||||
(void) instance;
|
||||
@@ -358,6 +423,7 @@ extern "C" uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, h
|
||||
|
||||
// Invoked when received SET_REPORT control request or
|
||||
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
|
||||
extern "C" void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) __attribute__((weak));
|
||||
extern "C" void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) {
|
||||
// TODO set LED based on CAPLOCK, NUMLOCK etc...
|
||||
(void) instance;
|
||||
@@ -367,4 +433,188 @@ extern "C" void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_r
|
||||
(void) bufsize;
|
||||
}
|
||||
|
||||
extern "C" int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) __attribute__((weak));
|
||||
extern "C" int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) {
|
||||
(void) lun;
|
||||
(void) lba;
|
||||
(void) offset;
|
||||
(void) buffer;
|
||||
(void) bufsize;
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" bool tud_msc_test_unit_ready_cb(uint8_t lun) __attribute__((weak));
|
||||
extern "C" bool tud_msc_test_unit_ready_cb(uint8_t lun) {
|
||||
(void) lun;
|
||||
return false;
|
||||
}
|
||||
|
||||
extern "C" int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) __attribute__((weak));
|
||||
extern "C" int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) {
|
||||
(void) lun;
|
||||
(void) lba;
|
||||
(void) offset;
|
||||
(void) buffer;
|
||||
(void) bufsize;
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) __attribute__((weak));
|
||||
extern "C" int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) {
|
||||
(void) lun;
|
||||
(void) scsi_cmd;
|
||||
(void) buffer;
|
||||
(void) bufsize;
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size) __attribute__((weak));
|
||||
extern "C" void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size) {
|
||||
(void) lun;
|
||||
*block_count = 0;
|
||||
*block_size = 0;
|
||||
}
|
||||
|
||||
extern "C" void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) __attribute__((weak));
|
||||
extern "C" void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) {
|
||||
(void) lun;
|
||||
vendor_id[0] = 0;
|
||||
product_id[0] = 0;
|
||||
product_rev[0] = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef ENABLE_PICOTOOL_USB
|
||||
|
||||
static uint8_t _picotool_itf_num;
|
||||
|
||||
static void resetd_init() {
|
||||
}
|
||||
|
||||
static void resetd_reset(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
_picotool_itf_num = 0;
|
||||
}
|
||||
|
||||
static uint16_t resetd_open(uint8_t rhport,
|
||||
tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
|
||||
(void) rhport;
|
||||
TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass &&
|
||||
RESET_INTERFACE_SUBCLASS == itf_desc->bInterfaceSubClass &&
|
||||
RESET_INTERFACE_PROTOCOL == itf_desc->bInterfaceProtocol, 0);
|
||||
|
||||
uint16_t const drv_len = sizeof(tusb_desc_interface_t);
|
||||
TU_VERIFY(max_len >= drv_len, 0);
|
||||
|
||||
_picotool_itf_num = itf_desc->bInterfaceNumber;
|
||||
return drv_len;
|
||||
}
|
||||
|
||||
// Support for parameterized reset via vendor interface control request
|
||||
static bool resetd_control_xfer_cb(uint8_t rhport, uint8_t stage,
|
||||
tusb_control_request_t const *request) {
|
||||
(void) rhport;
|
||||
// nothing to do with DATA & ACK stage
|
||||
if (stage != CONTROL_STAGE_SETUP) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (request->wIndex == _picotool_itf_num) {
|
||||
if (request->bRequest == RESET_REQUEST_BOOTSEL) {
|
||||
reset_usb_boot(0, (request->wValue & 0x7f));
|
||||
// does not return, otherwise we'd return true
|
||||
}
|
||||
|
||||
if (request->bRequest == RESET_REQUEST_FLASH) {
|
||||
watchdog_reboot(0, 0, 100);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool resetd_xfer_cb(uint8_t rhport, uint8_t ep_addr,
|
||||
xfer_result_t result, uint32_t xferred_bytes) {
|
||||
(void) rhport;
|
||||
(void) ep_addr;
|
||||
(void) result;
|
||||
(void) xferred_bytes;
|
||||
return true;
|
||||
}
|
||||
|
||||
static usbd_class_driver_t const _resetd_driver = {
|
||||
#if CFG_TUSB_DEBUG >= 2
|
||||
.name = "RESET",
|
||||
#endif
|
||||
.init = resetd_init,
|
||||
.reset = resetd_reset,
|
||||
.open = resetd_open,
|
||||
.control_xfer_cb = resetd_control_xfer_cb,
|
||||
.xfer_cb = resetd_xfer_cb,
|
||||
.sof = NULL
|
||||
};
|
||||
|
||||
// Implement callback to add our custom driver
|
||||
usbd_class_driver_t const *usbd_app_driver_get_cb(uint8_t *driver_count) {
|
||||
*driver_count = 1;
|
||||
return &_resetd_driver;
|
||||
}
|
||||
|
||||
#elif defined NO_USB
|
||||
|
||||
// will ensure backward compatibility with existing code when using pico-debug
|
||||
|
||||
#warning "NO_USB selected. No output to Serial will occur!"
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
void SerialUSB::begin(unsigned long baud) {
|
||||
}
|
||||
|
||||
void SerialUSB::end() {
|
||||
|
||||
}
|
||||
|
||||
int SerialUSB::peek() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SerialUSB::read() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int SerialUSB::available() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SerialUSB::availableForWrite() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SerialUSB::flush() {
|
||||
|
||||
}
|
||||
|
||||
size_t SerialUSB::write(uint8_t c) {
|
||||
(void) c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t SerialUSB::write(const uint8_t *buf, size_t length) {
|
||||
(void) buf;
|
||||
(void) length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SerialUSB::operator bool() {
|
||||
return false;
|
||||
}
|
||||
|
||||
SerialUSB Serial;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -19,13 +19,20 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "pico/mutex.h"
|
||||
#include <pico/mutex.h>
|
||||
|
||||
// Weak function definitions for each type of endpoint
|
||||
extern void __USBInstallSerial() __attribute__((weak));
|
||||
|
||||
extern void __USBInstallKeyboard() __attribute__((weak));
|
||||
|
||||
extern void __USBInstallJoystick() __attribute__((weak));
|
||||
|
||||
// One or the other allowed, not both
|
||||
extern void __USBInstallMouse() __attribute__((weak));
|
||||
extern void __USBInstallAbsoluteMouse() __attribute__((weak));
|
||||
|
||||
extern void __USBInstallMassStorage() __attribute__((weak));
|
||||
|
||||
// Big, global USB mutex, shared with all USB devices to make sure we don't
|
||||
// have multiple cores updating the TUSB state in parallel
|
||||
@@ -38,3 +45,6 @@ int __USBGetJoystickReportID();
|
||||
|
||||
// Called by main() to init the USB HW/SW.
|
||||
void __USBStart();
|
||||
|
||||
// Helper class for HID report sending with wait and timeout
|
||||
bool __USBHIDReady();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#define ARDUINO_PICO_MAJOR 2
|
||||
#define ARDUINO_PICO_MAJOR 3
|
||||
#define ARDUINO_PICO_MINOR 5
|
||||
#define ARDUINO_PICO_REVISION 4
|
||||
#define ARDUINO_PICO_VERSION_STR "2.5.4"
|
||||
#define ARDUINO_PICO_REVISION 0
|
||||
#define ARDUINO_PICO_VERSION_STR "3.5.0"
|
||||
|
||||
+24
-15
@@ -46,22 +46,24 @@ static pio_program_t *pio_make_uart_prog(int repl, const pio_program_t *pg) {
|
||||
return p;
|
||||
}
|
||||
|
||||
static PIOProgram *_getTxProgram(int bits) {
|
||||
auto f = _txMap.find(bits);
|
||||
static PIOProgram *_getTxProgram(int bits, bool inverted) {
|
||||
int key = inverted ? -bits : bits;
|
||||
auto f = _txMap.find(key);
|
||||
if (f == _txMap.end()) {
|
||||
pio_program_t * p = pio_make_uart_prog(bits, &pio_tx_program);
|
||||
_txMap.insert({bits, new PIOProgram(p)});
|
||||
f = _txMap.find(bits);
|
||||
pio_program_t * p = pio_make_uart_prog(bits, inverted ? &pio_tx_inv_program : &pio_tx_program);
|
||||
_txMap.insert({key, new PIOProgram(p)});
|
||||
f = _txMap.find(key);
|
||||
}
|
||||
return f->second;
|
||||
}
|
||||
|
||||
static PIOProgram *_getRxProgram(int bits) {
|
||||
auto f = _rxMap.find(bits);
|
||||
static PIOProgram *_getRxProgram(int bits, bool inverted) {
|
||||
int key = inverted ? -bits : bits;
|
||||
auto f = _rxMap.find(key);
|
||||
if (f == _rxMap.end()) {
|
||||
pio_program_t * p = pio_make_uart_prog(bits, &pio_rx_program);
|
||||
_rxMap.insert({bits, new PIOProgram(p)});
|
||||
f = _rxMap.find(bits);
|
||||
pio_program_t * p = pio_make_uart_prog(bits, inverted ? &pio_rx_inv_program : &pio_rx_program);
|
||||
_rxMap.insert({key, new PIOProgram(p)});
|
||||
f = _rxMap.find(key);
|
||||
}
|
||||
return f->second;
|
||||
}
|
||||
@@ -96,7 +98,7 @@ void __not_in_flash_func(SerialPIO::_handleIRQ)() {
|
||||
return;
|
||||
}
|
||||
while (!pio_sm_is_rx_fifo_empty(_rxPIO, _rxSM)) {
|
||||
uint32_t decode = _rxPIO->rxf[_rxSM];
|
||||
uint32_t decode = _rxPIO->rxf[_rxSM] ^ (_rxInverted ? 0xffffffff : 0);
|
||||
decode >>= 33 - _rxBits;
|
||||
uint32_t val = 0;
|
||||
for (int b = 0; b < _bits + 1; b++) {
|
||||
@@ -189,7 +191,7 @@ void SerialPIO::begin(unsigned long baud, uint16_t config) {
|
||||
|
||||
if (_tx != NOPIN) {
|
||||
_txBits = _bits + _stop + (_parity != UART_PARITY_NONE ? 1 : 0) + 1/*start bit*/;
|
||||
_txPgm = _getTxProgram(_txBits);
|
||||
_txPgm = _getTxProgram(_txBits, _txInverted);
|
||||
int off;
|
||||
if (!_txPgm->prepare(&_txPIO, &_txSM, &off)) {
|
||||
DEBUGCORE("ERROR: Unable to allocate PIO TX UART, out of PIO resources\n");
|
||||
@@ -216,7 +218,7 @@ void SerialPIO::begin(unsigned long baud, uint16_t config) {
|
||||
_reader = 0;
|
||||
|
||||
_rxBits = 2 * (_bits + _stop + (_parity != UART_PARITY_NONE ? 1 : 0) + 1) - 1;
|
||||
_rxPgm = _getRxProgram(_rxBits);
|
||||
_rxPgm = _getRxProgram(_rxBits, _rxInverted);
|
||||
int off;
|
||||
if (!_rxPgm->prepare(&_rxPIO, &_rxSM, &off)) {
|
||||
DEBUGCORE("ERROR: Unable to allocate PIO RX UART, out of PIO resources\n");
|
||||
@@ -230,7 +232,7 @@ void SerialPIO::begin(unsigned long baud, uint16_t config) {
|
||||
pio_sm_clear_fifos(_rxPIO, _rxSM); // Remove any existing data
|
||||
|
||||
// Put phase divider into OSR w/o using add'l program memory
|
||||
pio_sm_put_blocking(_rxPIO, _rxSM, clock_get_hz(clk_sys) / (_baud * 2) - 5 /* insns in PIO halfbit loop */);
|
||||
pio_sm_put_blocking(_rxPIO, _rxSM, clock_get_hz(clk_sys) / (_baud * 2) - 7 /* insns in PIO halfbit loop */);
|
||||
pio_sm_exec(_rxPIO, _rxSM, pio_encode_pull(false, false));
|
||||
|
||||
// Join the TX FIFO to the RX one now that we don't need it
|
||||
@@ -259,9 +261,11 @@ void SerialPIO::end() {
|
||||
}
|
||||
if (_tx != NOPIN) {
|
||||
pio_sm_set_enabled(_txPIO, _txSM, false);
|
||||
pio_sm_unclaim(_txPIO, _txSM);
|
||||
}
|
||||
if (_rx != NOPIN) {
|
||||
pio_sm_set_enabled(_rxPIO, _rxSM, false);
|
||||
pio_sm_unclaim(_rxPIO, _rxSM);
|
||||
_pioSP[pio_get_index(_rxPIO)][_rxSM] = nullptr;
|
||||
// If no more active, disable the IRQ
|
||||
auto pioNum = pio_get_index(_rxPIO);
|
||||
@@ -344,6 +348,11 @@ void SerialPIO::flush() {
|
||||
delay((1000 * (_txBits + 1)) / _baud);
|
||||
}
|
||||
|
||||
void SerialPIO::setInverted(bool invTx, bool invRx) {
|
||||
_txInverted = invTx;
|
||||
_rxInverted = invRx;
|
||||
}
|
||||
|
||||
size_t SerialPIO::write(uint8_t c) {
|
||||
CoreMutex m(&_mutex);
|
||||
if (!_running || !m || (_tx == NOPIN)) {
|
||||
@@ -362,7 +371,7 @@ size_t SerialPIO::write(uint8_t c) {
|
||||
}
|
||||
val <<= 1; // Start bit = low
|
||||
|
||||
pio_sm_put_blocking(_txPIO, _txSM, val);
|
||||
pio_sm_put_blocking(_txPIO, _txSM, _txInverted ? ~val : val);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -41,6 +41,8 @@ public:
|
||||
void begin(unsigned long baud, uint16_t config) override;
|
||||
void end() override;
|
||||
|
||||
void setInverted(bool invTx = true, bool invRx = true);
|
||||
|
||||
virtual int peek() override;
|
||||
virtual int read() override;
|
||||
virtual int available() override;
|
||||
@@ -63,6 +65,8 @@ protected:
|
||||
int _stop;
|
||||
bool _overflow;
|
||||
mutex_t _mutex;
|
||||
bool _txInverted = false;
|
||||
bool _rxInverted = false;
|
||||
|
||||
PIOProgram *_txPgm;
|
||||
PIO _txPIO;
|
||||
|
||||
+75
-19
@@ -40,6 +40,10 @@ bool SerialUART::setRX(pin_size_t pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_rx == pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set Serial%d.RX while running", uart_get_index(_uart) + 1);
|
||||
} else {
|
||||
@@ -57,6 +61,10 @@ bool SerialUART::setTX(pin_size_t pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_tx == pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set Serial%d.TX while running", uart_get_index(_uart) + 1);
|
||||
} else {
|
||||
@@ -69,11 +77,15 @@ bool SerialUART::setRTS(pin_size_t pin) {
|
||||
constexpr uint32_t valid[2] = { __bitset({3, 15, 19}) /* UART0 */,
|
||||
__bitset({7, 11, 23, 27}) /* UART1 */
|
||||
};
|
||||
if ((!_running) && ((1 << pin) & valid[uart_get_index(_uart)])) {
|
||||
if ((!_running) && ((pin == UART_PIN_NOT_DEFINED) || ((1 << pin) & valid[uart_get_index(_uart)]))) {
|
||||
_rts = pin;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_rts == pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set Serial%d.RTS while running", uart_get_index(_uart) + 1);
|
||||
} else {
|
||||
@@ -86,11 +98,15 @@ bool SerialUART::setCTS(pin_size_t pin) {
|
||||
constexpr uint32_t valid[2] = { __bitset({2, 14, 18}) /* UART0 */,
|
||||
__bitset({6, 10, 22, 26}) /* UART1 */
|
||||
};
|
||||
if ((!_running) && ((1 << pin) & valid[uart_get_index(_uart)])) {
|
||||
if ((!_running) && ((pin == UART_PIN_NOT_DEFINED) || ((1 << pin) & valid[uart_get_index(_uart)]))) {
|
||||
_cts = pin;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_cts == pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set Serial%d.CTS while running", uart_get_index(_uart) + 1);
|
||||
} else {
|
||||
@@ -134,6 +150,20 @@ void SerialUART::begin(unsigned long baud, uint16_t config) {
|
||||
_overflow = false;
|
||||
_queue = new uint8_t[_fifoSize];
|
||||
_baud = baud;
|
||||
|
||||
_fcnTx = gpio_get_function(_tx);
|
||||
_fcnRx = gpio_get_function(_rx);
|
||||
gpio_set_function(_tx, GPIO_FUNC_UART);
|
||||
gpio_set_function(_rx, GPIO_FUNC_UART);
|
||||
if (_rts != UART_PIN_NOT_DEFINED) {
|
||||
_fcnRts = gpio_get_function(_rts);
|
||||
gpio_set_function(_rts, GPIO_FUNC_UART);
|
||||
}
|
||||
if (_cts != UART_PIN_NOT_DEFINED) {
|
||||
_fcnCts = gpio_get_function(_cts);
|
||||
gpio_set_function(_cts, GPIO_FUNC_UART);
|
||||
}
|
||||
|
||||
uart_init(_uart, baud);
|
||||
int bits, stop;
|
||||
uart_parity_t parity;
|
||||
@@ -171,18 +201,6 @@ void SerialUART::begin(unsigned long baud, uint16_t config) {
|
||||
break;
|
||||
}
|
||||
uart_set_format(_uart, bits, stop, parity);
|
||||
_fcnTx = gpio_get_function(_tx);
|
||||
_fcnRx = gpio_get_function(_rx);
|
||||
gpio_set_function(_tx, GPIO_FUNC_UART);
|
||||
gpio_set_function(_rx, GPIO_FUNC_UART);
|
||||
if (_rts != UART_PIN_NOT_DEFINED) {
|
||||
_fcnRts = gpio_get_function(_rts);
|
||||
gpio_set_function(_rts, GPIO_FUNC_UART);
|
||||
}
|
||||
if (_cts != UART_PIN_NOT_DEFINED) {
|
||||
_fcnCts = gpio_get_function(_cts);
|
||||
gpio_set_function(_cts, GPIO_FUNC_UART);
|
||||
}
|
||||
uart_set_hw_flow(_uart, _rts != UART_PIN_NOT_DEFINED, _cts != UART_PIN_NOT_DEFINED);
|
||||
_writer = 0;
|
||||
_reader = 0;
|
||||
@@ -200,6 +218,7 @@ void SerialUART::begin(unsigned long baud, uint16_t config) {
|
||||
} else {
|
||||
// Polling mode has no IRQs used
|
||||
}
|
||||
_break = false;
|
||||
_running = true;
|
||||
}
|
||||
|
||||
@@ -288,13 +307,22 @@ int SerialUART::read() {
|
||||
}
|
||||
|
||||
bool SerialUART::overflow() {
|
||||
CoreMutex m(&_mutex);
|
||||
if (!_running || !m) {
|
||||
if (!_running) {
|
||||
return false;
|
||||
}
|
||||
bool hold = _overflow;
|
||||
|
||||
if (_polling) {
|
||||
_handleIRQ(false);
|
||||
} else {
|
||||
_pumpFIFO();
|
||||
}
|
||||
|
||||
mutex_enter_blocking(&_fifoMutex);
|
||||
bool ovf = _overflow;
|
||||
_overflow = false;
|
||||
return hold;
|
||||
mutex_exit(&_fifoMutex);
|
||||
|
||||
return ovf;
|
||||
}
|
||||
|
||||
int SerialUART::available() {
|
||||
@@ -365,6 +393,25 @@ SerialUART::operator bool() {
|
||||
return _running;
|
||||
}
|
||||
|
||||
bool SerialUART::getBreakReceived() {
|
||||
if (!_running) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_polling) {
|
||||
_handleIRQ(false);
|
||||
} else {
|
||||
_pumpFIFO();
|
||||
}
|
||||
|
||||
mutex_enter_blocking(&_fifoMutex);
|
||||
bool break_received = _break;
|
||||
_break = false;
|
||||
mutex_exit(&_fifoMutex);
|
||||
|
||||
return break_received;
|
||||
}
|
||||
|
||||
void arduino::serialEvent1Run(void) {
|
||||
if (serialEvent1 && Serial1.available()) {
|
||||
serialEvent1();
|
||||
@@ -390,7 +437,16 @@ void __not_in_flash_func(SerialUART::_handleIRQ)(bool inIRQ) {
|
||||
// ICR is write-to-clear
|
||||
uart_get_hw(_uart)->icr = UART_UARTICR_RTIC_BITS | UART_UARTICR_RXIC_BITS;
|
||||
while (uart_is_readable(_uart)) {
|
||||
auto val = uart_getc(_uart);
|
||||
uint32_t raw = uart_get_hw(_uart)->dr;
|
||||
if (raw & 0x400) {
|
||||
// break!
|
||||
_break = true;
|
||||
continue;
|
||||
} else if (raw & 0x300) {
|
||||
// Framing, Parity Error. Ignore this bad char
|
||||
continue;
|
||||
}
|
||||
uint8_t val = raw & 0xff;
|
||||
auto next_writer = _writer + 1;
|
||||
if (next_writer == _fifoSize) {
|
||||
next_writer = 0;
|
||||
|
||||
@@ -63,9 +63,18 @@ public:
|
||||
bool overflow();
|
||||
operator bool() override;
|
||||
|
||||
// ESP8266 compat
|
||||
void setDebugOutput(bool unused) {
|
||||
(void) unused;
|
||||
}
|
||||
|
||||
// Not to be called by users, only from the IRQ handler. In public so that the C-language IQR callback can access it
|
||||
void _handleIRQ(bool inIRQ = true);
|
||||
|
||||
// Allows the user to sleep until a break is received (self-clears the flag
|
||||
// on read)
|
||||
bool getBreakReceived();
|
||||
|
||||
private:
|
||||
bool _running = false;
|
||||
uart_inst_t *_uart;
|
||||
@@ -76,6 +85,7 @@ private:
|
||||
mutex_t _mutex;
|
||||
bool _polling = false;
|
||||
bool _overflow;
|
||||
bool _break;
|
||||
|
||||
// Lockless, IRQ-handled circular queue
|
||||
uint32_t _writer;
|
||||
|
||||
+26
-10
@@ -25,14 +25,15 @@
|
||||
#include <Arduino.h>
|
||||
#include "CoreMutex.h"
|
||||
|
||||
#include "tusb.h"
|
||||
#include "pico/time.h"
|
||||
#include "pico/binary_info.h"
|
||||
#include "pico/bootrom.h"
|
||||
#include "hardware/irq.h"
|
||||
#include "pico/mutex.h"
|
||||
#include "hardware/watchdog.h"
|
||||
#include "pico/unique_id.h"
|
||||
#include <tusb.h>
|
||||
#include <pico/time.h>
|
||||
#include <pico/binary_info.h>
|
||||
#include <pico/bootrom.h>
|
||||
#include <hardware/irq.h>
|
||||
#include <pico/mutex.h>
|
||||
#include <hardware/watchdog.h>
|
||||
#include <pico/unique_id.h>
|
||||
#include <hardware/resets.h>
|
||||
|
||||
#ifndef DISABLE_USB_SERIAL
|
||||
// Ensure we are installed in the USB chain
|
||||
@@ -129,7 +130,7 @@ size_t SerialUSB::write(const uint8_t *buf, size_t length) {
|
||||
|
||||
static uint64_t last_avail_time;
|
||||
int written = 0;
|
||||
if (tud_cdc_connected()) {
|
||||
if (tud_cdc_connected() || _ignoreFlowControl) {
|
||||
for (size_t i = 0; i < length;) {
|
||||
int n = length - i;
|
||||
int avail = tud_cdc_write_available();
|
||||
@@ -170,12 +171,27 @@ SerialUSB::operator bool() {
|
||||
return tud_cdc_connected();
|
||||
}
|
||||
|
||||
void SerialUSB::ignoreFlowControl(bool ignore) {
|
||||
_ignoreFlowControl = ignore;
|
||||
}
|
||||
|
||||
static bool _dtr = false;
|
||||
static bool _rts = false;
|
||||
static int _bps = 115200;
|
||||
static bool _rebooting = false;
|
||||
static void CheckSerialReset() {
|
||||
if ((_bps == 1200) && (!_dtr)) {
|
||||
if (!_rebooting && (_bps == 1200) && (!_dtr)) {
|
||||
if (__isFreeRTOS) {
|
||||
__freertos_idle_other_core();
|
||||
}
|
||||
_rebooting = true;
|
||||
// Disable NVIC IRQ, so that we don't get bothered anymore
|
||||
irq_set_enabled(USBCTRL_IRQ, false);
|
||||
// Reset the whole USB hardware block
|
||||
reset_block(RESETS_RESET_USBCTRL_BITS);
|
||||
unreset_block(RESETS_RESET_USBCTRL_BITS);
|
||||
// Delay a bit, so the PC can figure out that we have disconnected.
|
||||
busy_wait_ms(3);
|
||||
reset_usb_boot(0, 0);
|
||||
while (1); // WDT will fire here
|
||||
}
|
||||
|
||||
@@ -44,8 +44,16 @@ public:
|
||||
using Print::write;
|
||||
operator bool() override;
|
||||
|
||||
void ignoreFlowControl(bool ignore = true);
|
||||
|
||||
// ESP8266 compat
|
||||
void setDebugOutput(bool unused) {
|
||||
(void) unused;
|
||||
}
|
||||
|
||||
private:
|
||||
bool _running = false;
|
||||
bool _ignoreFlowControl = false;
|
||||
};
|
||||
|
||||
extern SerialUSB Serial;
|
||||
|
||||
+14
-4
@@ -28,6 +28,7 @@ typedef struct {
|
||||
pin_size_t pin;
|
||||
PIO pio;
|
||||
int sm;
|
||||
int off;
|
||||
alarm_id_t alarm;
|
||||
} Tone;
|
||||
|
||||
@@ -38,10 +39,18 @@ auto_init_mutex(_toneMutex);
|
||||
static PIOProgram _tone2Pgm(&tone2_program);
|
||||
static std::map<pin_size_t, Tone *> _toneMap;
|
||||
|
||||
static inline bool pio_sm_get_enabled(PIO pio, uint sm) {
|
||||
check_pio_param(pio);
|
||||
check_sm_param(sm);
|
||||
return (pio->ctrl & ~(1u << sm)) & (1 << sm);
|
||||
}
|
||||
|
||||
int64_t _stopTonePIO(alarm_id_t id, void *user_data) {
|
||||
(void) id;
|
||||
Tone *tone = (Tone *)user_data;
|
||||
tone->alarm = 0;
|
||||
digitalWrite(tone->pin, LOW);
|
||||
pinMode(tone->pin, OUTPUT);
|
||||
pio_sm_set_enabled(tone->pio, tone->sm, false);
|
||||
return 0;
|
||||
}
|
||||
@@ -72,14 +81,12 @@ void tone(uint8_t pin, unsigned int frequency, unsigned long duration) {
|
||||
newTone = new Tone();
|
||||
newTone->pin = pin;
|
||||
pinMode(pin, OUTPUT);
|
||||
int off;
|
||||
if (!_tone2Pgm.prepare(&newTone->pio, &newTone->sm, &off)) {
|
||||
if (!_tone2Pgm.prepare(&newTone->pio, &newTone->sm, &newTone->off)) {
|
||||
DEBUGCORE("ERROR: tone unable to start, out of PIO resources\n");
|
||||
// ERROR, no free slots
|
||||
delete newTone;
|
||||
return;
|
||||
}
|
||||
tone2_program_init(newTone->pio, newTone->sm, off, pin);
|
||||
newTone->alarm = 0;
|
||||
} else {
|
||||
newTone = entry->second;
|
||||
@@ -88,6 +95,9 @@ void tone(uint8_t pin, unsigned int frequency, unsigned long duration) {
|
||||
newTone->alarm = 0;
|
||||
}
|
||||
}
|
||||
if (!pio_sm_get_enabled(newTone->pio, newTone->sm)) {
|
||||
tone2_program_init(newTone->pio, newTone->sm, newTone->off, pin);
|
||||
}
|
||||
pio_sm_clear_fifos(newTone->pio, newTone->sm); // Remove any old updates that haven't yet taken effect
|
||||
pio_sm_put_blocking(newTone->pio, newTone->sm, RP2040::usToPIOCycles(us));
|
||||
pio_sm_set_enabled(newTone->pio, newTone->sm, true);
|
||||
@@ -99,7 +109,7 @@ void tone(uint8_t pin, unsigned int frequency, unsigned long duration) {
|
||||
if (ret > 0) {
|
||||
newTone->alarm = ret;
|
||||
} else {
|
||||
DEBUGCORE("ERROR: Unable to allocate timer for tone(%d, %d, %d)\n",
|
||||
DEBUGCORE("ERROR: Unable to allocate timer for tone(%d, %d, %lu)\n",
|
||||
pin, frequency, duration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "stdlib.h"
|
||||
#include "stdint.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void randomSeed(uint32_t dwSeed) {
|
||||
if (dwSeed != 0) {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
#include "api/String.h"
|
||||
@@ -19,8 +19,9 @@
|
||||
*/
|
||||
|
||||
#include "_freertos.h"
|
||||
#include "pico/mutex.h"
|
||||
#include <pico/mutex.h>
|
||||
#include <stdlib.h>
|
||||
#include "Arduino.h"
|
||||
|
||||
typedef struct {
|
||||
mutex_t *src;
|
||||
@@ -28,7 +29,7 @@ typedef struct {
|
||||
} FMMap;
|
||||
|
||||
static FMMap *_map = nullptr;
|
||||
extern "C" SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m) {
|
||||
SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m, bool recursive) {
|
||||
if (!_map) {
|
||||
_map = (FMMap *)calloc(sizeof(FMMap), 16);
|
||||
}
|
||||
@@ -38,10 +39,20 @@ extern "C" SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m) {
|
||||
return _map[i].dst;
|
||||
}
|
||||
}
|
||||
// Make a new mutex
|
||||
auto fm = __freertos_mutex_create();
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (_map[i].src == nullptr) {
|
||||
// Make a new mutex
|
||||
SemaphoreHandle_t fm;
|
||||
if (recursive) {
|
||||
fm = _freertos_recursive_mutex_create();
|
||||
} else {
|
||||
fm = __freertos_mutex_create();
|
||||
}
|
||||
if (fm == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
_map[i].src = m;
|
||||
_map[i].dst = fm;
|
||||
return fm;
|
||||
@@ -49,3 +60,45 @@ extern "C" SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m) {
|
||||
}
|
||||
return nullptr; // Need to make space for more mutex maps!
|
||||
}
|
||||
|
||||
|
||||
// The HW Random code needs a precise sleep_until() in order to assure it
|
||||
// grabs the ROSC bit when it wants. Unfortunately, under FreeRTOS the
|
||||
// sleep_until() becomes imprecise and the "did I get the bit when I wanted"
|
||||
// check in the pico_rand code always fails and you get an infinite loop.
|
||||
|
||||
// This block wraps the 2 get_rand calls to set a flag to convert
|
||||
// sleep_until() (which can do a task swap and cause bad timing) into a
|
||||
// busy wait.
|
||||
|
||||
extern "C" {
|
||||
static bool __inRand = false;
|
||||
|
||||
extern uint64_t __real_get_rand_64();
|
||||
uint64_t __wrap_get_rand_64() {
|
||||
if (__isFreeRTOS) {
|
||||
rp2040.idleOtherCore();
|
||||
__inRand = true;
|
||||
auto r = __real_get_rand_64();
|
||||
__inRand = false;
|
||||
rp2040.resumeOtherCore();
|
||||
return r;
|
||||
} else {
|
||||
return __real_get_rand_64();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t __wrap_get_rand_32() {
|
||||
return (uint32_t) __wrap_get_rand_64();
|
||||
}
|
||||
|
||||
extern void __real_sleep_until(absolute_time_t t);
|
||||
void __wrap_sleep_until(absolute_time_t t) {
|
||||
if (__inRand) {
|
||||
busy_wait_until(t);
|
||||
} else {
|
||||
__real_sleep_until(t);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+10
-14
@@ -19,7 +19,7 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "pico/mutex.h"
|
||||
#include <pico/mutex.h>
|
||||
|
||||
// Cannot include refs to FreeRTOS's actual semaphore calls because they
|
||||
// are implemented as macros, so we have a wrapper in our variant hook
|
||||
@@ -35,30 +35,26 @@ 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
|
||||
|
||||
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 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 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));
|
||||
|
||||
#ifndef INC_FREERTOS_H
|
||||
extern void vTaskSuspendAll() __attribute__((weak));
|
||||
extern int32_t xTaskResumeAll() __attribute__((weak));
|
||||
|
||||
typedef struct tskTaskControlBlock * TaskHandle_t;
|
||||
extern void vTaskPreemptionDisable(TaskHandle_t p) __attribute__((weak));
|
||||
extern void vTaskPreemptionEnable(TaskHandle_t p) __attribute__((weak));
|
||||
#endif
|
||||
|
||||
extern SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m);
|
||||
extern void __freertos_idle_other_core() __attribute__((weak));
|
||||
extern void __freertos_resume_other_core() __attribute__((weak));
|
||||
}
|
||||
|
||||
// Halt the FreeRTOS PendSV task switching magic
|
||||
extern "C" int __holdUpPendSV;
|
||||
extern SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m, bool recursive = false);
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
// Simple helper header to ensure pico libs support ?BT
|
||||
|
||||
#ifndef ENABLE_CLASSIC
|
||||
#define ENABLE_CLASSIC 0
|
||||
#endif
|
||||
|
||||
static_assert(ENABLE_CLASSIC, "This library needs Bluetooth enabled. Use the 'Tools->IP/Bluetooth Stack' menu in the IDE to enable it.");
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
|
||||
|
||||
#include <lwip/netif.h>
|
||||
extern "C" {
|
||||
#include <cyw43.h>
|
||||
#include <cyw43_stats.h>
|
||||
}
|
||||
#include <pico/cyw43_arch.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
// From cyw43_ctrl.c
|
||||
#define WIFI_JOIN_STATE_KIND_MASK (0x000f)
|
||||
#define WIFI_JOIN_STATE_ACTIVE (0x0001)
|
||||
#define WIFI_JOIN_STATE_FAIL (0x0002)
|
||||
#define WIFI_JOIN_STATE_NONET (0x0003)
|
||||
#define WIFI_JOIN_STATE_BADAUTH (0x0004)
|
||||
#define WIFI_JOIN_STATE_AUTH (0x0200)
|
||||
#define WIFI_JOIN_STATE_LINK (0x0400)
|
||||
#define WIFI_JOIN_STATE_KEYED (0x0800)
|
||||
#define WIFI_JOIN_STATE_ALL (0x0e01)
|
||||
|
||||
// The core can't directly call a library, so put in a dummy weak one to be overridden by one in lwip_cyw43 library
|
||||
extern struct netif *__getCYW43Netif() __attribute__((weak));
|
||||
struct netif *__getCYW43Netif() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// CB from the cyw43 driver
|
||||
extern "C" void __wrap_cyw43_cb_process_ethernet(void *cb_data, int itf, size_t len, const uint8_t *buf) {
|
||||
(void) cb_data;
|
||||
(void) itf;
|
||||
struct netif *netif = __getCYW43Netif();
|
||||
if (netif && (netif->flags & NETIF_FLAG_LINK_UP)) {
|
||||
struct pbuf *p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
|
||||
if (p != nullptr) {
|
||||
pbuf_take(p, buf, len);
|
||||
if ((netif->input(p, netif) != ERR_OK)) {
|
||||
pbuf_free(p);
|
||||
}
|
||||
CYW43_STAT_INC(PACKET_IN_COUNT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void __wrap_cyw43_cb_tcpip_set_link_up(cyw43_t *self, int itf) {
|
||||
(void) self;
|
||||
(void) itf;
|
||||
struct netif *netif = __getCYW43Netif();
|
||||
if (netif) {
|
||||
netif_set_link_up(netif);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void __wrap_cyw43_cb_tcpip_set_link_down(cyw43_t *self, int itf) {
|
||||
(void) self;
|
||||
(void) itf;
|
||||
struct netif *netif = __getCYW43Netif();
|
||||
if (netif) {
|
||||
netif_set_link_down(netif);
|
||||
}
|
||||
self->wifi_join_state &= ~WIFI_JOIN_STATE_ACTIVE;
|
||||
}
|
||||
|
||||
extern "C" int __wrap_cyw43_tcpip_link_status(cyw43_t *self, int itf) {
|
||||
struct netif *netif = __getCYW43Netif();
|
||||
//if ((CYW43::_netif->flags & (NETIF_FLAG_UP | NETIF_FLAG_LINK_UP)) == (NETIF_FLAG_UP | NETIF_FLAG_LINK_UP))
|
||||
// Fake this since it's only used in the SDK
|
||||
if (netif && ((netif->flags & (NETIF_FLAG_LINK_UP)) == (NETIF_FLAG_LINK_UP))) {
|
||||
return CYW43_LINK_UP;
|
||||
} else {
|
||||
return cyw43_wifi_link_status(self, itf);
|
||||
}
|
||||
}
|
||||
|
||||
// CBs from the SDK, not needed here as we do TCP later in the game
|
||||
extern "C" void __wrap_cyw43_cb_tcpip_init(cyw43_t *self, int itf) {
|
||||
(void) self;
|
||||
(void) itf;
|
||||
}
|
||||
extern "C" void __wrap_cyw43_cb_tcpip_deinit(cyw43_t *self, int itf) {
|
||||
(void) self;
|
||||
(void) itf;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -18,6 +18,8 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(DEBUG_RP2040_PORT)
|
||||
#define DEBUGV(...) do { } while(0)
|
||||
#define DEBUGCORE(...) do { } while(0)
|
||||
@@ -44,3 +46,7 @@
|
||||
#define DEBUGSPI(...) do { } while(0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern void hexdump(const void* mem, uint32_t len, uint8_t cols = 16);
|
||||
#endif
|
||||
|
||||
+16
-4
@@ -80,7 +80,7 @@ static SemaphoreHandle_t __getFreeRTOSMutex(_LOCK_T lock) {
|
||||
} else if (l == &__lock___arc4random_mutex) {
|
||||
return __lock___arc4random_mutex_freertos;
|
||||
}
|
||||
return nullptr;
|
||||
return __get_freertos_mutex_for_ptr(l, false);
|
||||
}
|
||||
|
||||
static SemaphoreHandle_t __getFreeRTOSRecursiveMutex(_LOCK_T lock) {
|
||||
@@ -96,12 +96,18 @@ static SemaphoreHandle_t __getFreeRTOSRecursiveMutex(_LOCK_T lock) {
|
||||
} else if (l == &__lock___env_recursive_mutex) {
|
||||
return __lock___env_recursive_mutex_freertos;
|
||||
}
|
||||
return nullptr;
|
||||
return __get_freertos_mutex_for_ptr((mutex_t *)l, true);
|
||||
}
|
||||
|
||||
void __retarget_lock_init(_LOCK_T *lock) {
|
||||
if (__freeRTOSinitted) {
|
||||
/* Already done in initFreeRTOSMutexes() */
|
||||
mutex_t *l = (mutex_t *)lock;
|
||||
if ((l == &__lock___at_quick_exit_mutex) || (l == &__lock___tz_mutex) || (l == &__lock___dd_hash_mutex) || (l == &__lock___arc4random_mutex)) {
|
||||
/* Already done in initFreeRTOSMutexes() */
|
||||
} else {
|
||||
// Will init the mutex as well
|
||||
__get_freertos_mutex_for_ptr(l, false);
|
||||
}
|
||||
} else {
|
||||
mutex_init((mutex_t*) lock);
|
||||
}
|
||||
@@ -109,7 +115,13 @@ void __retarget_lock_init(_LOCK_T *lock) {
|
||||
|
||||
void __retarget_lock_init_recursive(_LOCK_T *lock) {
|
||||
if (__freeRTOSinitted) {
|
||||
/* Already done in initFreeRTOSMutexes() */
|
||||
recursive_mutex_t *l = (recursive_mutex_t *)lock;
|
||||
if ((l == &__lock___sinit_recursive_mutex) || (l == &__lock___sfp_recursive_mutex) || (l == &__lock___atexit_recursive_mutex) || (l == &__lock___malloc_recursive_mutex) || (l == &__lock___env_recursive_mutex)) {
|
||||
/* Already done in initFreeRTOSMutexes() */
|
||||
} else {
|
||||
// Will init the mutex as well
|
||||
__get_freertos_mutex_for_ptr((mutex_t *)l, true);
|
||||
}
|
||||
} else {
|
||||
recursive_mutex_init((recursive_mutex_t*) lock);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,307 @@
|
||||
/*
|
||||
LWIP wrappers to protect against timer-based re-entrancy
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
#include <pico/mutex.h>
|
||||
#include <lwip/pbuf.h>
|
||||
#include <lwip/udp.h>
|
||||
#include <lwip/tcp.h>
|
||||
#include <lwip/dns.h>
|
||||
#include <lwip/raw.h>
|
||||
#include <lwip/timeouts.h>
|
||||
#include <pico/cyw43_arch.h>
|
||||
|
||||
class LWIPMutex {
|
||||
public:
|
||||
LWIPMutex() {
|
||||
cyw43_arch_lwip_begin();
|
||||
}
|
||||
|
||||
~LWIPMutex() {
|
||||
cyw43_arch_lwip_end();
|
||||
}
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
extern u8_t __real_pbuf_header(struct pbuf *p, s16_t header_size);
|
||||
u8_t __wrap_pbuf_header(struct pbuf *p, s16_t header_size) {
|
||||
LWIPMutex m;
|
||||
return __real_pbuf_header(p, header_size);
|
||||
}
|
||||
|
||||
extern u8_t __real_pbuf_free(struct pbuf *p);
|
||||
u8_t __wrap_pbuf_free(struct pbuf *p) {
|
||||
LWIPMutex m;
|
||||
return __real_pbuf_free(p);
|
||||
}
|
||||
|
||||
extern struct pbuf *__real_pbuf_alloc(pbuf_layer l, u16_t length, pbuf_type type);
|
||||
struct pbuf *__wrap_pbuf_alloc(pbuf_layer l, u16_t length, pbuf_type type) {
|
||||
LWIPMutex m;
|
||||
return __real_pbuf_alloc(l, length, type);
|
||||
}
|
||||
|
||||
extern err_t __real_pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len);
|
||||
err_t __wrap_pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len) {
|
||||
LWIPMutex m;
|
||||
return __real_pbuf_take(buf, dataptr, len);
|
||||
}
|
||||
|
||||
extern u16_t __real_pbuf_copy_partial(const struct pbuf *p, void *dataptr, u16_t len, u16_t offset);
|
||||
u16_t __wrap_pbuf_copy_partial(const struct pbuf *p, void *dataptr, u16_t len, u16_t offset) {
|
||||
LWIPMutex m;
|
||||
return __real_pbuf_copy_partial(p, dataptr, len, offset);
|
||||
}
|
||||
|
||||
extern void __real_pbuf_ref(struct pbuf *p);
|
||||
void __wrap_pbuf_ref(struct pbuf *p) {
|
||||
LWIPMutex m;
|
||||
__real_pbuf_ref(p);
|
||||
}
|
||||
|
||||
extern u8_t __real_pbuf_get_at(const struct pbuf* p, u16_t offset);
|
||||
u8_t __wrap_pbuf_get_at(const struct pbuf* p, u16_t offset) {
|
||||
LWIPMutex m;
|
||||
return __real_pbuf_get_at(p, offset);
|
||||
}
|
||||
|
||||
extern void *__real_pbuf_get_contiguous(const struct pbuf *p, void *buffer, size_t bufsize, u16_t len, u16_t offset);
|
||||
void *__wrap_pbuf_get_contiguous(const struct pbuf *p, void *buffer, size_t bufsize, u16_t len, u16_t offset) {
|
||||
LWIPMutex m;
|
||||
return __real_pbuf_get_contiguous(p, buffer, bufsize, len, offset);
|
||||
}
|
||||
|
||||
extern void __real_pbuf_cat(struct pbuf *head, struct pbuf *tail);
|
||||
void __wrap_pbuf_cat(struct pbuf *head, struct pbuf *tail) {
|
||||
LWIPMutex m;
|
||||
__real_pbuf_cat(head, tail);
|
||||
}
|
||||
|
||||
extern void __real_tcp_arg(struct tcp_pcb *pcb, void *arg);
|
||||
void __wrap_tcp_arg(struct tcp_pcb *pcb, void *arg) {
|
||||
LWIPMutex m;
|
||||
__real_tcp_arg(pcb, arg);
|
||||
}
|
||||
|
||||
extern struct tcp_pcb *__real_tcp_new(void);
|
||||
struct tcp_pcb *__wrap_tcp_new(void) {
|
||||
LWIPMutex m;
|
||||
return __real_tcp_new();
|
||||
}
|
||||
|
||||
extern err_t __real_tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port);
|
||||
err_t __wrap_tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) {
|
||||
LWIPMutex m;
|
||||
return __real_tcp_bind(pcb, ipaddr, port);
|
||||
}
|
||||
|
||||
extern struct tcp_pcb *__real_tcp_listen(struct tcp_pcb *pcb);
|
||||
struct tcp_pcb *__wrap_tcp_listen(struct tcp_pcb *pcb) {
|
||||
LWIPMutex m;
|
||||
return __real_tcp_listen(pcb);
|
||||
}
|
||||
|
||||
extern struct tcp_pcb *__real_tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog);
|
||||
struct tcp_pcb *__wrap_tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog) {
|
||||
LWIPMutex m;
|
||||
return __real_tcp_listen_with_backlog(pcb, backlog);
|
||||
}
|
||||
|
||||
extern void __real_tcp_accept(struct tcp_pcb *pcb, err_t (* accept)(void *arg, struct tcp_pcb *newpcb, err_t err));
|
||||
void __wrap_tcp_accept(struct tcp_pcb *pcb, err_t (* accept)(void *arg, struct tcp_pcb *newpcb, err_t err)) {
|
||||
LWIPMutex m;
|
||||
__real_tcp_accept(pcb, accept);
|
||||
}
|
||||
|
||||
extern err_t __real_tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port, err_t (* connected)(void *arg, struct tcp_pcb *tpcb, err_t err));
|
||||
err_t __wrap_tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port, err_t (* connected)(void *arg, struct tcp_pcb *tpcb, err_t err)) {
|
||||
LWIPMutex m;
|
||||
return __real_tcp_connect(pcb, ipaddr, port, connected);
|
||||
}
|
||||
|
||||
extern err_t __real_tcp_write(struct tcp_pcb *pcb, const void *dataptr, u16_t len, u8_t apiflags);
|
||||
err_t __wrap_tcp_write(struct tcp_pcb *pcb, const void *dataptr, u16_t len, u8_t apiflags) {
|
||||
LWIPMutex m;
|
||||
return __real_tcp_write(pcb, dataptr, len, apiflags);
|
||||
}
|
||||
|
||||
extern void __real_tcp_sent(struct tcp_pcb *pcb, err_t (* sent)(void *arg, struct tcp_pcb *tpcb, u16_t len));
|
||||
void __wrap_tcp_sent(struct tcp_pcb *pcb, err_t (* sent)(void *arg, struct tcp_pcb *tpcb, u16_t len)) {
|
||||
LWIPMutex m;
|
||||
__real_tcp_sent(pcb, sent);
|
||||
}
|
||||
|
||||
extern void __real_tcp_recv(struct tcp_pcb *pcb, err_t (* recv)(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err));
|
||||
void __wrap_tcp_recv(struct tcp_pcb *pcb, err_t (* recv)(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)) {
|
||||
LWIPMutex m;
|
||||
__real_tcp_recv(pcb, recv);
|
||||
}
|
||||
|
||||
extern void __real_tcp_recved(struct tcp_pcb *pcb, u16_t len);
|
||||
void __wrap_tcp_recved(struct tcp_pcb *pcb, u16_t len) {
|
||||
LWIPMutex m;
|
||||
__real_tcp_recved(pcb, len);
|
||||
}
|
||||
|
||||
extern void __real_tcp_poll(struct tcp_pcb *pcb, err_t (* poll)(void *arg, struct tcp_pcb *tpcb), u8_t interval);
|
||||
void __wrap_tcp_poll(struct tcp_pcb *pcb, err_t (* poll)(void *arg, struct tcp_pcb *tpcb), u8_t interval) {
|
||||
LWIPMutex m;
|
||||
__real_tcp_poll(pcb, poll, interval);
|
||||
}
|
||||
|
||||
extern err_t __real_tcp_close(struct tcp_pcb *pcb);
|
||||
err_t __wrap_tcp_close(struct tcp_pcb *pcb) {
|
||||
LWIPMutex m;
|
||||
return __real_tcp_close(pcb);
|
||||
}
|
||||
|
||||
extern void __real_tcp_abort(struct tcp_pcb *pcb);
|
||||
void __wrap_tcp_abort(struct tcp_pcb *pcb) {
|
||||
LWIPMutex m;
|
||||
__real_tcp_abort(pcb);
|
||||
}
|
||||
|
||||
extern void __real_tcp_err(struct tcp_pcb *pcb, void (* err)(void *arg, err_t err));
|
||||
void __wrap_tcp_err(struct tcp_pcb *pcb, void (* err)(void *arg, err_t err)) {
|
||||
LWIPMutex m;
|
||||
__real_tcp_err(pcb, err);
|
||||
}
|
||||
|
||||
extern err_t __real_tcp_output(struct tcp_pcb *pcb);
|
||||
err_t __wrap_tcp_output(struct tcp_pcb *pcb) {
|
||||
LWIPMutex m;
|
||||
return __real_tcp_output(pcb);
|
||||
}
|
||||
|
||||
extern void __real_tcp_setprio(struct tcp_pcb *pcb, u8_t prio);
|
||||
void __wrap_tcp_setprio(struct tcp_pcb *pcb, u8_t prio) {
|
||||
LWIPMutex m;
|
||||
return __real_tcp_setprio(pcb, prio);
|
||||
}
|
||||
|
||||
extern void __real_tcp_backlog_delayed(struct tcp_pcb* pcb);
|
||||
void __wrap_tcp_backlog_delayed(struct tcp_pcb* pcb) {
|
||||
LWIPMutex m;
|
||||
return __real_tcp_backlog_delayed(pcb);
|
||||
}
|
||||
|
||||
extern void __real_tcp_backlog_accepted(struct tcp_pcb* pcb);
|
||||
void __wrap_tcp_backlog_accepted(struct tcp_pcb* pcb) {
|
||||
LWIPMutex m;
|
||||
return __real_tcp_backlog_accepted(pcb);
|
||||
}
|
||||
extern struct udp_pcb *__real_udp_new(void);
|
||||
struct udp_pcb *__wrap_udp_new(void) {
|
||||
LWIPMutex m;
|
||||
return __real_udp_new();
|
||||
}
|
||||
|
||||
extern void __real_udp_remove(struct udp_pcb *pcb);
|
||||
void __wrap_udp_remove(struct udp_pcb *pcb) {
|
||||
LWIPMutex m;
|
||||
__real_udp_remove(pcb);
|
||||
}
|
||||
|
||||
extern err_t __real_udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port);
|
||||
err_t __wrap_udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) {
|
||||
LWIPMutex m;
|
||||
return __real_udp_bind(pcb, ipaddr, port);
|
||||
}
|
||||
|
||||
extern err_t __real_udp_connect(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port);
|
||||
err_t __wrap_udp_connect(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) {
|
||||
LWIPMutex m;
|
||||
return __real_udp_connect(pcb, ipaddr, port);
|
||||
}
|
||||
|
||||
extern err_t __real_udp_disconnect(struct udp_pcb *pcb);
|
||||
err_t __wrap_udp_disconnect(struct udp_pcb *pcb) {
|
||||
LWIPMutex m;
|
||||
return __real_udp_disconnect(pcb);
|
||||
}
|
||||
|
||||
extern err_t __real_udp_send(struct udp_pcb *pcb, struct pbuf *p);
|
||||
err_t __wrap_udp_send(struct udp_pcb *pcb, struct pbuf *p) {
|
||||
LWIPMutex m;
|
||||
return __real_udp_send(pcb, p);
|
||||
}
|
||||
|
||||
extern void __real_udp_recv(struct udp_pcb *pcb, void (* recv)(void *arg, struct udp_pcb *upcb, struct pbuf *p, ip_addr_t *addr, u16_t port), void *recv_arg);
|
||||
void __wrap_udp_recv(struct udp_pcb *pcb, void (* recv)(void *arg, struct udp_pcb *upcb, struct pbuf *p, ip_addr_t *addr, u16_t port), void *recv_arg) {
|
||||
LWIPMutex m;
|
||||
__real_udp_recv(pcb, recv, recv_arg);
|
||||
}
|
||||
|
||||
extern err_t __real_udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif);
|
||||
err_t __wrap_udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif) {
|
||||
LWIPMutex m;
|
||||
return __real_udp_sendto_if(pcb, p, dst_ip, dst_port, netif);
|
||||
}
|
||||
|
||||
// sys_check_timeouts is special case because the async process will call it. If we're already in a timeout check, just do a noop
|
||||
extern void __real_sys_check_timeouts();
|
||||
void __wrap_sys_check_timeouts(void) {
|
||||
LWIPMutex m;
|
||||
__real_sys_check_timeouts();
|
||||
}
|
||||
|
||||
extern err_t __real_dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found, void *callback_arg);
|
||||
err_t __wrap_dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found, void *callback_arg) {
|
||||
LWIPMutex m;
|
||||
return __real_dns_gethostbyname(hostname, addr, found, callback_arg);
|
||||
}
|
||||
|
||||
extern err_t __real_dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, dns_found_callback found, void *callback_arg, u8_t dns_addrtype);
|
||||
err_t __wrap_dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, dns_found_callback found, void *callback_arg, u8_t dns_addrtype) {
|
||||
LWIPMutex m;
|
||||
return __real_dns_gethostbyname_addrtype(hostname, addr, found, callback_arg, dns_addrtype);
|
||||
}
|
||||
|
||||
extern struct raw_pcb *__real_raw_new(u8_t proto);
|
||||
struct raw_pcb *__wrap_raw_new(u8_t proto) {
|
||||
LWIPMutex m;
|
||||
return __real_raw_new(proto);
|
||||
}
|
||||
|
||||
extern void __real_raw_recv(struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg);
|
||||
void __wrap_raw_recv(struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg) {
|
||||
LWIPMutex m;
|
||||
__real_raw_recv(pcb, recv, recv_arg);
|
||||
}
|
||||
|
||||
extern err_t __real_raw_bind(struct raw_pcb *pcb, const ip_addr_t *ipaddr);
|
||||
err_t __wrap_raw_bind(struct raw_pcb *pcb, const ip_addr_t *ipaddr) {
|
||||
LWIPMutex m;
|
||||
return __real_raw_bind(pcb, ipaddr);
|
||||
}
|
||||
|
||||
extern err_t __real_raw_sendto(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *ipaddr);
|
||||
err_t __wrap_raw_sendto(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *ipaddr) {
|
||||
LWIPMutex m;
|
||||
return __real_raw_sendto(pcb, p, ipaddr);
|
||||
}
|
||||
|
||||
extern void __real_raw_remove(struct raw_pcb *pcb);
|
||||
void __wrap_raw_remove(struct raw_pcb *pcb) {
|
||||
LWIPMutex m;
|
||||
__real_raw_remove(pcb);
|
||||
}
|
||||
|
||||
}; // extern "C"
|
||||
+28
-10
@@ -22,19 +22,13 @@
|
||||
#include "RP2040USB.h"
|
||||
#include <pico/stdlib.h>
|
||||
#include <pico/multicore.h>
|
||||
#include "LWIPMutex.h"
|
||||
#include <reent.h>
|
||||
|
||||
RP2040 rp2040;
|
||||
extern "C" {
|
||||
volatile bool __otherCoreIdled = false;
|
||||
int __holdUpPendSV = 0;
|
||||
};
|
||||
|
||||
mutex_t _pioMutex;
|
||||
|
||||
int LWIPMutex::_ref = 0;
|
||||
|
||||
extern void setup();
|
||||
extern void loop();
|
||||
|
||||
@@ -95,8 +89,6 @@ extern "C" int main() {
|
||||
_REENT_INIT_PTR(_impure_ptr1);
|
||||
}
|
||||
|
||||
mutex_init(&_pioMutex);
|
||||
|
||||
rp2040.begin();
|
||||
|
||||
initVariant();
|
||||
@@ -128,7 +120,6 @@ extern "C" int main() {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef NO_USB
|
||||
if (!__isFreeRTOS) {
|
||||
if (setup1 || loop1) {
|
||||
rp2040.fifo.begin(2);
|
||||
@@ -137,7 +128,6 @@ extern "C" int main() {
|
||||
}
|
||||
rp2040.fifo.registerCore();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!__isFreeRTOS) {
|
||||
if (setup1 || loop1) {
|
||||
@@ -168,6 +158,7 @@ extern "C" void __register_impure_ptr(struct _reent *p) {
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" struct _reent *__wrap___getreent() __attribute__((weak));
|
||||
extern "C" struct _reent *__wrap___getreent() {
|
||||
if (get_core_num() == 0) {
|
||||
return _impure_ptr;
|
||||
@@ -175,3 +166,30 @@ extern "C" struct _reent *__wrap___getreent() {
|
||||
return _impure_ptr1;
|
||||
}
|
||||
}
|
||||
|
||||
// ESP8266 internal debug routine
|
||||
extern void hexdump(const void* mem, uint32_t len, uint8_t cols) __attribute__((weak));
|
||||
void hexdump(const void* mem, uint32_t len, uint8_t cols) {
|
||||
const char* src = (const char*)mem;
|
||||
printf("\n[HEXDUMP] Address: %p len: 0x%lX (%ld)", src, len, len);
|
||||
while (len > 0) {
|
||||
uint32_t linesize = cols > len ? len : cols;
|
||||
printf("\n[%p] 0x%04x: ", src, (int)(src - (const char*)mem));
|
||||
for (uint32_t i = 0; i < linesize; i++) {
|
||||
printf("%02x ", *(src + i));
|
||||
}
|
||||
printf(" ");
|
||||
for (uint32_t i = linesize; i < cols; i++) {
|
||||
printf(" ");
|
||||
}
|
||||
for (uint32_t i = 0; i < linesize; i++) {
|
||||
unsigned char c = *(src + i);
|
||||
putc(isprint(c) ? c : '.', stdout);
|
||||
}
|
||||
src += linesize;
|
||||
len -= linesize;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
const String emptyString = "";
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
Malloc/etc. interrupt locking wrappers
|
||||
|
||||
Copyright (c) 2022 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>
|
||||
|
||||
extern "C" void *__real_malloc(size_t size);
|
||||
extern "C" void *__real_calloc(size_t count, size_t size);
|
||||
extern "C" void *__real_realloc(void *mem, size_t size);
|
||||
extern "C" void __real_free(void *mem);
|
||||
|
||||
extern "C" void *__wrap_malloc(size_t size) {
|
||||
noInterrupts();
|
||||
void *rc = __real_malloc(size);
|
||||
interrupts();
|
||||
return rc;
|
||||
}
|
||||
|
||||
extern "C" void *__wrap_calloc(size_t count, size_t size) {
|
||||
noInterrupts();
|
||||
void *rc = __real_calloc(count, size);
|
||||
interrupts();
|
||||
return rc;
|
||||
}
|
||||
|
||||
extern "C" void *__wrap_realloc(void *mem, size_t size) {
|
||||
noInterrupts();
|
||||
void *rc = __real_realloc(mem, size);
|
||||
interrupts();
|
||||
return rc;
|
||||
}
|
||||
|
||||
extern "C" void __wrap_free(void *mem) {
|
||||
noInterrupts();
|
||||
__real_free(mem);
|
||||
interrupts();
|
||||
}
|
||||
@@ -29,7 +29,7 @@
|
||||
; We shift out the start and stop bit as part of the FIFO
|
||||
set x, 9
|
||||
|
||||
pull side 1 ; Force stop bit
|
||||
pull side 1 ; Force stop bit high
|
||||
|
||||
; Send the bits
|
||||
bitloop:
|
||||
@@ -41,6 +41,26 @@ wait_bit:
|
||||
|
||||
|
||||
|
||||
; inverted-logic version (inverts the stop bit)
|
||||
|
||||
.program pio_tx_inv
|
||||
.side_set 1 opt
|
||||
|
||||
|
||||
; We shift out the start and stop bit as part of the FIFO
|
||||
set x, 9
|
||||
|
||||
pull side 0 ; Force stop bit low
|
||||
|
||||
; Send the bits
|
||||
bitloop:
|
||||
out pins, 1
|
||||
mov y, isr ; ISR is loaded by the setup routine with the period-1 count
|
||||
wait_bit:
|
||||
jmp y-- wait_bit
|
||||
jmp x-- bitloop
|
||||
|
||||
|
||||
% c-sdk {
|
||||
|
||||
static inline void pio_tx_program_init(PIO pio, uint sm, uint offset, uint pin_tx) {
|
||||
@@ -68,7 +88,7 @@ static inline void pio_tx_program_init(PIO pio, uint sm, uint offset, uint pin_t
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
|
||||
|
||||
.program pio_rx
|
||||
|
||||
@@ -91,6 +111,27 @@ wait_half:
|
||||
push ; Stuff it and wait for next start
|
||||
|
||||
|
||||
.program pio_rx_inv
|
||||
|
||||
; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX.
|
||||
|
||||
start:
|
||||
set x, 18 ; Preload bit counter...we'll shift in the start bit and stop bit, and each bit will be double-recorded (to be fixed by RP2040 code)
|
||||
wait 1 pin 0 ; Stall until start bit is asserted
|
||||
|
||||
bitloop:
|
||||
; Delay until 1/2 way into the bit time
|
||||
mov y, osr
|
||||
wait_half:
|
||||
jmp y-- wait_half
|
||||
|
||||
; Read in the bit
|
||||
in pins, 1 ; Shift data bit into ISR
|
||||
jmp x-- bitloop ; Loop all bits
|
||||
|
||||
push ; Stuff it and wait for next start
|
||||
|
||||
|
||||
% c-sdk {
|
||||
static inline void pio_rx_program_init(PIO pio, uint sm, uint offset, uint pin) {
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false);
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// This file is autogenerated by pioasm; do not edit! //
|
||||
// -------------------------------------------------- //
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
#include "hardware/pio.h"
|
||||
#endif
|
||||
@@ -37,6 +39,39 @@ static inline pio_sm_config pio_tx_program_get_default_config(uint offset) {
|
||||
sm_config_set_sideset(&c, 2, true, false);
|
||||
return c;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ---------- //
|
||||
// pio_tx_inv //
|
||||
// ---------- //
|
||||
|
||||
#define pio_tx_inv_wrap_target 0
|
||||
#define pio_tx_inv_wrap 5
|
||||
|
||||
static const uint16_t pio_tx_inv_program_instructions[] = {
|
||||
// .wrap_target
|
||||
0xe029, // 0: set x, 9
|
||||
0x90a0, // 1: pull block side 0
|
||||
0x6001, // 2: out pins, 1
|
||||
0xa046, // 3: mov y, isr
|
||||
0x0084, // 4: jmp y--, 4
|
||||
0x0042, // 5: jmp x--, 2
|
||||
// .wrap
|
||||
};
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
static const struct pio_program pio_tx_inv_program = {
|
||||
.instructions = pio_tx_inv_program_instructions,
|
||||
.length = 6,
|
||||
.origin = -1,
|
||||
};
|
||||
|
||||
static inline pio_sm_config pio_tx_inv_program_get_default_config(uint offset) {
|
||||
pio_sm_config c = pio_get_default_sm_config();
|
||||
sm_config_set_wrap(&c, offset + pio_tx_inv_wrap_target, offset + pio_tx_inv_wrap);
|
||||
sm_config_set_sideset(&c, 2, true, false);
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline void pio_tx_program_init(PIO pio, uint sm, uint offset, uint pin_tx) {
|
||||
// Tell PIO to initially drive output-high on the selected pin, then map PIO
|
||||
@@ -90,6 +125,39 @@ static inline pio_sm_config pio_rx_program_get_default_config(uint offset) {
|
||||
sm_config_set_wrap(&c, offset + pio_rx_wrap_target, offset + pio_rx_wrap);
|
||||
return c;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ---------- //
|
||||
// pio_rx_inv //
|
||||
// ---------- //
|
||||
|
||||
#define pio_rx_inv_wrap_target 0
|
||||
#define pio_rx_inv_wrap 6
|
||||
|
||||
static const uint16_t pio_rx_inv_program_instructions[] = {
|
||||
// .wrap_target
|
||||
0xe032, // 0: set x, 18
|
||||
0x20a0, // 1: wait 1 pin, 0
|
||||
0xa047, // 2: mov y, osr
|
||||
0x0083, // 3: jmp y--, 3
|
||||
0x4001, // 4: in pins, 1
|
||||
0x0042, // 5: jmp x--, 2
|
||||
0x8020, // 6: push block
|
||||
// .wrap
|
||||
};
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
static const struct pio_program pio_rx_inv_program = {
|
||||
.instructions = pio_rx_inv_program_instructions,
|
||||
.length = 7,
|
||||
.origin = -1,
|
||||
};
|
||||
|
||||
static inline pio_sm_config pio_rx_inv_program_get_default_config(uint offset) {
|
||||
pio_sm_config c = pio_get_default_sm_config();
|
||||
sm_config_set_wrap(&c, offset + pio_rx_inv_wrap_target, offset + pio_rx_inv_wrap);
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline void pio_rx_program_init(PIO pio, uint sm, uint offset, uint pin) {
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false);
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
|
||||
#include <btstack.h>
|
||||
#include <pico/btstack_flash_bank.h>
|
||||
#include <hardware/flash.h>
|
||||
#include <hardware/sync.h>
|
||||
#include <string.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
const uint8_t __bluetooth_tlv[8192] __attribute__((aligned(4096))) = { 0 };
|
||||
extern const uint8_t __flash_binary_start;
|
||||
|
||||
#undef PICO_FLASH_BANK_TOTAL_SIZE
|
||||
#undef PICO_FLASH_BANK_STORAGE_OFFSET
|
||||
#define PICO_FLASH_BANK_TOTAL_SIZE sizeof(__bluetooth_tlv)
|
||||
#define PICO_FLASH_BANK_STORAGE_OFFSET ((unsigned int)(__bluetooth_tlv - &__flash_binary_start))
|
||||
|
||||
#if 0
|
||||
// Check sizes
|
||||
static_assert(PICO_FLASH_BANK_TOTAL_SIZE % (FLASH_SECTOR_SIZE * 2) == 0, "PICO_FLASH_BANK_TOTAL_SIZE invalid");
|
||||
static_assert(PICO_FLASH_BANK_TOTAL_SIZE <= PICO_FLASH_SIZE_BYTES, "PICO_FLASH_BANK_TOTAL_SIZE too big");
|
||||
static_assert(PICO_FLASH_BANK_STORAGE_OFFSET + PICO_FLASH_BANK_TOTAL_SIZE <= PICO_FLASH_SIZE_BYTES, "PICO_FLASH_BANK_TOTAL_SIZE too big");
|
||||
#endif
|
||||
|
||||
// Size of one bank
|
||||
#define PICO_FLASH_BANK_SIZE (PICO_FLASH_BANK_TOTAL_SIZE / 2)
|
||||
|
||||
#if 0
|
||||
#define DEBUG_PRINT(format,args...) printf(format, ## args)
|
||||
#else
|
||||
#define DEBUG_PRINT(...)
|
||||
#endif
|
||||
|
||||
static uint32_t pico_flash_bank_get_size(void * context) {
|
||||
(void)(context);
|
||||
return PICO_FLASH_BANK_SIZE;
|
||||
}
|
||||
|
||||
static uint32_t pico_flash_bank_get_alignment(void * context) {
|
||||
(void)(context);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void pico_flash_bank_erase(void * context, int bank) {
|
||||
(void)(context);
|
||||
DEBUG_PRINT("erase: bank %d\n", bank);
|
||||
noInterrupts();
|
||||
rp2040.idleOtherCore();
|
||||
flash_range_erase(PICO_FLASH_BANK_STORAGE_OFFSET + (PICO_FLASH_BANK_SIZE * bank), PICO_FLASH_BANK_SIZE);
|
||||
rp2040.resumeOtherCore();
|
||||
interrupts();
|
||||
}
|
||||
|
||||
static void pico_flash_bank_read(void *context, int bank, uint32_t offset, uint8_t *buffer, uint32_t size) {
|
||||
(void)(context);
|
||||
DEBUG_PRINT("read: bank %d offset %u size %u\n", bank, offset, size);
|
||||
|
||||
assert(bank <= 1);
|
||||
if (bank > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(offset < PICO_FLASH_BANK_SIZE);
|
||||
if (offset >= PICO_FLASH_BANK_SIZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert((offset + size) <= PICO_FLASH_BANK_SIZE);
|
||||
if ((offset + size) > PICO_FLASH_BANK_SIZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Flash is xip
|
||||
memcpy(buffer, (void *)(XIP_BASE + PICO_FLASH_BANK_STORAGE_OFFSET + (PICO_FLASH_BANK_SIZE * bank) + offset), size);
|
||||
}
|
||||
|
||||
static void pico_flash_bank_write(void * context, int bank, uint32_t offset, const uint8_t *data, uint32_t size) {
|
||||
(void)(context);
|
||||
DEBUG_PRINT("write: bank %d offset %u size %u\n", bank, offset, size);
|
||||
|
||||
assert(bank <= 1);
|
||||
if (bank > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(offset < PICO_FLASH_BANK_SIZE);
|
||||
if (offset >= PICO_FLASH_BANK_SIZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert((offset + size) <= PICO_FLASH_BANK_SIZE);
|
||||
if ((offset + size) > PICO_FLASH_BANK_SIZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// calc bank start position
|
||||
const uint32_t bank_start_pos = PICO_FLASH_BANK_STORAGE_OFFSET + (PICO_FLASH_BANK_SIZE * bank);
|
||||
|
||||
// Calculate first and last page in the bank
|
||||
const uint32_t first_page = offset / FLASH_PAGE_SIZE;
|
||||
const uint32_t last_page = (offset + size + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE;
|
||||
|
||||
// Now we only care about the offset in the first page
|
||||
offset %= FLASH_PAGE_SIZE;
|
||||
|
||||
// Amount of data we've copied
|
||||
uint32_t data_pos = 0;
|
||||
uint32_t size_left = size;
|
||||
|
||||
// Write all the pages required
|
||||
for (uint32_t page = first_page; page < last_page; page++) {
|
||||
uint8_t page_data[FLASH_PAGE_SIZE];
|
||||
|
||||
assert(data_pos < size && size_left <= size);
|
||||
|
||||
// Copy data we're not going to overwrite in the first page
|
||||
if (page == first_page && offset > 0) {
|
||||
memcpy(page_data,
|
||||
(void *)(XIP_BASE + bank_start_pos + (page * FLASH_PAGE_SIZE)),
|
||||
offset);
|
||||
}
|
||||
|
||||
// Copy the data we're not going to overwrite in the last page
|
||||
if (page == last_page - 1 && (offset + size_left) < FLASH_PAGE_SIZE) {
|
||||
memcpy(page_data + offset + size_left,
|
||||
(void *)(XIP_BASE + bank_start_pos + (page * FLASH_PAGE_SIZE) + offset + size_left),
|
||||
FLASH_PAGE_SIZE - offset - size_left);
|
||||
}
|
||||
|
||||
// Now copy the new data into the page
|
||||
const uint32_t size_to_copy = MIN(size_left, FLASH_PAGE_SIZE - offset);
|
||||
memcpy(page_data + offset, data + data_pos, size_to_copy);
|
||||
|
||||
data_pos += size_to_copy;
|
||||
size_left -= size_to_copy;
|
||||
|
||||
// zero offset for the following pages
|
||||
offset = 0;
|
||||
|
||||
// Now program the entire page
|
||||
noInterrupts();
|
||||
rp2040.idleOtherCore();
|
||||
flash_range_program(bank_start_pos + (page * FLASH_PAGE_SIZE), page_data, FLASH_PAGE_SIZE);
|
||||
rp2040.resumeOtherCore();
|
||||
interrupts();
|
||||
}
|
||||
}
|
||||
|
||||
static const hal_flash_bank_t pico_flash_bank_instance_obj = {
|
||||
/* uint32_t (*get_size)(..) */ &pico_flash_bank_get_size,
|
||||
/* uint32_t (*get_alignment)(..); */ &pico_flash_bank_get_alignment,
|
||||
/* void (*erase)(..); */ &pico_flash_bank_erase,
|
||||
/* void (*read)(..); */ &pico_flash_bank_read,
|
||||
/* void (*write)(..); */ &pico_flash_bank_write,
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
const hal_flash_bank_t *pico_flash_bank_instance(void) {
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Check we're not overlapping the binary in flash
|
||||
//extern char __flash_binary_end;
|
||||
// assert((uintptr_t)&__flash_binary_end - XIP_BASE <= PICO_FLASH_BANK_STORAGE_OFFSET);
|
||||
#endif
|
||||
|
||||
return &pico_flash_bank_instance_obj;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,348 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
// Taken from the Pico-SDK v1.4.0 and hacked by EFP3 to allow overriding the LWIP setup
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "pico/cyw43_arch.h"
|
||||
#include "pico/mutex.h"
|
||||
#include "pico/sem.h"
|
||||
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/irq.h"
|
||||
|
||||
#include "cyw43_stats.h"
|
||||
|
||||
#include <lwip/init.h>
|
||||
#include "lwip/timeouts.h"
|
||||
|
||||
#ifndef CYW43_PIN_WL_HOST_WAKE
|
||||
#define CYW43_PIN_WL_HOST_WAKE 24
|
||||
#endif
|
||||
|
||||
#ifndef CYW43_PIN_WL_REG_ON
|
||||
#define CYW43_PIN_WL_REG_ON 23
|
||||
#endif
|
||||
|
||||
#ifndef CYW43_WL_GPIO_COUNT
|
||||
#define CYW43_WL_GPIO_COUNT 3
|
||||
#endif
|
||||
|
||||
#ifndef CYW43_WL_GPIO_LED_PIN
|
||||
#define CYW43_WL_GPIO_LED_PIN 0
|
||||
#endif
|
||||
|
||||
volatile bool __inLWIP = false;
|
||||
|
||||
// note same code
|
||||
#if PICO_CYW43_ARCH_THREADSAFE_BACKGROUND
|
||||
|
||||
#if PICO_CYW43_ARCH_THREADSAFE_BACKGROUND && CYW43_LWIP && !NO_SYS
|
||||
#error PICO_CYW43_ARCH_THREADSAFE_BACKGROUND requires lwIP NO_SYS=1
|
||||
#endif
|
||||
#if PICO_CYW43_ARCH_THREADSAFE_BACKGROUND && CYW43_LWIP && MEM_LIBC_MALLOC
|
||||
#error MEM_LIBC_MALLOC is incompatible with PICO_CYW43_ARCH_THREADSAFE_BACKGROUND
|
||||
#endif
|
||||
// todo right now we are now always doing a cyw43_dispatch along with a lwip one when hopping cores in low_prio_irq_schedule_dispatch
|
||||
|
||||
#ifndef CYW43_SLEEP_CHECK_MS
|
||||
#define CYW43_SLEEP_CHECK_MS 50 // How often to run lwip callback
|
||||
#endif
|
||||
static alarm_id_t periodic_alarm = -1;
|
||||
|
||||
static inline uint recursive_mutex_enter_count(recursive_mutex_t *mutex) {
|
||||
return mutex->enter_count;
|
||||
}
|
||||
|
||||
static inline lock_owner_id_t recursive_mutex_owner(recursive_mutex_t *mutex) {
|
||||
return mutex->owner;
|
||||
}
|
||||
|
||||
#define CYW43_GPIO_IRQ_HANDLER_PRIORITY 0x40
|
||||
|
||||
enum {
|
||||
CYW43_DISPATCH_SLOT_CYW43 = 0,
|
||||
CYW43_DISPATCH_SLOT_ADAPTER,
|
||||
CYW43_DISPATCH_SLOT_ENUM_COUNT
|
||||
};
|
||||
#ifndef CYW43_DISPATCH_SLOT_COUNT
|
||||
#define CYW43_DISPATCH_SLOT_COUNT CYW43_DISPATCH_SLOT_ENUM_COUNT
|
||||
#endif
|
||||
|
||||
typedef void (*low_prio_irq_dispatch_t)(void);
|
||||
static void low_prio_irq_schedule_dispatch(size_t slot, low_prio_irq_dispatch_t f);
|
||||
|
||||
static uint8_t cyw43_core_num;
|
||||
#ifndef NDEBUG
|
||||
static bool in_low_priority_irq;
|
||||
#endif
|
||||
static uint8_t low_priority_irq_num;
|
||||
static bool low_priority_irq_missed;
|
||||
static low_prio_irq_dispatch_t low_priority_irq_dispatch_slots[CYW43_DISPATCH_SLOT_COUNT];
|
||||
static recursive_mutex_t cyw43_mutex;
|
||||
semaphore_t cyw43_irq_sem;
|
||||
|
||||
// Called in low priority pendsv interrupt only to do lwip processing and check cyw43 sleep
|
||||
static void periodic_worker(void) {
|
||||
#if CYW43_USE_STATS && LWIP_SYS_CHECK_MS
|
||||
static uint32_t counter;
|
||||
if (counter++ % (30000 / LWIP_SYS_CHECK_MS) == 0) {
|
||||
cyw43_dump_stats();
|
||||
}
|
||||
#endif
|
||||
|
||||
CYW43_STAT_INC(LWIP_RUN_COUNT);
|
||||
//#if CYW43_LWIP
|
||||
if (!__inLWIP) {
|
||||
sys_check_timeouts();
|
||||
}
|
||||
//#endif
|
||||
if (cyw43_poll) {
|
||||
if (cyw43_sleep > 0) {
|
||||
if (--cyw43_sleep == 0) {
|
||||
low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_CYW43, cyw43_poll);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Regular callback to get lwip to check for timeouts
|
||||
static int64_t periodic_alarm_handler(__unused alarm_id_t id, __unused void *user_data) {
|
||||
// Do lwip processing in low priority pendsv interrupt
|
||||
low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_ADAPTER, periodic_worker);
|
||||
return CYW43_SLEEP_CHECK_MS * 1000;
|
||||
}
|
||||
|
||||
void cyw43_await_background_or_timeout_us(uint32_t timeout_us) {
|
||||
// if we are called from within an IRQ, then don't wait (we are only ever called in a polling loop)
|
||||
if (!__get_current_exception()) {
|
||||
sem_acquire_timeout_us(&cyw43_irq_sem, timeout_us);
|
||||
}
|
||||
}
|
||||
|
||||
// GPIO interrupt handler to tell us there's cyw43 has work to do
|
||||
static void gpio_irq_handler(void) {
|
||||
uint32_t events = gpio_get_irq_event_mask(CYW43_PIN_WL_HOST_WAKE);
|
||||
if (events & GPIO_IRQ_LEVEL_HIGH) {
|
||||
// As we use a high level interrupt, it will go off forever until it's serviced
|
||||
// So disable the interrupt until this is done. It's re-enabled again by CYW43_POST_POLL_HOOK
|
||||
// which is called at the end of cyw43_poll_func
|
||||
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, false);
|
||||
// also clear the force bit which we use to progratically cause this handler to fire (on the right core)
|
||||
io_irq_ctrl_hw_t *irq_ctrl_base = get_core_num() ?
|
||||
&iobank0_hw->proc1_irq_ctrl : &iobank0_hw->proc0_irq_ctrl;
|
||||
hw_clear_bits(&irq_ctrl_base->intf[CYW43_PIN_WL_HOST_WAKE / 8], GPIO_IRQ_LEVEL_HIGH << (4 * (CYW43_PIN_WL_HOST_WAKE & 7)));
|
||||
low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_CYW43, cyw43_poll);
|
||||
CYW43_STAT_INC(IRQ_COUNT);
|
||||
}
|
||||
}
|
||||
|
||||
// Low priority interrupt handler to perform background processing
|
||||
static void low_priority_irq_handler(void) {
|
||||
assert(cyw43_core_num == get_core_num());
|
||||
if (recursive_mutex_try_enter(&cyw43_mutex, NULL)) {
|
||||
if (recursive_mutex_enter_count(&cyw43_mutex) != 1) {
|
||||
low_priority_irq_missed = true;
|
||||
CYW43_STAT_INC(PENDSV_DISABLED_COUNT);
|
||||
} else {
|
||||
CYW43_STAT_INC(PENDSV_RUN_COUNT);
|
||||
#ifndef NDEBUG
|
||||
in_low_priority_irq = true;
|
||||
#endif
|
||||
for (size_t i = 0; i < count_of(low_priority_irq_dispatch_slots); i++) {
|
||||
if (low_priority_irq_dispatch_slots[i] != NULL) {
|
||||
low_prio_irq_dispatch_t f = low_priority_irq_dispatch_slots[i];
|
||||
low_priority_irq_dispatch_slots[i] = NULL;
|
||||
f();
|
||||
}
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
in_low_priority_irq = false;
|
||||
#endif
|
||||
}
|
||||
recursive_mutex_exit(&cyw43_mutex);
|
||||
} else {
|
||||
CYW43_STAT_INC(PENDSV_DISABLED_COUNT);
|
||||
low_priority_irq_missed = true;
|
||||
}
|
||||
sem_release(&cyw43_irq_sem);
|
||||
}
|
||||
|
||||
static bool low_prio_irq_init(uint8_t priority) {
|
||||
assert(get_core_num() == cyw43_core_num);
|
||||
int irq = user_irq_claim_unused(false);
|
||||
if (irq < 0) {
|
||||
return false;
|
||||
}
|
||||
low_priority_irq_num = (uint8_t) irq;
|
||||
irq_set_exclusive_handler(low_priority_irq_num, low_priority_irq_handler);
|
||||
irq_set_enabled(low_priority_irq_num, true);
|
||||
irq_set_priority(low_priority_irq_num, priority);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void low_prio_irq_deinit(void) {
|
||||
if (low_priority_irq_num > 0) {
|
||||
irq_set_enabled(low_priority_irq_num, false);
|
||||
irq_remove_handler(low_priority_irq_num, low_priority_irq_handler);
|
||||
user_irq_unclaim(low_priority_irq_num);
|
||||
low_priority_irq_num = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int cyw43_arch_init(void) {
|
||||
cyw43_core_num = get_core_num();
|
||||
recursive_mutex_init(&cyw43_mutex);
|
||||
cyw43_init(&cyw43_state);
|
||||
sem_init(&cyw43_irq_sem, 0, 1);
|
||||
|
||||
// Start regular lwip callback to handle timeouts
|
||||
periodic_alarm = add_alarm_in_us(CYW43_SLEEP_CHECK_MS * 1000, periodic_alarm_handler, NULL, true);
|
||||
if (periodic_alarm < 0) {
|
||||
return PICO_ERROR_GENERIC;
|
||||
}
|
||||
|
||||
gpio_add_raw_irq_handler_with_order_priority(IO_IRQ_BANK0, gpio_irq_handler, CYW43_GPIO_IRQ_HANDLER_PRIORITY);
|
||||
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, true);
|
||||
irq_set_enabled(IO_IRQ_BANK0, true);
|
||||
|
||||
lwip_init();
|
||||
|
||||
// start low priority handler (no background work is done before this)
|
||||
bool ok = low_prio_irq_init(PICO_LOWEST_IRQ_PRIORITY);
|
||||
if (!ok) {
|
||||
cyw43_arch_deinit();
|
||||
return PICO_ERROR_GENERIC;
|
||||
}
|
||||
return PICO_OK;
|
||||
}
|
||||
|
||||
void cyw43_arch_deinit(void) {
|
||||
if (periodic_alarm >= 0) {
|
||||
cancel_alarm(periodic_alarm);
|
||||
periodic_alarm = -1;
|
||||
}
|
||||
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, false);
|
||||
gpio_remove_raw_irq_handler(IO_IRQ_BANK0, gpio_irq_handler);
|
||||
low_prio_irq_deinit();
|
||||
}
|
||||
|
||||
void cyw43_post_poll_hook(void) {
|
||||
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, true);
|
||||
}
|
||||
|
||||
// This is called in the gpio and low_prio_irq interrupts and on either core
|
||||
static void low_prio_irq_schedule_dispatch(size_t slot, low_prio_irq_dispatch_t f) {
|
||||
assert(slot < count_of(low_priority_irq_dispatch_slots));
|
||||
low_priority_irq_dispatch_slots[slot] = f;
|
||||
if (cyw43_core_num == get_core_num()) {
|
||||
//on same core, can dispatch directly
|
||||
irq_set_pending(low_priority_irq_num);
|
||||
} else {
|
||||
// on wrong core, so force via GPIO IRQ which itself calls this method for the CYW43 slot.
|
||||
// since the CYW43 slot always uses the same function, this is fine with the addition of an
|
||||
// extra (but harmless) CYW43 slot call when another SLOT is invoked.
|
||||
// We could do better, but would have to track why the IRQ was called.
|
||||
io_irq_ctrl_hw_t *irq_ctrl_base = cyw43_core_num ?
|
||||
&iobank0_hw->proc1_irq_ctrl : &iobank0_hw->proc0_irq_ctrl;
|
||||
hw_set_bits(&irq_ctrl_base->intf[CYW43_PIN_WL_HOST_WAKE / 8], GPIO_IRQ_LEVEL_HIGH << (4 * (CYW43_PIN_WL_HOST_WAKE & 7)));
|
||||
}
|
||||
}
|
||||
|
||||
void cyw43_schedule_internal_poll_dispatch(void (*func)(void)) {
|
||||
low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_CYW43, func);
|
||||
}
|
||||
|
||||
// Prevent background processing in pensv and access by the other core
|
||||
// These methods are called in pensv context and on either core
|
||||
// They can be called recursively
|
||||
void cyw43_thread_enter(void) {
|
||||
// Lock the other core and stop low_prio_irq running
|
||||
recursive_mutex_enter_blocking(&cyw43_mutex);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
void cyw43_thread_lock_check(void) {
|
||||
// Lock the other core and stop low_prio_irq running
|
||||
if (recursive_mutex_enter_count(&cyw43_mutex) < 1 || recursive_mutex_owner(&cyw43_mutex) != lock_get_caller_owner_id()) {
|
||||
panic("cyw43_thread_lock_check failed");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Re-enable background processing
|
||||
void cyw43_thread_exit(void) {
|
||||
// Run low_prio_irq if needed
|
||||
if (1 == recursive_mutex_enter_count(&cyw43_mutex)) {
|
||||
// note the outer release of the mutex is not via cyw43_exit in the low_priority_irq case (it is a direct mutex exit)
|
||||
assert(!in_low_priority_irq);
|
||||
// if (low_priority_irq_missed) {
|
||||
// low_priority_irq_missed = false;
|
||||
if (low_priority_irq_dispatch_slots[CYW43_DISPATCH_SLOT_CYW43]) {
|
||||
low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_CYW43, cyw43_poll);
|
||||
}
|
||||
// }
|
||||
}
|
||||
recursive_mutex_exit(&cyw43_mutex);
|
||||
}
|
||||
|
||||
|
||||
static void cyw43_delay_until(absolute_time_t until) {
|
||||
// sleep can be called in IRQs, so there's not much we can do there
|
||||
if (__get_current_exception()) {
|
||||
busy_wait_until(until);
|
||||
} else {
|
||||
sleep_until(until);
|
||||
}
|
||||
}
|
||||
|
||||
void cyw43_delay_ms(uint32_t ms) {
|
||||
cyw43_delay_until(make_timeout_time_ms(ms));
|
||||
}
|
||||
|
||||
void cyw43_delay_us(uint32_t us) {
|
||||
cyw43_delay_until(make_timeout_time_us(us));
|
||||
}
|
||||
|
||||
void cyw43_arch_poll() {
|
||||
// should not be necessary
|
||||
// if (cyw43_poll) {
|
||||
// low_prio_irq_schedule_dispatch(CYW43_DISPATCH_SLOT_CYW43, cyw43_poll);
|
||||
// }
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_RASPBERRY_PI_PICO_W
|
||||
void __attribute__((weak)) cyw43_cb_tcpip_init(cyw43_t *self, int itf);
|
||||
void cyw43_cb_tcpip_init(cyw43_t *self, int itf) {
|
||||
(void) self;
|
||||
(void) itf;
|
||||
}
|
||||
void __attribute__((weak)) cyw43_cb_tcpip_deinit(cyw43_t *self, int itf);
|
||||
void cyw43_cb_tcpip_deinit(cyw43_t *self, int itf) {
|
||||
(void) self;
|
||||
(void) itf;
|
||||
}
|
||||
void __attribute__((weak)) cyw43_cb_tcpip_set_link_up(cyw43_t *self, int itf);
|
||||
void cyw43_cb_tcpip_set_link_up(cyw43_t *self, int itf) {
|
||||
(void) self;
|
||||
(void) itf;
|
||||
}
|
||||
void __attribute__((weak)) cyw43_cb_tcpip_set_link_down(cyw43_t *self, int itf);
|
||||
void cyw43_cb_tcpip_set_link_down(cyw43_t *self, int itf) {
|
||||
(void) self;
|
||||
(void) itf;
|
||||
}
|
||||
void __attribute__((weak)) cyw43_cb_process_ethernet(void *cb_data, int itf, size_t len, const uint8_t *buf);
|
||||
void cyw43_cb_process_ethernet(void *cb_data, int itf, size_t len, const uint8_t *buf) {
|
||||
(void) cb_data;
|
||||
(void) itf;
|
||||
(void) len;
|
||||
(void) buf;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
|
||||
|
||||
Hacked by EFP3 to allow disabling unless requested
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <pico/time.h>
|
||||
#include <pico/bootrom.h>
|
||||
#include <pico/binary_info.h>
|
||||
|
||||
// PICO_CONFIG: PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS, Window of opportunity for a second press of a reset button to enter BOOTSEL mode (milliseconds), type=int, default=200, group=pico_bootsel_via_double_reset
|
||||
#ifndef PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS
|
||||
#define PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS 350
|
||||
#endif
|
||||
|
||||
// PICO_CONFIG: PICO_BOOTSEL_VIA_DOUBLE_RESET_ACTIVITY_LED, Optionally define a pin to use as bootloader activity LED when BOOTSEL mode is entered via reset double tap, type=int, min=0, max=29, group=pico_bootsel_via_double_reset
|
||||
|
||||
// PICO_CONFIG: PICO_BOOTSEL_VIA_DOUBLE_RESET_INTERFACE_DISABLE_MASK, Optionally disable either the mass storage interface (bit 0) or the PICOBOOT interface (bit 1) when entering BOOTSEL mode via double reset, type=int, min=0, max=3, default=0, group=pico_bootsel_via_double_reset
|
||||
#ifndef PICO_BOOTSEL_VIA_DOUBLE_RESET_INTERFACE_DISABLE_MASK
|
||||
#define PICO_BOOTSEL_VIA_DOUBLE_RESET_INTERFACE_DISABLE_MASK 0u
|
||||
#endif
|
||||
|
||||
/** \defgroup pico_bootsel_via_double_reset pico_bootsel_via_double_reset
|
||||
|
||||
When the 'pico_bootsel_via_double_reset' library is linked, a function is
|
||||
injected before main() which will detect when the system has been reset
|
||||
twice in quick succession, and enter the USB ROM bootloader (BOOTSEL mode)
|
||||
when this happens. This allows a double tap of a reset button on a
|
||||
development board to be used to enter the ROM bootloader, provided this
|
||||
library is always linked.
|
||||
*/
|
||||
|
||||
#if !PICO_NO_BI_BOOTSEL_VIA_DOUBLE_RESET
|
||||
bi_decl(bi_program_feature("double reset -> BOOTSEL"));
|
||||
#endif
|
||||
|
||||
// Doesn't make any sense for a RAM only binary
|
||||
#if !PICO_NO_FLASH
|
||||
static const uint32_t magic_token[] = {
|
||||
0xf01681de, 0xbd729b29, 0xd359be7a,
|
||||
};
|
||||
|
||||
// Place our marker on the 2nd core's stack...which will not have been touched when this code is executing
|
||||
static uint32_t *magic_location = (uint32_t*)0x20040000;
|
||||
//static uint32_t __uninitialized_ram(magic_location)[count_of(magic_token)];
|
||||
|
||||
/* Check for double reset and enter BOOTSEL mode if detected
|
||||
|
||||
This function is registered to run automatically before main(). The
|
||||
algorithm is:
|
||||
|
||||
1. Check for magic token in memory; enter BOOTSEL mode if found.
|
||||
2. Initialise that memory with that magic token.
|
||||
3. Do nothing for a short while (few hundred ms).
|
||||
4. Clear the magic token.
|
||||
5. Continue with normal boot.
|
||||
|
||||
Resetting the device twice quickly will interrupt step 3, leaving the token
|
||||
in place so that the second boot will go to the bootloader.
|
||||
*/
|
||||
|
||||
/*static*/ void __attribute__((constructor)) boot_double_tap_check(void) {
|
||||
for (uint i = 0; i < count_of(magic_token); i++) {
|
||||
if (magic_location[i] != magic_token[i]) {
|
||||
// Arm, wait, then disarm and continue booting
|
||||
for (i = 0; i < count_of(magic_token); i++) {
|
||||
magic_location[i] = magic_token[i];
|
||||
}
|
||||
busy_wait_us(PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS * 1000);
|
||||
magic_location[0] = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Detected a double reset, so enter USB bootloader
|
||||
magic_location[0] = 0;
|
||||
#ifdef PICO_BOOTSEL_VIA_DOUBLE_RESET_ACTIVITY_LED
|
||||
const uint32_t led_mask = 1u << PICO_BOOTSEL_VIA_DOUBLE_RESET_ACTIVITY_LED;
|
||||
#else
|
||||
const uint32_t led_mask = 0u;
|
||||
#endif
|
||||
reset_usb_boot(
|
||||
led_mask,
|
||||
PICO_BOOTSEL_VIA_DOUBLE_RESET_INTERFACE_DISABLE_MASK
|
||||
);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
Expost absolute mouse HID descriptor and report
|
||||
Taken from @tobozo PR https://github.com/hathach/tinyusb/pull/1363
|
||||
TODO - remove once that PR merged with TinyUSB
|
||||
|
||||
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 "tusb.h"
|
||||
#include "class/hid/hid_device.h"
|
||||
|
||||
// Absolute Mouse: same as the Standard (relative) Mouse Report but
|
||||
// with int16_t instead of int8_t for X and Y coordinates.
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t buttons; /**< buttons mask for currently pressed buttons in the mouse. */
|
||||
int16_t x; /**< Current x position of the mouse. */
|
||||
int16_t y; /**< Current y position of the mouse. */
|
||||
int8_t wheel; /**< Current delta wheel movement on the mouse. */
|
||||
int8_t pan; // using AC Pan
|
||||
} hid_abs_mouse_report_t;
|
||||
|
||||
|
||||
// Absolute Mouse Report Descriptor Template
|
||||
#define TUD_HID_REPORT_DESC_ABSMOUSE(...) \
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_MOUSE ) ,\
|
||||
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
|
||||
/* Report ID if any */\
|
||||
__VA_ARGS__ \
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_POINTER ) ,\
|
||||
HID_COLLECTION ( HID_COLLECTION_PHYSICAL ) ,\
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\
|
||||
HID_USAGE_MIN ( 1 ) ,\
|
||||
HID_USAGE_MAX ( 5 ) ,\
|
||||
HID_LOGICAL_MIN ( 0 ) ,\
|
||||
HID_LOGICAL_MAX ( 1 ) ,\
|
||||
/* Left, Right, Middle, Backward, Forward buttons */ \
|
||||
HID_REPORT_COUNT( 5 ) ,\
|
||||
HID_REPORT_SIZE ( 1 ) ,\
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||
/* 3 bit padding */ \
|
||||
HID_REPORT_COUNT( 1 ) ,\
|
||||
HID_REPORT_SIZE ( 3 ) ,\
|
||||
HID_INPUT ( HID_CONSTANT ) ,\
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||
/* X, Y absolute position [0, 32767] */ \
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_X ) ,\
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_Y ) ,\
|
||||
HID_LOGICAL_MIN ( 0x00 ) ,\
|
||||
HID_LOGICAL_MAX_N( 0x7FFF, 2 ) ,\
|
||||
HID_REPORT_SIZE ( 16 ) ,\
|
||||
HID_REPORT_COUNT ( 2 ) ,\
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||
/* Vertical wheel scroll [-127, 127] */ \
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_WHEEL ) ,\
|
||||
HID_LOGICAL_MIN ( 0x81 ) ,\
|
||||
HID_LOGICAL_MAX ( 0x7f ) ,\
|
||||
HID_REPORT_COUNT( 1 ) ,\
|
||||
HID_REPORT_SIZE ( 8 ) ,\
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ), \
|
||||
/* Horizontal wheel scroll [-127, 127] */ \
|
||||
HID_USAGE_N ( HID_USAGE_CONSUMER_AC_PAN, 2 ), \
|
||||
HID_LOGICAL_MIN ( 0x81 ), \
|
||||
HID_LOGICAL_MAX ( 0x7f ), \
|
||||
HID_REPORT_COUNT( 1 ), \
|
||||
HID_REPORT_SIZE ( 8 ), \
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ), \
|
||||
HID_COLLECTION_END , \
|
||||
HID_COLLECTION_END \
|
||||
|
||||
|
||||
static inline bool tud_hid_abs_mouse_report(uint8_t report_id,
|
||||
uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal) {
|
||||
hid_abs_mouse_report_t report = {
|
||||
.buttons = buttons,
|
||||
.x = x,
|
||||
.y = y,
|
||||
.wheel = vertical,
|
||||
.pan = horizontal
|
||||
};
|
||||
|
||||
return tud_hid_n_report(0, report_id, &report, sizeof(report));
|
||||
}
|
||||
|
||||
@@ -26,9 +26,12 @@
|
||||
#include <hardware/pll.h>
|
||||
#include <hardware/adc.h>
|
||||
|
||||
void __clearADCPin(pin_size_t p);
|
||||
|
||||
static uint32_t analogScale = 255;
|
||||
static uint32_t analogFreq = 1000;
|
||||
static bool pwmInitted = false;
|
||||
static uint32_t pwmInitted = 0;
|
||||
static bool scaleInitted = false;
|
||||
static bool adcInitted = false;
|
||||
static uint16_t analogWritePseudoScale = 1;
|
||||
static uint16_t analogWriteSlowScale = 1;
|
||||
@@ -40,31 +43,33 @@ extern "C" void analogWriteFreq(uint32_t freq) {
|
||||
return;
|
||||
}
|
||||
if (freq < 100) {
|
||||
DEBUGCORE("ERROR: analogWriteFreq too low (%d)\n", freq);
|
||||
DEBUGCORE("ERROR: analogWriteFreq too low (%lu)\n", freq);
|
||||
analogFreq = 100;
|
||||
} else if (freq > 1'000'000) {
|
||||
DEBUGCORE("ERROR: analogWriteFreq too high (%d)\n", freq);
|
||||
analogFreq = 1'000'000;
|
||||
} else if (freq > 10'000'000) {
|
||||
DEBUGCORE("ERROR: analogWriteFreq too high (%lu)\n", freq);
|
||||
analogFreq = 10'000'000;
|
||||
} else {
|
||||
analogFreq = freq;
|
||||
}
|
||||
pwmInitted = false;
|
||||
pwmInitted = 0;
|
||||
scaleInitted = false;
|
||||
}
|
||||
|
||||
extern "C" void analogWriteRange(uint32_t range) {
|
||||
if (range == analogScale) {
|
||||
return;
|
||||
}
|
||||
if ((range >= 15) && (range <= 65535)) {
|
||||
if ((range >= 3) && (range <= 65535)) {
|
||||
analogScale = range;
|
||||
pwmInitted = false;
|
||||
pwmInitted = 0;
|
||||
scaleInitted = false;
|
||||
} else {
|
||||
DEBUGCORE("ERROR: analogWriteRange out of range (%d)\n", range);
|
||||
DEBUGCORE("ERROR: analogWriteRange out of range (%lu)\n", range);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void analogWriteResolution(int res) {
|
||||
if ((res >= 4) && (res <= 16)) {
|
||||
if ((res >= 2) && (res <= 16)) {
|
||||
analogWriteRange((1 << res) - 1);
|
||||
} else {
|
||||
DEBUGCORE("ERROR: analogWriteResolution out of range (%d)\n", res);
|
||||
@@ -78,29 +83,30 @@ extern "C" void analogWrite(pin_size_t pin, int val) {
|
||||
DEBUGCORE("ERROR: Illegal analogWrite pin (%d)\n", pin);
|
||||
return;
|
||||
}
|
||||
if (!pwmInitted) {
|
||||
__clearADCPin(pin);
|
||||
if (!scaleInitted) {
|
||||
// For low frequencies, we need to scale the output max value up to achieve lower periods
|
||||
analogWritePseudoScale = 1;
|
||||
while (((clock_get_hz(clk_sys) / (float)(analogScale * analogFreq)) > 255.0) && (analogScale < 32678)) {
|
||||
while (((clock_get_hz(clk_sys) / ((float)analogScale * analogFreq)) > 255.0) && (analogScale < 32678)) {
|
||||
analogWritePseudoScale++;
|
||||
analogScale *= 2;
|
||||
DEBUGCORE("Adjusting analogWrite values PS=%d, scale=%d\n", analogWritePseudoScale, analogScale);
|
||||
DEBUGCORE("Adjusting analogWrite values PS=%d, scale=%lu\n", analogWritePseudoScale, analogScale);
|
||||
}
|
||||
// For high frequencies, we need to scale the output max value down to actually hit the frequency target
|
||||
analogWriteSlowScale = 1;
|
||||
while (((clock_get_hz(clk_sys) / (float)(analogScale * analogFreq)) < 2.0) && (analogScale > 32)) {
|
||||
while (((clock_get_hz(clk_sys) / ((float)analogScale * analogFreq)) < 1.0) && (analogScale >= 6)) {
|
||||
analogWriteSlowScale++;
|
||||
analogScale /= 2;
|
||||
DEBUGCORE("Adjusting analogWrite values SS=%d, scale=%d\n", analogWriteSlowScale, analogScale);
|
||||
DEBUGCORE("Adjusting analogWrite values SS=%d, scale=%lu\n", analogWriteSlowScale, analogScale);
|
||||
}
|
||||
|
||||
scaleInitted = true;
|
||||
}
|
||||
if (!(pwmInitted & (1 << pwm_gpio_to_slice_num(pin)))) {
|
||||
pwm_config c = pwm_get_default_config();
|
||||
pwm_config_set_clkdiv(&c, clock_get_hz(clk_sys) / (float)(analogScale * analogFreq));
|
||||
pwm_config_set_wrap(&c, analogScale);
|
||||
for (int i = 0; i < 30; i++) {
|
||||
pwm_init(pwm_gpio_to_slice_num(i), &c, true);
|
||||
}
|
||||
pwmInitted = true;
|
||||
pwm_config_set_clkdiv(&c, clock_get_hz(clk_sys) / ((float)analogScale * analogFreq));
|
||||
pwm_config_set_wrap(&c, analogScale - 1);
|
||||
pwm_init(pwm_gpio_to_slice_num(pin), &c, true);
|
||||
pwmInitted |= 1 << pwm_gpio_to_slice_num(pin);
|
||||
}
|
||||
|
||||
val <<= analogWritePseudoScale;
|
||||
@@ -117,7 +123,13 @@ extern "C" void analogWrite(pin_size_t pin, int val) {
|
||||
}
|
||||
|
||||
auto_init_mutex(_adcMutex);
|
||||
static int _readBits = 10;
|
||||
static uint8_t _readBits = 10;
|
||||
static uint8_t _lastADCMux = 0;
|
||||
static uint32_t _adcGPIOInit = 0;
|
||||
|
||||
void __clearADCPin(pin_size_t p) {
|
||||
_adcGPIOInit &= ~(1 << p);
|
||||
}
|
||||
|
||||
extern "C" int analogRead(pin_size_t pin) {
|
||||
CoreMutex m(&_adcMutex);
|
||||
@@ -133,12 +145,18 @@ extern "C" int analogRead(pin_size_t pin) {
|
||||
adc_init();
|
||||
adcInitted = true;
|
||||
}
|
||||
adc_gpio_init(pin);
|
||||
adc_select_input(pin - minPin);
|
||||
if (!(_adcGPIOInit & (1 << pin))) {
|
||||
adc_gpio_init(pin);
|
||||
_adcGPIOInit |= 1 << pin;
|
||||
}
|
||||
if (_lastADCMux != pin) {
|
||||
adc_select_input(pin - minPin);
|
||||
_lastADCMux = pin;
|
||||
}
|
||||
return (_readBits < 12) ? adc_read() >> (12 - _readBits) : adc_read() << (_readBits - 12);
|
||||
}
|
||||
|
||||
extern "C" float analogReadTemp() {
|
||||
extern "C" float analogReadTemp(float vref) {
|
||||
CoreMutex m(&_adcMutex);
|
||||
|
||||
if (!m) {
|
||||
@@ -148,12 +166,13 @@ extern "C" float analogReadTemp() {
|
||||
adc_init();
|
||||
adcInitted = true;
|
||||
}
|
||||
_lastADCMux = 0;
|
||||
adc_set_temp_sensor_enabled(true);
|
||||
delay(1); // Allow things to settle. Without this, readings can be erratic
|
||||
adc_select_input(4); // Temperature sensor
|
||||
int v = adc_read();
|
||||
adc_set_temp_sensor_enabled(false);
|
||||
float t = 27.0f - ((v * 3.3f / 4096.0f) - 0.706f) / 0.001721f; // From the datasheet
|
||||
float t = 27.0f - ((v * vref / 4096.0f) - 0.706f) / 0.001721f; // From the datasheet
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "Arduino.h"
|
||||
#include <hardware/gpio.h>
|
||||
|
||||
extern void __clearADCPin(pin_size_t p);
|
||||
|
||||
static PinMode _pm[30];
|
||||
|
||||
extern "C" void pinMode(pin_size_t ulPin, PinMode ulMode) __attribute__((weak, alias("__pinMode")));
|
||||
@@ -75,6 +77,10 @@ extern "C" void __pinMode(pin_size_t ulPin, PinMode ulMode) {
|
||||
return;
|
||||
}
|
||||
_pm[ulPin] = ulMode;
|
||||
|
||||
if ((ulPin >= std::min(A0, A3)) && (ulPin <= std::max(A0, A3))) {
|
||||
__clearADCPin(ulPin);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void digitalWrite(pin_size_t ulPin, PinStatus ulVal) __attribute__((weak, alias("__digitalWrite")));
|
||||
|
||||
@@ -22,24 +22,30 @@
|
||||
#include <CoreMutex.h>
|
||||
#include <hardware/gpio.h>
|
||||
#include <hardware/sync.h>
|
||||
#include <stack>
|
||||
#include <map>
|
||||
|
||||
// Support nested IRQ disable/re-enable
|
||||
static std::stack<uint32_t> _irqStack[2];
|
||||
#define maxIRQs 15
|
||||
static uint32_t _irqStackTop[2] = { 0, 0 };
|
||||
static uint32_t _irqStack[2][maxIRQs];
|
||||
|
||||
extern "C" void interrupts() {
|
||||
if (_irqStack[get_core_num()].empty()) {
|
||||
auto core = get_core_num();
|
||||
if (!_irqStackTop[core]) {
|
||||
// ERROR
|
||||
return;
|
||||
}
|
||||
auto oldIrqs = _irqStack[get_core_num()].top();
|
||||
_irqStack[get_core_num()].pop();
|
||||
restore_interrupts(oldIrqs);
|
||||
restore_interrupts(_irqStack[core][--_irqStackTop[core]]);
|
||||
}
|
||||
|
||||
extern "C" void noInterrupts() {
|
||||
_irqStack[get_core_num()].push(save_and_disable_interrupts());
|
||||
auto core = get_core_num();
|
||||
if (_irqStackTop[core] == maxIRQs) {
|
||||
// ERROR
|
||||
panic("IRQ stack overflow");
|
||||
}
|
||||
|
||||
_irqStack[core][_irqStackTop[core]++] = save_and_disable_interrupts();
|
||||
}
|
||||
|
||||
// Only 1 GPIO IRQ callback for all pins, so we need to look at the pin it's for and
|
||||
@@ -81,6 +87,15 @@ void _gpioInterruptDispatcher(uint gpio, uint32_t events) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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()) {
|
||||
gpio_set_irq_enabled(pin, 0x0f /* all */, false);
|
||||
_map.erase(pin);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void attachInterrupt(pin_size_t pin, voidFuncPtr callback, PinStatus mode) {
|
||||
CoreMutex m(&_irqMutex);
|
||||
if (!m) {
|
||||
@@ -97,7 +112,7 @@ extern "C" void attachInterrupt(pin_size_t pin, voidFuncPtr callback, PinStatus
|
||||
default: return; // ERROR
|
||||
}
|
||||
noInterrupts();
|
||||
detachInterrupt(pin);
|
||||
_detachInterruptInternal(pin);
|
||||
CBInfo cb(callback);
|
||||
_map.insert({pin, cb});
|
||||
gpio_set_irq_enabled_with_callback(pin, events, true, _gpioInterruptDispatcher);
|
||||
@@ -120,7 +135,7 @@ void attachInterruptParam(pin_size_t pin, voidFuncPtrParam callback, PinStatus m
|
||||
default: return; // ERROR
|
||||
}
|
||||
noInterrupts();
|
||||
detachInterrupt(pin);
|
||||
_detachInterruptInternal(pin);
|
||||
CBInfo cb(callback, param);
|
||||
_map.insert({pin, cb});
|
||||
gpio_set_irq_enabled_with_callback(pin, events, true, _gpioInterruptDispatcher);
|
||||
@@ -134,10 +149,6 @@ extern "C" void detachInterrupt(pin_size_t pin) {
|
||||
}
|
||||
|
||||
noInterrupts();
|
||||
auto irq = _map.find(pin);
|
||||
if (irq != _map.end()) {
|
||||
gpio_set_irq_enabled(pin, 0x0f /* all */, false);
|
||||
_map.erase(pin);
|
||||
}
|
||||
_detachInterruptInternal(pin);
|
||||
interrupts();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
ADC Input Library
|
||||
=================
|
||||
|
||||
The ADC pins can be sampled and recorded by an application using the same
|
||||
interface as the I2S or PWM Audio libraries. This allows analog devices which
|
||||
need to be periodically sampled to be read by applications, easily, such as:
|
||||
|
||||
* Analog electret microphones
|
||||
|
||||
* Potentiometers
|
||||
|
||||
* Light dependent resistors (LDR), etc.
|
||||
|
||||
|
||||
Up to 4 analog samples can be recorded by the hardware (``A0`` ... ``A3``), and all
|
||||
recording is done at 16-bit levels (but be aware that the ADC in the Pico will only
|
||||
ever return values between 0...4095).
|
||||
|
||||
The interface for the ``ADCInput`` device is very similar to the ``I2S`` input
|
||||
device, and most code can be ported simply by instantiating a ``ADCInput``
|
||||
object in lieu of an ``I2S`` input object and choosing the pins to record.
|
||||
|
||||
Since this uses the ADC hardware, no ``analogRead`` or ``analogReadTemp`` calls are
|
||||
allowed while in use.
|
||||
|
||||
ADC Input API
|
||||
-------------
|
||||
|
||||
ADCInput(pin0 [, pin1, pin2, pin3])
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Creates an ADC input object which will record the pins specified in the code.
|
||||
Only pins ``A0`` ... ``A3`` can be used, and they must be specified in increasing
|
||||
order (i.e. ``ADCInput(A0, A1);`` is valid, but ``ADCInput(A1, A0)`` is not.
|
||||
|
||||
bool setBuffers(size_t buffers, size_t bufferWords)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Set the number of DMA buffers and their size in 32-bit words. Call before
|
||||
``ADCInput::begin()``.
|
||||
|
||||
When running at high sample rates, it is recommended to increase the
|
||||
``bufferWords`` to 32 or higher (i.e. ``adcinput.setBuffers(4, 32);`` ).
|
||||
|
||||
bool setPins(pin_size_t pin [, pin1, pin2, pin3])
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Adjusts the pin to record. Only legal before ``ADCInput::begin()``.
|
||||
|
||||
bool setFrequency(long sampleRate)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Sets the ADC sampling frequency, but does not start recording (however if the
|
||||
device was already running, it will continue to run at the new frequency). Note
|
||||
that every pin requested will be sampled at this frequency, one after the other.
|
||||
That is, if you have code like this:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
ADCInput adc(A0, A1);
|
||||
adc.setFrequency(1000);
|
||||
|
||||
``A0`` will be sampled at 0ms, 1ms, 2ms, etc. and ``A1`` will be sampled at 0.5ms
|
||||
1.5ms, 2.5ms, etc. Each input is sampled at the proper frequency but offset in time
|
||||
since there is only one active ADC at a time.
|
||||
|
||||
bool begin()/begin(long sampleRate)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Start the ADC input up with the given sample rate, or with the value set
|
||||
using the prior ``setFrequency`` call.
|
||||
|
||||
void end()
|
||||
~~~~~~~~~~
|
||||
Stops the ADC Input device.
|
||||
|
||||
int read()
|
||||
~~~~~~~~~~
|
||||
Reads a single sample of recorded ADC data, as a 16-bit value. When multiple pins are
|
||||
recorded the first read will be pin 0, the second will be pin 1, etc. Applications need
|
||||
to keep track of which pin is being returned (normally by always reading out all pins
|
||||
at once). Will not return until data is available.
|
||||
|
||||
int available()
|
||||
~~~~~~~~~~~~~~~
|
||||
Returns the number of samples that can be read without potentially blocking.
|
||||
|
||||
void onReceive(void (\*fn)(void))
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Sets a callback to be called when a ADC input DMA buffer is fully filled.
|
||||
Will be in an interrupt context so the specified function must operate
|
||||
quickly and not use blocking calls like delay().
|
||||
+4
-3
@@ -14,13 +14,14 @@ Returns a value from 0...4095 correspionding to the ADC reading
|
||||
of the specific pin.
|
||||
|
||||
void analogReadResolution(int bits)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Determines the resolution (in bits) of the value returned by the analogRead() function.
|
||||
Default resolution is 10bit.
|
||||
|
||||
float analogReadTemp()
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
float analogReadTemp(float vref = 3.3f)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns the temperature, in Celsius, of the onboard thermal sensor.
|
||||
If you have a custom Vref for the ADC on your RP2040 board, you can pass it in as a parameter. Calling with no parameters assumes the normal, 3.3V Vref.
|
||||
This reading is not exceedingly accurate and of relatively low
|
||||
resolution, so it is not a replacement for an external temperature
|
||||
sensor in many cases.
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
Bluetooth on PicoW Support
|
||||
==========================
|
||||
|
||||
As of the Pico-SDK version 1.5.0, the PicoW has **BETA** Bluetooth support.
|
||||
So, since this core builds off the SDK the best that can be suggested it
|
||||
that we have **ALPHA** Bluetooth support. As such, bug reports are welcome,
|
||||
but Pull Requests fixing problems you find are seriously appreciated.
|
||||
|
||||
Enabling Bluetooth
|
||||
------------------
|
||||
To enable Bluetooth (BT), use the ``Tools->IP/Bluetooth Stack`` menu. It
|
||||
requires around 80KB of flash and 20KB of RAM when enabled.
|
||||
|
||||
Both Bluetooth Classic and BluetoothBLE are enabled in ``btstack_config.h``.
|
||||
|
||||
Included Bluetooth Libraries
|
||||
----------------------------
|
||||
You may use the ``KeyboardBT``, ``MouseBT``, or ``JoystickBT`` to emulate a
|
||||
Bluetooth Classic HID device using the same API as their USB versions.
|
||||
|
||||
You may use the ``KeyboardBLE``, ``MouseBLE``, or ``JoystickBLE`` to emulate a
|
||||
Bluetooth Low Energy (BLE) HID device using the same API as their USB versions.
|
||||
|
||||
The ``SerialBT`` library implements a very simple SPP (Serial Port Profile)
|
||||
Serial-compatible port.
|
||||
|
||||
Writing Custom Bluetooth Applications
|
||||
-------------------------------------
|
||||
You may also write full applications using the ``BTStack`` standard callback
|
||||
method, but please be aware that the Raspberry Pi team has built an
|
||||
interrupt-driven version of the BT execute loop, so there is no need
|
||||
to actually call ``btstack_run_loop_execute`` because the ``async_context``
|
||||
handler will do it for you.
|
||||
|
||||
There is no need to call ``cyw43_arch_init`` in your code, either, as that
|
||||
is part of the PicoW variant booting process.
|
||||
|
||||
For many BTStack examples, you simply need call the included
|
||||
``btstack_main()`` and make sure that ``hci_power_control(HCI_POWER_ON);`` is
|
||||
called afterwards to start processing (in the background).
|
||||
|
||||
You will also need to acquire the BT ``async_context`` system lock before
|
||||
calling any BTStack APIs. See the ``libraries/PicoBluetoothHID`` helper
|
||||
class for an example of how to do this.
|
||||
|
||||
Note that if you need to modify the system ``btstack_config.h`` file, do so
|
||||
in the ``tools/libpico`` directory and rebuild the Pico SDK static library.
|
||||
Otherwise the change will not take effect in the precompiled code, leading
|
||||
to really bad behavior.
|
||||
+2
-2
@@ -54,9 +54,9 @@ author = u'Earle F. Philhower, III'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = u'2.5.4'
|
||||
version = u'3.5.0'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = u'2.5.4'
|
||||
release = u'3.5.0'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
Contributing and Porting to the Core
|
||||
====================================
|
||||
|
||||
First of all, thank you for contributing to the project. It's a lot of work
|
||||
keeping up with all the different uses of the RP2040, so the more people
|
||||
working on the code, the better. Your assistance can help the project
|
||||
succeed.
|
||||
|
||||
Contributing to the Core (Pull Requests)
|
||||
----------------------------------------
|
||||
|
||||
We use the standard GitHub Pull Request model. If you're unfamiliar with it,
|
||||
this `guide <https://www.freecodecamp.org/news/how-to-make-your-first-pull-request-on-github-3/>`__
|
||||
gives a simple overview of the process.
|
||||
|
||||
All pull requests have to pass a set of Continuous Integration (CI) checks
|
||||
which help make sure the code compiles under different configurations and has
|
||||
no spelling or style errors.
|
||||
|
||||
Tips for a Good Pull Request (PR)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
All code in the core and libraries, except for INO sketches, uses a 4-space
|
||||
indent with cuddled brackets. When in doubt, copy your formatting from the
|
||||
surrounding code. You should install ``astyle`` and run ``tests/restyle.sh``
|
||||
on your machine before committing and pushing any pull requests to ensure
|
||||
the formatting is correct.
|
||||
|
||||
Describe the change you're proposing and why it's important in your
|
||||
``git commit`` message. If it fixes an open issue, place ``Fixes #xxxx``
|
||||
(where xxxx is the issue number) in the message to link the two.
|
||||
|
||||
Try and only change one thing per pull request. That makes it easier to
|
||||
review and prioritize. Opening up a separate PR per change also helps keep
|
||||
track of them when release messages are generated.
|
||||
|
||||
Adding a New Board
|
||||
------------------
|
||||
|
||||
Adding a new board requires:
|
||||
|
||||
* Updated ``tools/makeboards.py`` script
|
||||
* Updated ``boards.txt`` file, generated by ``makeboard.py``
|
||||
* Updated ``package_pico_index.template.json`` file, generated by ``makeboard.py``
|
||||
* New ``tools/json/BOARD_NAME.json`` board file for Platform.IO
|
||||
* New ``variants/BOARD_NAME/pins_arduino.h`` header defining the I/O pins
|
||||
|
||||
To add a new RP2040 board you will need to update the ``tools/makeboards.py``
|
||||
script. Do *NOT* manually edit ``boards.txt``, that file is machine generated.
|
||||
You will need to add a ``MakeBoard`` call at the end of the file. Please be sure
|
||||
to add your board so that it sorts alphabetically, starting with the company name
|
||||
and then the board name. Otherwise it is hard to find a specific board in the menu.
|
||||
|
||||
Run ``python3 tools/makeboards.py`` to update the ``boards.txt`` file and generate
|
||||
a Platform.IO JSON file in the ``tools/json`` directory.
|
||||
|
||||
Create a folder called ``variants/BOARD_NAME`` and place in a ``pins_arduino.h``
|
||||
file in it that contains your default pin name mapping (i.e. SPI0/1 pins, UART
|
||||
pins, LED_DEFAULT, etc.). Copying one of the existing ones as a template can
|
||||
make this task much simpler.
|
||||
|
||||
In your ``git commit`` be sure to add the newly generated ``tools/json/XXX.json``
|
||||
file as well as the modified ``makeboards`` script and ``boards.txt``, the new
|
||||
``pins_arduino.h`` header you generated, and the Arduino packaging JSON
|
||||
``package/package_pico_index.template.json``. You should also add a note in
|
||||
the ``README.md`` file listing your new board.
|
||||
|
||||
Submit the updated commit as a PR and, if all goes well, your board will be in
|
||||
on the next core release.
|
||||
|
||||
|
||||
Porting Libraries and Applications to the Core
|
||||
----------------------------------------------
|
||||
|
||||
We try and follow Arduino standards so, with luck, porting to this core should
|
||||
be relatively straightforward. The ``WiFi`` library and associates support
|
||||
libraries like ``WebServer`` are modeled after the ESP32 and ESP8266 versions
|
||||
of those libraries, combined with the "standard" Arduino ``WiFi`` one.
|
||||
|
||||
Compiler Defines for Porting
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you are adding RP2040 support to an existing library and need to isolate
|
||||
code that only runs on this core, use the following define.
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
#if defined(ARDUINO_ARCH_RP2040) && !defined(__MBED__)
|
||||
~~~ your changes ~~~
|
||||
#endif
|
||||
|
||||
Library Architectures
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
After adding support in the code, libraries need their ``library.properties``
|
||||
and ``library.json`` files updated to indicate support, or the IDE will
|
||||
not know your new code is compatible here.
|
||||
|
||||
Add ``rp2040`` to ``architectures`` (in ``library.properties``) and
|
||||
``"rp2040"`` to ``platforms[]`` (in ``library.json``) to let the tools know.
|
||||
+2
-2
@@ -325,8 +325,8 @@ info64
|
||||
.. code:: cpp
|
||||
|
||||
FSInfo64 fsinfo;
|
||||
SDFS.info(fsinfo);
|
||||
or LittleFS(fsinfo);
|
||||
SDFS.info64(fsinfo);
|
||||
or LittleFS.info64(fsinfo);
|
||||
|
||||
Performs the same operation as ``info`` but allows for reporting greater than
|
||||
4GB for filesystem size/used/etc. Should be used with the SD and SDFS
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
Getting Help and Contributing
|
||||
=============================
|
||||
Getting Help
|
||||
============
|
||||
|
||||
This is a community supported project and has multiple ways to get assistance.
|
||||
Posting complete details, in a polite and organized way will get the best
|
||||
|
||||
+5
-5
@@ -1,7 +1,7 @@
|
||||
HTTPClient Library
|
||||
==================
|
||||
|
||||
A simple HTTP requestor that can handle both HTTP and HTTP requests is
|
||||
A simple HTTP requester that can handle both HTTP and HTTPS requests is
|
||||
included as the ``HTTPClient`` library.
|
||||
|
||||
Check the examples for use under HTTP and HTTPS configurations. In general,
|
||||
@@ -27,12 +27,12 @@ WiFiClientSecure documentation for more details.
|
||||
|
||||
// Error checking is left as an exercise for the reader...
|
||||
HTTPClient https;
|
||||
https.setInsecure(); // Use certs, but do not check their authenticity
|
||||
https.setInsecure(); // Use certs, but do not check their authenticity
|
||||
if (https.begin("https://my.secure.server/url")) {
|
||||
if (http.GET() > 0) {
|
||||
String data = http.getString();
|
||||
if (https.GET() > 0) {
|
||||
String data = https.getString();
|
||||
}
|
||||
http.end();
|
||||
https.end();
|
||||
}
|
||||
|
||||
Unlike the ESP8266 and ESP32 ``HTTPClient`` implementations it is not necessary
|
||||
|
||||
+45
-1
@@ -40,6 +40,16 @@ bool setDATA(pin_size_t pin)
|
||||
Sets the DOUT or DIN pin of the I2S device. Any pin may be used.
|
||||
Call before ``I2S::begin()``
|
||||
|
||||
bool setMCLK(pin_size_t pin)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Sets the MCLK pin of the I2S device and enables MCLK output. Any pin may be used.
|
||||
Call before ``I2S::begin()``
|
||||
|
||||
bool setMCLKmult(int mult)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Sets the sample rate to MCLK multiplier value. Only multiples of 64 are valid.
|
||||
Call before ``I2S::begin()``
|
||||
|
||||
bool setBitsPerSample(int bits)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Specify how many bits per audio sample to read or write. Note that
|
||||
@@ -58,6 +68,35 @@ Sets the word clock frequency, but does not start the I2S device if not
|
||||
already running. May be called after ``I2S::begin()`` to change the
|
||||
sample rate on-the-fly.
|
||||
|
||||
bool setSysClk(int samplerate)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Changes the PICO system clock to optimise for the desired samplerate.
|
||||
The clock changes to 147.6 MHz for samplerates that are a multiple of 8 kHz, and 135.6 MHz for multiples of 11.025 kHz.
|
||||
Note that using ``setSysClk()`` may affect the timing of other sysclk-dependent functions.
|
||||
Should be called before any I2S functions and any other sysclk dependent initialisations.
|
||||
|
||||
bool setLSBJFormat()
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
Enables LSB-J format for I2S output. In this mode the MSB comes out at the
|
||||
same time as the LRCLK changes, and not the normal 1-cycle delay. Useful for
|
||||
DAC chips like the PT8211.
|
||||
|
||||
bool setTDMFormat()
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
Enabled TDM formatted multi-channel output. Be sure to set the number of channels to
|
||||
the expected value (8 normally) and the bits per sample to 32.
|
||||
|
||||
bool setTDMChannels(int channels)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Sets the number of TDM channels between frame syncs. Generally should be set to 8.
|
||||
|
||||
bool swapClocks()
|
||||
~~~~~~~~~~~~~~~~~
|
||||
Certain boards are hardwired with the WCLK before the BCLK, instead of the normal
|
||||
way around. This call swaps the WCLK and BCLK pins. Note that you still call
|
||||
``setBCLK(x)`` with ``x`` being the lowest pin ID (i.e. in swapClocks mode the
|
||||
``setBCLK`` call actually sets LRCLK).
|
||||
|
||||
bool begin()/begin(long sampleRate)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Start the I2S device up with the given sample rate, or with the value set
|
||||
@@ -71,6 +110,11 @@ void flush()
|
||||
~~~~~~~~~~~~
|
||||
Waits until all the I2S buffers have been output.
|
||||
|
||||
void getOverUnderflow()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns a flag indicating if the I2S system ran our of data to send on output,
|
||||
or had to throw away data on input.
|
||||
|
||||
size_t write(uint8_t/int8_t/int16_t/int32_t)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Writes a single sample of ``bitsPerSample`` to the buffer. It is up to the
|
||||
@@ -99,7 +143,7 @@ many bytes were actually written.
|
||||
|
||||
int availableForWrite()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns the number of L/R samples that can be written without
|
||||
Returns the amount of bytes that can be written without
|
||||
potentially blocking.
|
||||
|
||||
int read()
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 7.0 KiB |
+9
-1
@@ -16,7 +16,9 @@ For the latest version, always check https://github.com/earlephilhower/arduino-p
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
Getting Help and Contributing <help>
|
||||
Getting Help <help>
|
||||
Contributing <contrib>
|
||||
|
||||
Installation <install>
|
||||
IDE Menus <ide>
|
||||
|
||||
@@ -30,6 +32,8 @@ For the latest version, always check https://github.com/earlephilhower/arduino-p
|
||||
Digital I/O <digital>
|
||||
EEPROM <eeprom>
|
||||
I2S Audio <i2s>
|
||||
PWM Audio <pwm>
|
||||
Microphone (and Analog Sensor) Input <adc>
|
||||
Serial USB and UARTs <serial>
|
||||
"Software Serial" PIO UART <piouart>
|
||||
Servo <servo>
|
||||
@@ -39,6 +43,10 @@ For the latest version, always check https://github.com/earlephilhower/arduino-p
|
||||
USB (Arduino and Adafruit_TinyUSB) <usb>
|
||||
Multicore Processing <multicore>
|
||||
|
||||
Bluetooth (Alpha/Beta) <bluetooth>
|
||||
|
||||
Single File USB Drive <singlefile>
|
||||
|
||||
FreeRTOS SMP (multicore) <freertos>
|
||||
|
||||
WiFi (Pico-W Support) <wifi>
|
||||
|
||||
+63
-26
@@ -9,14 +9,6 @@ installation.
|
||||
|
||||
Installing via Arduino Boards Manager
|
||||
-------------------------------------
|
||||
**Note for Windows Users**: Please do not use the Windows Store version of
|
||||
the actual Arduino application because it has issues detecting attached Pico
|
||||
boards. Use the "Windows ZIP" or plain "Windows" executable (EXE) download
|
||||
direct from https://arduino.cc. and allow it to install any device drivers
|
||||
it suggests. Otherwise the Pico board may not be detected. Also, if trying
|
||||
out the 2.0 beta Arduino please install the release 1.8 version beforehand
|
||||
to ensure needed device drivers are present.
|
||||
|
||||
1. Open up the Arduino IDE and go to File->Preferences.
|
||||
2. In the dialog that pops up, enter the following URL in the "Additional Boards Manager URLs" field: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
|
||||
|
||||
@@ -28,6 +20,40 @@ to ensure needed device drivers are present.
|
||||
|
||||
.. image:: images/install2.png
|
||||
|
||||
Arduino IDE Installation Warning
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
**Note for Windows Users**: Please do not use the Windows Store version of
|
||||
the actual Arduino application because it has issues detecting attached Pico
|
||||
boards. Use the "Windows ZIP" or plain "Windows" executable (EXE) download
|
||||
direct from https://arduino.cc. and allow it to install any device drivers
|
||||
it suggests. Otherwise the Pico board may not be detected. Also, if trying
|
||||
out the 2.0 beta Arduino please install the release 1.8 version beforehand
|
||||
to ensure needed device drivers are present.
|
||||
|
||||
**Note for Linux Users**: If you installed the Arduino IDE using Flatpak, which
|
||||
is common in Pop!_OS, Fedora, and Mint, among others, you may need to configure
|
||||
Flatpak to allow the IDE access to files outside your home folder. The RP2040
|
||||
device is sometimes mounted as a folder in /opt or /media, which Flatpak will
|
||||
prevent the Arduino IDE from accessing. For Arduino IDE V2, override the filesystem
|
||||
restriction using ``flatpak override --user --filesystem=host cc.arduino.IDE2`` . For
|
||||
For Arduino IDE < V2, use ``flatpak override --user --filesystem=host cc.arduino.arduinoide``.
|
||||
|
||||
Installing via Arduino CLI
|
||||
--------------------------
|
||||
To install using the Arduino command line tool (arduino-cli):
|
||||
|
||||
.. code:: bash
|
||||
|
||||
arduino-cli config add board_manager.additional_urls https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
|
||||
arduino-cli core update-index
|
||||
arduino-cli core install rp2040:rp2040
|
||||
|
||||
To list the supported boards:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
arduino-cli board listall | grep rp2040
|
||||
|
||||
Installing via GIT
|
||||
------------------
|
||||
To install via GIT (for latest and greatest versions):
|
||||
@@ -65,25 +91,14 @@ Them hit the upload button and your sketch should upload and run.
|
||||
In some cases the Pico will encounter a hard hang and its USB port will not respond to the auto-reset request. Should this happen, just
|
||||
follow the initial procedure of holding the BOOTSEL button down while plugging in the Pico to enter the ROM bootloader.
|
||||
|
||||
Unable to Upload First Sketch
|
||||
-----------------------------
|
||||
If the Arduino IDE has never seen a serial port from a working device (Pico, AVR, or any other serial port), you
|
||||
may not be able to install using the prior directions because the ``Tools->Port`` menu will be grayed out and
|
||||
empty. In this case, a special workaround identified by @fjansson in `this issue report <https://github.com/earlephilhower/arduino-pico/issues/688>`_ .
|
||||
|
||||
To allow subsequent uploads to automatically work, you will need to manually install a UF2 binary onto the Raspberry Pi Pico one time to
|
||||
allow it to present a Serial port for the Arduino IDE to detect and save.
|
||||
|
||||
Perform the following steps to program a dummy sketch:
|
||||
|
||||
1. Open a new, empty sketch (doesn't work with a read-only example sketch).
|
||||
2. Compile it by pressing the "Verify" checkmark button.
|
||||
3. Select the ``Sketch -> Export Compiled Binary`` menu. Select a location e.g. the desktop. A folder is created there, containing a file ending in .uf2
|
||||
4. Copy this file to the Pico's drive, by drag and drop in the Explorer.
|
||||
|
||||
The Pico restarts and now now has a serial port. Now the Port menu in Arduino is not gray anymore, select the port there.
|
||||
After this, normal uploading from the Arduino editor should work.
|
||||
Uploading the First Sketch
|
||||
--------------------------
|
||||
The first time you upload a sketch to a board, you'll need to use the built-in ROM bootloader to handle the upload and not a serial port.
|
||||
|
||||
1. Hold the BOOTSEL button while plugging in the board.
|
||||
2. Select ``Tools->Port->UF2 Board`` from the menu.
|
||||
3. Upload as normal.
|
||||
4. After the board boots up, select the new serial port from the ``Tools->Port`` menu.
|
||||
|
||||
Windows 7 Driver Notes
|
||||
----------------------
|
||||
@@ -136,6 +151,28 @@ For detailed usage information, please check the repo documentation available at
|
||||
|
||||
* https://arduino-pico.readthedocs.io/en/latest/fs.html
|
||||
|
||||
Uploading Sketches with Picotool
|
||||
--------------------------------
|
||||
Because the Picotool uses a custom device driver in the Pico to handle upload, when using the ``Upload Method->Picotool`` mode custom code needs to be run on the Pico which is not included by default for compatibility and code savings.
|
||||
|
||||
So for the first sketch you will need to rebuild (with the ``Upload Method->Picotool`` selected in them menus) and then manually hold down BOOTSEL and insert the Pico USB cable to enter the ROM bootloader.
|
||||
|
||||
After the initial upload, as long as the running binary was built using the ``Picotool`` upload method, then the normal upload process should work.
|
||||
|
||||
Under MacOS, it may be necessary to install the USB support libraries from a command terminal before the ``Picotool`` upload method can be used:
|
||||
|
||||
.. code::
|
||||
|
||||
brew install libusb
|
||||
|
||||
For Ubuntu and other Linux operating systems you may need to add the following lines to a new `udev` config file(``99-picotool.rules``) to allow normal users to access the special USB device the Pico exports:
|
||||
|
||||
.. code::
|
||||
|
||||
echo 'SUBSYSTEM=="usb", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="0003", MODE="660", GROUP="plugdev"' | sudo tee -a /etc/udev/rules.d/98-Picotool.rules
|
||||
echo 'SUBSYSTEM=="usb", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="000a", MODE="660", GROUP="plugdev"' | sudo tee -a /etc/udev/rules.d/98-Picotool.rules
|
||||
sudo udevadm control --reload
|
||||
|
||||
Uploading Sketches with Picoprobe
|
||||
---------------------------------
|
||||
If you have built a Raspberry Pi Picoprobe, you can use OpenOCD to handle your sketch uploads and for debugging with GDB.
|
||||
|
||||
+1
-1
@@ -10,5 +10,5 @@ In addition, it contains code from additional open source projects:
|
||||
* The `Pico-SDK <https://github.com/raspberrypi/pico-sdk>`_ and `Pico-Extras <https://github.com/raspberrypi/pico-extras>`_ are by Raspberry Pi (Trading) Ltd. and licensed under the BSD 3-Clause license.
|
||||
* `Arduino-Pico <https://github.com/earlephilhower/arduino-pico>`_ core files are licenses under the LGPL.
|
||||
* `LittleFS <https://github.com/ARMmbed/littlefs>`_ library written by ARM Limited and released under the `BSD 3-clause license <https://github.com/ARMmbed/littlefs/blob/master/LICENSE.md>`_ .
|
||||
* `UF2CONV.PY <https://github.com/microsoft/uf2>`_ is by Microsoft Corporatio and licensed under the MIT license.
|
||||
* `UF2CONV.PY <https://github.com/microsoft/uf2>`_ is by Microsoft Corporation and licensed under the MIT license.
|
||||
* Some filesystem code taken from the `ESP8266 Arduino Core <https://github.com/esp8266/Arduino>`_ and licensed under the LGPL.
|
||||
|
||||
+12
-12
@@ -90,7 +90,7 @@ Signing requires the generation of an RSA-2048 key (other bit lengths are suppor
|
||||
Automatic Signing
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
The simplest way of implementing signing is to use the automatic mode, which presently is only possible on Linux and Mac due to some of the tools not being available for Windows. This mode uses the IDE to configure the source code to enable sigining verification with a given public key, and signs binaries as part of the standard build process using a given public key.
|
||||
The simplest way of implementing signing is to use the automatic mode, which presently is only possible on Linux and Mac due to some of the tools not being available for Windows. This mode uses the IDE to configure the source code to enable signing verification with a given public key, and signs binaries as part of the standard build process using a given public key.
|
||||
|
||||
To enable this mode, just include `private.key` and `public.key` in the sketch `.ino` directory. The IDE will call a helper script (`tools/signing.py`) before the build begins to create a header to enable key validation using the given public key, and to actually do the signing after the build process, generating a `sketch.bin.signed` file. When OTA is enabled (ArduinoOTA, Web, or HTTP), the binary will automatically only accept signed updates.
|
||||
|
||||
@@ -140,11 +140,11 @@ Compile the sketch normally and, once a `.bin` file is available, sign it using
|
||||
Compression
|
||||
-----------
|
||||
|
||||
The eboot bootloader incorporates a GZIP decompressor, built for very low code requirements. For applications, this optional decompression is completely transparent. For uploading compressed filesystems, the application must be built with `ATOMIC_FS_UPDATE` defined because, otherwise, eboot will not be involved in writing the filesystem.
|
||||
The bootloader incorporates a GZIP decompressor, built for very low code requirements. For applications, this optional decompression is completely transparent.
|
||||
|
||||
No changes to the application are required. The `Updater` class and `eboot` bootloader (which performs actual application overwriting on update) automatically search for the `gzip` header in the uploaded binary, and if found, handle it.
|
||||
No changes to the application are required. The `Updater` class and bootloader (which performs actual application overwriting on update) automatically search for the `gzip` header in the uploaded binary, and if found, handle it.
|
||||
|
||||
Compress an application `.bin` file or filesystem package using any `gzip` available, at any desired compression level (`gzip -9` is recommended because it provides the maximum compression and uncompresses as fast as any other compressino level). For example:
|
||||
Compress an application `.bin` file or filesystem package using any `gzip` available, at any desired compression level (`gzip -9` is recommended because it provides the maximum compression and uncompresses as fast as any other compression level). For example:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
@@ -185,11 +185,11 @@ Uploading modules wirelessly from Arduino IDE is intended for the following typi
|
||||
|
||||
- Only if modules are accessible on the same network as the computer with the Arduino IDE.
|
||||
|
||||
- For all IDE uploads,m the Pico W and the computer must be connected to the same network.
|
||||
- For all IDE uploads, the Pico W and the computer must be connected to the same network.
|
||||
|
||||
To upload wirelessly from the IDE:
|
||||
|
||||
1. Build a sketch starts ``WiFi`` and includes the appropriare calls to ``ArduinoOTA`` (see the examples for reference). These include the ``ArduinoOTA.begin()`` call in ``setup()`` and periodically calling ``ArduinoOTA.handle();`` from the ``loop()``
|
||||
1. Build a sketch starts ``WiFi`` and includes the appropriate calls to ``ArduinoOTA`` (see the examples for reference). These include the ``ArduinoOTA.begin()`` call in ``setup()`` and periodically calling ``ArduinoOTA.handle();`` from the ``loop()``
|
||||
|
||||
2. Upload using standard USB connection the first time.
|
||||
|
||||
@@ -200,7 +200,7 @@ To upload wirelessly from the IDE:
|
||||
Password Protection
|
||||
-------------------
|
||||
|
||||
Protecting your OTA uploads with password is really straightforward. All you need to do, is to include the following statement in your code:
|
||||
Protecting your OTA uploads with a password is really straightforward. All you need to do, is to include the following statement in your code:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
@@ -208,11 +208,11 @@ Protecting your OTA uploads with password is really straightforward. All you nee
|
||||
|
||||
Where ``123`` is a sample password that you should replace with your own.
|
||||
|
||||
Before implementing it in your sketch, it is a good idea to check how it works using *BasicOTA.ino* sketch available under *File > Examples > ArduinoOTA*. Go ahead, open *BasicOTA.ino*, uncomment the above statement that is already there, and upload the sketch. To make troubleshooting easier, do not modify example sketch besides what is absolutely required. This is including original simple ``123`` OTA password. Then attempt to upload sketch again (using OTA). After compilation is complete, once upload is about to begin, you should see prompt for password.
|
||||
Before implementing it in your sketch, it is a good idea to check how it works using *BasicOTA.ino* sketch available under *File > Examples > ArduinoOTA*. Go ahead, open *BasicOTA.ino*, uncomment the above statement that is already there, and upload the sketch. To make troubleshooting easier, do not modify example sketch besides what is absolutely required. This includes the original simple ``123`` OTA password. Then attempt to upload a sketch again (using OTA). After compilation is complete, once upload is about to begin, you should see a prompt for password.
|
||||
|
||||
Enter the password and upload should be initiated as usual with the only difference being ``Authenticating...OK`` message visible in upload log.
|
||||
Enter the password and upload should be initiated as usual with the only difference being ``Authenticating...OK`` message visible in the upload log.
|
||||
|
||||
You will not be prompted for a reentering the same password next time. Arduino IDE will remember it for you. You will see prompt for password only after reopening IDE, or if you change it in your sketch, upload the sketch and then try to upload it again.
|
||||
You will not be prompted for a reentering the same password next time. Arduino IDE will remember it for you. You will see a prompt for password only after reopening IDE, or if you change it in your sketch, upload the sketch and then try to upload it again.
|
||||
|
||||
Please note, it is possible to reveal password entered previously in Arduino IDE, if IDE has not been closed since last upload. This can be done by enabling *Show verbose output during: upload* in *File > Preferences* and attempting to upload the module.
|
||||
|
||||
@@ -289,7 +289,7 @@ Simple updater downloads the file every time the function is called.
|
||||
Advanced updater
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
Its possible to point the update function to a script on the server. If a version string argument is given, it will be sent to the server. The server side script can use this string to check whether an update should be performed.
|
||||
It's possible to point the update function to a script on the server. If a version string argument is given, it will be sent to the server. The server side script can use this string to check whether an update should be performed.
|
||||
|
||||
The server-side script can respond as follows: - response code 200, and send the firmware image, - or response code 304 to notify Pico that no update is required.
|
||||
|
||||
@@ -305,7 +305,7 @@ The server-side script can respond as follows: - response code 200, and send the
|
||||
Serial.println("[update] Update no Update.");
|
||||
break;
|
||||
case HTTP_UPDATE_OK:
|
||||
Serial.println("[update] Update ok."); // may not be called since we reboot the RP2040
|
||||
Serial.println("[update] Update ok.");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
+85
-1
@@ -23,6 +23,46 @@ Especially useful is the `Getting started with VSCode + PlatformIO <https://docs
|
||||
|
||||
Hereafter it is assumed that you have a basic understanding of PlatformIO in regards to project creation, project file structure and building and uploading PlatformIO projects, through reading the above pages.
|
||||
|
||||
Important steps for Windows users, before installing
|
||||
----------------------------------------------------
|
||||
|
||||
By default, Windows has a limited path length that is not long enough to fully clone the ``Pico-SDK``'s ``tinyusb`` repository, resulting in error messages like the one below while attempting to fetch the repository.
|
||||
|
||||
.. code::
|
||||
|
||||
error: unable to create file '.....' : Filename too long
|
||||
|
||||
To work around this requires performing two steps and rebooting Windows once. These steps will enable longer file paths at the Windows OS and the ``git`` level.
|
||||
|
||||
Step 1: Enabling long paths in git
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Open up a Windows ``cmd`` or ``terminal`` window and execute the following command
|
||||
|
||||
.. code::
|
||||
|
||||
git config --system core.longpaths true
|
||||
|
||||
Step 2: Enabling long paths in the Windows OS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
(taken from https://www.microfocus.com/documentation/filr/filr-4/filr-desktop/t47bx2ogpfz7.html)
|
||||
|
||||
1. Click Window key and type gpedit.msc, then press the Enter key. This launches the Local Group Policy Editor.
|
||||
|
||||
2. Navigate to Local Computer Policy > Computer Configuration > Administrative Templates > System > Filesystem.
|
||||
|
||||
3. Double click Enable NTFS/Win32 long paths and close the dialog.
|
||||
|
||||
.. image:: images/longpath.png
|
||||
|
||||
|
||||
Step 3: Reboot the computer
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Once the two prior stages are complete, please do a full reboot or power cycle so that the new settings will take effect.
|
||||
|
||||
|
||||
Current state of development
|
||||
----------------------------
|
||||
|
||||
@@ -234,6 +274,17 @@ The lwIP stack can be configured to support only IPv4 (default) or additionally
|
||||
|
||||
to the ``platformio.ini``.
|
||||
|
||||
Bluetooth Stack
|
||||
---------------
|
||||
|
||||
The Bluetooth Classic (BTC) and Bluetooth Low Energy (BLE) stack can be activated by adding
|
||||
|
||||
.. code:: ini
|
||||
|
||||
; BTC and BLE
|
||||
build_flags = -DPIO_FRAMEWORK_ARDUINO_ENABLE_BLUETOOTH
|
||||
|
||||
to the ``platformio.ini``.
|
||||
|
||||
Selecting a different core version
|
||||
----------------------------------
|
||||
@@ -290,10 +341,15 @@ To specify the debugging adapter, use ``debug_tool`` (`documentation <https://do
|
||||
* ``cmsis-dap``
|
||||
* ``jlink``
|
||||
* ``raspberrypi-swd``
|
||||
* ``blackmagic``
|
||||
* ``pico-debug``
|
||||
|
||||
These values can also be used in ``upload_protocol`` if you want PlatformIO to upload the regular firmware through this method, which you likely want.
|
||||
|
||||
Especially the PicoProbe method is convenient when you have two Raspberry Pi Pico boards. One of them can be flashed with the PicoProbe firmware (`documentation <https://www.raspberrypi.com/documentation/microcontrollers/raspberry-pi-pico.html#debugging-using-another-raspberry-pi-pico>`_) and is then connected to the target Raspberry Pi Pico board (see `documentation <https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf>`_ chapter "Picoprobe Wiring"). Remember that on Windows, you have to use `Zadig <https://zadig.akeo.ie/>`_ to also load "WinUSB" drivers for the "Picoprobe (Interface 2)" device so that OpenOCD can speak to it.
|
||||
Especially the PicoProbe method is convenient when you have two Raspberry Pi Pico boards. One of them can be flashed with the PicoProbe firmware (`documentation <https://www.raspberrypi.com/documentation/microcontrollers/raspberry-pi-pico.html#debugging-using-another-raspberry-pi-pico>`__) and is then connected to the target Raspberry Pi Pico board (see `documentation <https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf>`__ chapter "Picoprobe Wiring"). Remember that on Windows, you have to use `Zadig <https://zadig.akeo.ie/>`_ to also load "WinUSB" drivers for the "Picoprobe (Interface 2)" device so that OpenOCD can speak to it.
|
||||
|
||||
.. note::
|
||||
Newer PicoProbe firmware versions have dropped the proprietary "PicoProbe" USB communication protocol and emulate a **CMSIS-DAP** instead. Meaning, you have to use ``debug_tool = cmsis-dap`` for these newer firmwares, such as those obtained from `raspberrypi/picoprobe <https://github.com/raspberrypi/picoprobe/releases>`__
|
||||
|
||||
With that set up, debugging can be started via the left debugging sidebar and works nicely: Setup breakpoints, inspect the value of variables in the code, step through the code line by line. When a breakpoint is hit or execution is halted, you can even see the execution state both Cortex-M0+ cores of the RP2040.
|
||||
|
||||
@@ -301,6 +357,30 @@ With that set up, debugging can be started via the left debugging sidebar and wo
|
||||
|
||||
For further information on customizing debug options, like the initial breakpoint or debugging / SWD speed, consult `the documentation <https://docs.platformio.org/en/latest/projectconf/section_env_debug.html>`_.
|
||||
|
||||
.. note::
|
||||
For the BlackMagicProbe debugging probe (as can be e.g., created by simply flashing a STM32F103C8 "Bluepill" board), you currently have to use the branch ``fix/rp2040-flash-reliability`` (or at least commit ``1d001bc``) **and** use the `official ARM provided toolchain <https://github.com/blackmagic-debug/blackmagic/issues/1364#issuecomment-1503393266>`_.
|
||||
|
||||
You can obtain precompiled binaries from `here <https://github.com/blackmagic-debug/blackmagic/issues/1364#issuecomment-1503372723>`__. A flashing guide is available `here <https://primalcortex.wordpress.com/2017/06/13/building-a-black-magic-debug-probe/>`__. You then have to configure the target serial port ("GDB port") in your project per `documentation <https://docs.platformio.org/en/latest/plus/debug-tools/blackmagic.html#debugging-tool-blackmagic>`__.
|
||||
|
||||
.. note::
|
||||
For the pico-debug (`download <https://github.com/majbthrd/pico-debug/releases>`__) debugging way, *which needs to no additional debug probe*, add this snippet to your ``platformio.ini`` and follow the given procedure:
|
||||
|
||||
.. code:: ini
|
||||
|
||||
upload_protocol = pico-debug
|
||||
debug_tool = pico-debug
|
||||
build_flags = -DPIO_FRAMEWORK_ARDUINO_NO_USB
|
||||
|
||||
1. Build your firmware normally
|
||||
2. Plug in the Pico in BOOTSEL mode
|
||||
3. Drag and drop your ``.pio/build/<env>/firmware.uf2`` onto the boot drive
|
||||
4. Unplug and replug your Pico back into BOOTSEL mode for the second time
|
||||
5. Drag and drop the downloaded ``pico-debug-gimmecache.uf2`` file onto the boot drive
|
||||
6. A CMSIS-DAP device should now appear on your computer
|
||||
7. Start debugging via the debug sidebar as normal
|
||||
|
||||
Note the restrictions: The second core cannot be used, the USB port cannot be used (no USB serial, only UART serial), 16KB less RAM is available.
|
||||
|
||||
Filesystem Uploading
|
||||
--------------------
|
||||
|
||||
@@ -315,3 +395,7 @@ The files you want to upload should be placed in a folder called ``data`` inside
|
||||
The task "Build Filesystem Image" will take all files in the data directory and create a ``littlefs.bin`` file from it using the ``mklittlefs`` tool.
|
||||
|
||||
The task "Upload Filesystem Image" will upload the filesystem image to the Pico via the specified ``upload_protocol``.
|
||||
|
||||
.. note::
|
||||
Set the space available for the filesystem in the ``platformio.ini`` using e.g., ``board_build.filesystem_size = 0.5m``, or filesystem
|
||||
creation will fail!
|
||||
@@ -0,0 +1,97 @@
|
||||
PWM Audio Library
|
||||
=================
|
||||
|
||||
Relatively good quality analog audio out can be generated by using the
|
||||
RP2040 onboard PWM hardware. It can drive an amplifier for speaker output,
|
||||
or be decoupled using a capacitor to drive a headphone-level signal. Mono
|
||||
and stereo signals can be generated.
|
||||
|
||||
All samples are sent to the ``PWMAudio`` library as **signed 16 bits per sample**.
|
||||
Due to frequency limitations of the PWM hardware, at higher bit rates
|
||||
these 16-bits will automatically be reduced to the maximum the hardware
|
||||
can handle.
|
||||
|
||||
Multiple ``PWMAudio`` devices are supported, depending on availability of
|
||||
DMA channels.
|
||||
|
||||
The interface for the ``PWMAudio`` device is very similar to the ``I2S``
|
||||
device, and most code can be ported simply by instantiating a ``PWMAudio``
|
||||
object in lieu of an ``I2S`` object.
|
||||
|
||||
PWM Class API
|
||||
-------------
|
||||
|
||||
PWMAudio(pin)
|
||||
~~~~~~~~~~~~~
|
||||
Creates a mono PWM output port. Any pin can be used, but no ``analogWrite``
|
||||
calls are allowed to any other pins using that pin's PWM slice hardware.
|
||||
See the RP2040 datasheet for more details about PWM slices.
|
||||
|
||||
PWMAudio(pin, true)
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
Creates a stereo PWM output port. Only even pins (left signal) can be used, the next odd
|
||||
pin will automatically be assigned to the right channel (i.e. ``PWMAudio pwm(0, true);``
|
||||
will make GP0 as the left channel, GP1 as the right channel). The same restriction as
|
||||
in mono mode applies.
|
||||
|
||||
bool setBuffers(size_t buffers, size_t bufferWords)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Set the number of DMA buffers and their size in 32-bit words.
|
||||
Call before ``PWMAudio::begin()``.
|
||||
|
||||
When running at high sample rates, it is recommended to increase the
|
||||
``bufferWords`` to 32 or higher (i.e. ``pwm.setBuffers(4, 32);`` ).
|
||||
|
||||
bool setPin(pin_size_t pin)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Adjusts the pin to connect to the PWM audio output. Only legal before
|
||||
``PWMAudio::begin()``.
|
||||
|
||||
bool setStereo(bool stereo)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Adjusts the mono/stereo setting of the PWM audio output. Only legal before
|
||||
``PWMAudio::begin()``.
|
||||
|
||||
bool setFrequency(long sampleRate)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Sets the sample frequency, but does not start the PWM device (however if the
|
||||
device was already running, it will wontinue to run at the new frequency).
|
||||
|
||||
bool begin()/begin(long sampleRate)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Start the PWM Audio device up with the given sample rate, or with the value set
|
||||
using the prior ``setFrequency`` call.
|
||||
|
||||
void end()
|
||||
~~~~~~~~~~
|
||||
Stops the PWMAudio device.
|
||||
|
||||
void flush()
|
||||
~~~~~~~~~~~~
|
||||
Waits until all the PWM Audio buffers have been output.
|
||||
|
||||
size_t write(int16_t sample, bool sync = true)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Writes a single 16-bit sample to the buffer. It is up to the user to keep track
|
||||
of left/right channels when in stereo mode. In mono mode, one sample is written
|
||||
per timestep while in stereo mode two ``write()`` calls are required.
|
||||
|
||||
This call will block (wait) until space is available to actually write
|
||||
the data if ``sync`` is not specified or set to ``true``.
|
||||
|
||||
size_t write(const uint8_t \*buffer, size_t size)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Transfers number of bytes from an application buffer to the PWM Audio output buffer.
|
||||
Be aware that ``size`` is in *bytes** and not samples. Size must be a multiple
|
||||
of **4 bytes**. Will not block, so check the return value to find out how
|
||||
many bytes were actually written.
|
||||
|
||||
int availableForWrite()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns the number of samples that can be written without potentially blocking.
|
||||
|
||||
void onTransmit(void (\*fn)(void))
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Sets a callback to be called when a PWM Audio DMA buffer is fully transmitted.
|
||||
Will be in an interrupt context so the specified function must operate
|
||||
quickly and not use blocking calls like delay() or write to the PWM Audio.
|
||||
+33
-4
@@ -13,6 +13,10 @@ Returns the current frequency of the core clock. This is read at runtime,
|
||||
versus the constant ``F_CPU`` macro that is also available. This is useful
|
||||
in cases where your code changes the core clock (i.e. low power modes, etc.)
|
||||
|
||||
int rp2040.cpuid()
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
Returns the current core ID (0 or 1) of the executing task.
|
||||
|
||||
uint32_t rp2040.getCycleCount()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns a 32-bit cycle count from then the core started running. Because it
|
||||
@@ -38,16 +42,19 @@ void rp2040.reboot()
|
||||
Forces a hardware reboot of the Pico.
|
||||
|
||||
Hardware Watchdog
|
||||
_________________
|
||||
-----------------
|
||||
|
||||
void rp2040.wdt_begin(uint32_t delay_ms)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Enables the hardware watchdog timer with a delay value of delay_ms
|
||||
microseconds. Note that on the RP2040, once this function has called, the
|
||||
milliseconds. Note that on the RP2040, once this function has called, the
|
||||
hardware watchdog can _not_ be disabled.
|
||||
|
||||
The maximum ``delay_ms`` allowed in this call is ``8300``, corresponding
|
||||
to 8.3 seconds. Any higher values will be truncated by the hardware.
|
||||
|
||||
void rp2040.wdt_reset()
|
||||
~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Reloads the watchdog's counter with the amount of time set by wdt_begin.
|
||||
|
||||
|
||||
@@ -70,3 +77,25 @@ int rp2040.getTotalHeap()
|
||||
Returns the total heap that was available to this program at compile time (i.e.
|
||||
the Pico RAM size minus things like the ``.data`` and ``.bss`` sections and other
|
||||
overhead).
|
||||
|
||||
Hardware Identification
|
||||
-----------------------
|
||||
|
||||
bool rp2040.isPicoW()
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns the core's best guess if this code is running on a Raspberry Pi Pico W.
|
||||
This would let you, possibly, use the same UF2 for Pico and PicoW by simply not
|
||||
doing any WiFi calls.
|
||||
|
||||
Bootloader
|
||||
----------
|
||||
|
||||
void rp2040.enableDoubleResetBootloader()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
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.
|
||||
|
||||
void rp2040.rebootToBootloader()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Will reboot the RP2040 into USB UF2 upload mode.
|
||||
|
||||
@@ -45,3 +45,13 @@ For applications where an IRQ driven serial port is not appropriate, use
|
||||
|
||||
For detailed information about the Serial ports, see the
|
||||
Arduino `Serial Reference <https://www.arduino.cc/reference/en/language/functions/communication/serial/>`_ .
|
||||
|
||||
|
||||
RP2040 Specific SerialUSB methods
|
||||
---------------------------------
|
||||
|
||||
void Serial.ignoreFlowControl(bool ignore)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
In some cases, the target application will not assert the DTR virtual line, thus preventing writing operations to succeed.
|
||||
|
||||
For this reason, the SerialUSB::ignoreFlowControl() method disables the connection's state verification, enabling the program to write on the port, even though the data might be lost.
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
SingleFileDrive
|
||||
===============
|
||||
|
||||
USB drive mode is supported through the ``SingleFileDrive`` class which
|
||||
allows the Pico to emulate a FAT-formatted USB stick while preserving the
|
||||
onboard LittleFS filesystem. A single file can be exported this way without
|
||||
needing to use FAT as the onboard filesystem (FAT is not appropriate for
|
||||
flash-based devices without complicated wear leveling because of the update
|
||||
frequency of the FAT tables).
|
||||
|
||||
This emulation is very simple and only allows for the reading of the single
|
||||
file, and deleting it.
|
||||
|
||||
Callbacks, Interrupt Safety, and File Operations
|
||||
------------------------------------------------
|
||||
|
||||
The ``SingleFileDrive`` library allows your application to get a callback
|
||||
when a PC attempts to mount or unmount the Pico as a drive. Your app can
|
||||
also get a callback if the user attempts to delete the file (but your
|
||||
sketch does not actually need to delete the file, it's up to you).
|
||||
|
||||
Note that when the USB drive is mounted by a PC it is not safe for your
|
||||
main sketch to make changes to the LittleFS filesystem or the file being
|
||||
exported. So, normally, your ``onPlug`` callback will set a flag letting
|
||||
your application know not to touch the filesystem, with the ``onUnplug``
|
||||
callback clearing this flag.
|
||||
|
||||
Also, because the USB port can be connected at any time, it is important
|
||||
to disable interrupts using ``noInterrupts()`` before writing to a file
|
||||
you will be exporting (and restoring them with ``interrupts()`` afterwards).
|
||||
It is also important to ``close()`` the file after each update, or the
|
||||
on-flash version the ``SingleFileDrive`` will attempt to export may not be
|
||||
up to date causing issues later on.
|
||||
|
||||
See the included ``DataLoggerUSB`` sketch for an example of working with
|
||||
these limitations.
|
||||
|
||||
Using SingleFileDrive
|
||||
---------------------
|
||||
|
||||
Implementing the drive requires including the header file, starting LittleFS,
|
||||
defining your callbacks, and telling the library what file to export. No
|
||||
polling or other calls are required outside of your ``setup()``. (Note that
|
||||
the callback routines allow for a parameter to be passed to them, but in most
|
||||
cases this can be safely ignored.)
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
#include <LittleFS.h>
|
||||
#include <SingleFileDrive.h>
|
||||
|
||||
void myPlugCB(uint32_t data) {
|
||||
// Tell my app not to write to flash, we're connected
|
||||
}
|
||||
|
||||
void myUnplugCB(uint32_t data) {
|
||||
// I can start writing to flash again
|
||||
}
|
||||
|
||||
void myDeleteDB(uint32_t data) {
|
||||
// Maybe LittleFS.remove("myfile.txt")? or do nothing
|
||||
}
|
||||
|
||||
void setup() {
|
||||
LittleFS.begin();
|
||||
singleFileDrive.onPlug(myPlugCB);
|
||||
singleFileDrive.onUnplug(myUnplugCB);
|
||||
singleFileDrive.onDelete(myDeleteCB);
|
||||
singleFileDrive.begin("littlefsfile.csv", "Data Recorder.csv");
|
||||
// ... rest of setup ...
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Take some measurements, delay, etc.
|
||||
if (okay-to-write) {
|
||||
noInterrupts();
|
||||
File f = LittleFS.open("littlefsfile.csv", "a");
|
||||
f.printf("%d,%d,%d\n", data1, data2, data3);
|
||||
f.close();
|
||||
interrupts();
|
||||
}
|
||||
}
|
||||
+2
-4
@@ -40,11 +40,9 @@ Please note that WiFi on the Pico W is a work-in-progress and there are some imp
|
||||
|
||||
* Combined STA/AP mode is not supported
|
||||
|
||||
* Certain WiFi status values (RSSI, BSSID, etc.) are not available.
|
||||
* Multicore is supported, but only core 0 may run ``WiFi`` related code.
|
||||
|
||||
* Multicore is supported, but only one core may run ``WiFi`` code.
|
||||
|
||||
* FreeRTOS is not yet supported due to the requirement for a very different LWIP implementation. PRs always appreciated!
|
||||
* 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!
|
||||
|
||||
The WiFi library borrows much work from the `ESP8266 Arduino Core <https://github.com/esp8266/Arduino>`__ , especially the ``WiFiClient`` and ``WiFiServer`` classes.
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
// BTstack features that can be enabled
|
||||
#define ENABLE_LOG_INFO
|
||||
#define ENABLE_LOG_ERROR
|
||||
#define ENABLE_PRINTF_HEXDUMP
|
||||
#define ENABLE_SCO_OVER_HCI
|
||||
|
||||
#ifdef ENABLE_CLASSIC
|
||||
#define ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_BLE
|
||||
#define ENABLE_L2CAP_LE_CREDIT_BASED_FLOW_CONTROL_MODE
|
||||
#define ENABLE_LE_PERIPHERAL
|
||||
#define ENABLE_LE_CENTRAL
|
||||
#endif
|
||||
|
||||
// BTstack configuration. buffers, sizes, ...
|
||||
#define HCI_OUTGOING_PRE_BUFFER_SIZE 4
|
||||
#define HCI_ACL_PAYLOAD_SIZE (1691 + 4)
|
||||
#define HCI_ACL_CHUNK_SIZE_ALIGNMENT 4
|
||||
#define MAX_NR_AVDTP_CONNECTIONS 1
|
||||
#define MAX_NR_AVDTP_STREAM_ENDPOINTS 1
|
||||
#define MAX_NR_AVRCP_CONNECTIONS 2
|
||||
#define MAX_NR_BNEP_CHANNELS 1
|
||||
#define MAX_NR_BNEP_SERVICES 1
|
||||
#define MAX_NR_BTSTACK_LINK_KEY_DB_MEMORY_ENTRIES 2
|
||||
#define MAX_NR_GATT_CLIENTS 1
|
||||
#define MAX_NR_HCI_CONNECTIONS 2
|
||||
#define MAX_NR_HID_HOST_CONNECTIONS 1
|
||||
#define MAX_NR_HIDS_CLIENTS 1
|
||||
#define MAX_NR_HFP_CONNECTIONS 1
|
||||
#define MAX_NR_L2CAP_CHANNELS 4
|
||||
#define MAX_NR_L2CAP_SERVICES 3
|
||||
#define MAX_NR_RFCOMM_CHANNELS 1
|
||||
#define MAX_NR_RFCOMM_MULTIPLEXERS 1
|
||||
#define MAX_NR_RFCOMM_SERVICES 1
|
||||
#define MAX_NR_SERVICE_RECORD_ITEMS 4
|
||||
#define MAX_NR_SM_LOOKUP_ENTRIES 3
|
||||
#define MAX_NR_WHITELIST_ENTRIES 16
|
||||
#define MAX_NR_LE_DEVICE_DB_ENTRIES 16
|
||||
|
||||
// Limit number of ACL/SCO Buffer to use by stack to avoid cyw43 shared bus overrun
|
||||
#define MAX_NR_CONTROLLER_ACL_BUFFERS 3
|
||||
#define MAX_NR_CONTROLLER_SCO_PACKETS 3
|
||||
|
||||
// Enable and configure HCI Controller to Host Flow Control to avoid cyw43 shared bus overrun
|
||||
#define ENABLE_HCI_CONTROLLER_TO_HOST_FLOW_CONTROL
|
||||
#define HCI_HOST_ACL_PACKET_LEN 1024
|
||||
#define HCI_HOST_ACL_PACKET_NUM 3
|
||||
#define HCI_HOST_SCO_PACKET_LEN 120
|
||||
#define HCI_HOST_SCO_PACKET_NUM 3
|
||||
|
||||
// Link Key DB and LE Device DB using TLV on top of Flash Sector interface
|
||||
#define NVM_NUM_DEVICE_DB_ENTRIES 16
|
||||
#define NVM_NUM_LINK_KEYS 16
|
||||
|
||||
// We don't give btstack a malloc, so use a fixed-size ATT DB.
|
||||
#define MAX_ATT_DB_SIZE 512
|
||||
|
||||
// BTstack HAL configuration
|
||||
#define HAVE_EMBEDDED_TIME_MS
|
||||
|
||||
// map btstack_assert onto Pico SDK assert()
|
||||
#define HAVE_ASSERT
|
||||
|
||||
// Some USB dongles take longer to respond to HCI reset (e.g. BCM20702A).
|
||||
#define HCI_RESET_RESEND_TIMEOUT_MS 1000
|
||||
|
||||
#define ENABLE_SOFTWARE_AES128
|
||||
#define ENABLE_MICRO_ECC_FOR_LE_SECURE_CONNECTIONS
|
||||
|
||||
#undef HAVE_BTSTACK_STDIN
|
||||
+7
-1
@@ -13,6 +13,9 @@ extern void interrupts();
|
||||
#define SYS_ARCH_PROTECT(lev) noInterrupts
|
||||
#define SYS_ARCH_UNPROTECT(lev) interrupts
|
||||
|
||||
extern unsigned long get_rand_32(void);
|
||||
#define LWIP_RAND() get_rand_32()
|
||||
|
||||
// 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)
|
||||
|
||||
@@ -25,7 +28,7 @@ extern void interrupts();
|
||||
#define MEMP_NUM_TCP_SEG 32
|
||||
#define MEMP_NUM_ARP_QUEUE 10
|
||||
#define PBUF_POOL_SIZE 24
|
||||
#define LWIP_ARP 1
|
||||
#define LWIP_ARP 2
|
||||
#define LWIP_ETHERNET 1
|
||||
#define LWIP_ICMP 1
|
||||
#define LWIP_RAW 1
|
||||
@@ -53,6 +56,9 @@ extern void interrupts();
|
||||
#define DHCP_DOES_ARP_CHECK 0
|
||||
#define LWIP_DHCP_DOES_ACD_CHECK 0
|
||||
|
||||
// See #1285
|
||||
#define MEMP_NUM_UDP_PCB 6
|
||||
|
||||
#if LWIP_IPV6
|
||||
#define LWIP_IPV6_DHCP6 1
|
||||
#endif
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
#define _PICO_VERSION_H
|
||||
|
||||
#define PICO_SDK_VERSION_MAJOR 1
|
||||
#define PICO_SDK_VERSION_MINOR 4
|
||||
#define PICO_SDK_VERSION_REVISION 0
|
||||
#define PICO_SDK_VERSION_STRING "1.4.0"
|
||||
#define PICO_SDK_VERSION_MINOR 5
|
||||
#define PICO_SDK_VERSION_REVISION 1
|
||||
#define PICO_SDK_VERSION_STRING "1.5.1"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -72,15 +72,14 @@
|
||||
//------------- CLASS -------------//
|
||||
#define CFG_TUD_HID (2)
|
||||
#define CFG_TUD_CDC (1)
|
||||
#define CFG_TUD_MSC (0)
|
||||
#define CFG_TUD_MSC (1)
|
||||
#define CFG_TUD_MIDI (0)
|
||||
#define CFG_TUD_VENDOR (0)
|
||||
|
||||
#define CFG_TUD_CDC_RX_BUFSIZE (256)
|
||||
#define CFG_TUD_CDC_TX_BUFSIZE (256)
|
||||
|
||||
#define CFG_TUD_MIDI_RX_BUFSIZE (64)
|
||||
#define CFG_TUD_MIDI_TX_BUFSIZE (64)
|
||||
#define CFG_TUD_MSC_EP_BUFSIZE (64)
|
||||
|
||||
// HID buffer size Should be sufficient to hold ID (if any) + Data
|
||||
#define CFG_TUD_HID_EP_BUFSIZE (64)
|
||||
|
||||
@@ -44,6 +44,8 @@ restartCore1 KEYWORD2
|
||||
reboot KEYWORD2
|
||||
restart KEYWORD2
|
||||
|
||||
isPicoW KEYWORD2
|
||||
|
||||
getChipID KEYWORD2
|
||||
|
||||
hwrand32 KEYWORD2
|
||||
@@ -54,6 +56,11 @@ SerialPIO KEYWORD2
|
||||
setFIFOSize KEYWORD2
|
||||
setPollingMode KEYWORD2
|
||||
|
||||
digitalWriteFast KEYWORD2
|
||||
digitalReadFast KEYWORD2
|
||||
|
||||
enableDoubleResetBootloader KEYWORD2
|
||||
|
||||
OUTPUT_2MA LITERAL1
|
||||
OUTPUT_4MA LITERAL1
|
||||
OUTPUT_8MA LITERAL1
|
||||
|
||||
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.
+13
-7
@@ -94,7 +94,9 @@ SECTIONS
|
||||
/* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from
|
||||
* FLASH ... we will include any thing excluded here in .data below by default */
|
||||
*(.init)
|
||||
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*)
|
||||
/* Some of these excludes required for PicoDVI library, won't affect most code */
|
||||
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a: *interp.c.obj *divider.S.obj *PicoDVI.cpp.o *dvi.c.o) .text*)
|
||||
|
||||
*(.fini)
|
||||
/* Pull all c'tors into .text */
|
||||
*crtbegin.o(.ctors)
|
||||
@@ -191,11 +193,6 @@ SECTIONS
|
||||
__data_end__ = .;
|
||||
} > RAM AT> FLASH
|
||||
|
||||
.uninitialized_data (COPY): {
|
||||
. = ALIGN(4);
|
||||
*(.uninitialized_data*)
|
||||
} > RAM
|
||||
|
||||
/* Start and end symbols must be word-aligned */
|
||||
.scratch_x : {
|
||||
__scratch_x_start__ = .;
|
||||
@@ -213,7 +210,7 @@ SECTIONS
|
||||
} > SCRATCH_Y AT > FLASH
|
||||
__scratch_y_source__ = LOADADDR(.scratch_y);
|
||||
|
||||
.bss : {
|
||||
.bss : {
|
||||
. = ALIGN(4);
|
||||
__bss_start__ = .;
|
||||
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
|
||||
@@ -222,6 +219,15 @@ SECTIONS
|
||||
__bss_end__ = .;
|
||||
} > RAM
|
||||
|
||||
/* At most one of the following two will be engaged, depending on the SDK version */
|
||||
.uninitialized_ram MAX(0x20003000, .) (NOLOAD) : {
|
||||
*(.uninitialized_ram*)
|
||||
} > RAM
|
||||
|
||||
.uninitialized_data MAX(0x20003000, .) (NOLOAD) : {
|
||||
*(.uninitialized_data*)
|
||||
} > RAM
|
||||
|
||||
.heap (COPY):
|
||||
{
|
||||
__end__ = .;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
source [find board/pico-debug.cfg]
|
||||
@@ -0,0 +1,3 @@
|
||||
source [find interface/cmsis-dap.cfg]
|
||||
adapter speed 5000
|
||||
source [find target/rp2040.cfg]
|
||||
@@ -9,6 +9,7 @@
|
||||
-iwithprefixbefore/pico-sdk/src/common/pico_stdlib/include
|
||||
-iwithprefixbefore/pico-sdk/src/common/pico_sync/include
|
||||
-iwithprefixbefore/pico-sdk/src/common/pico_time/include
|
||||
-iwithprefixbefore/pico-sdk/src/common/pico_usb_reset_interface/include
|
||||
-iwithprefixbefore/pico-sdk/src/common/pico_util/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2040/hardware_regs/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2040/hardware_structs/include
|
||||
@@ -39,8 +40,11 @@
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_vreg/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_watchdog/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_xosc/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_async_context/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_bootrom/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/src/rp2_common/pico_double/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_float/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_int64_ops/include
|
||||
@@ -49,8 +53,11 @@
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_platform/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_printf/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_runtime/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_rand/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_stdio/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_stdio_uart/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_unique_id/include
|
||||
-iwithprefixbefore/pico-sdk/lib/cyw43-driver/src
|
||||
-iwithprefixbefore/pico-sdk/lib/lwip/src/include
|
||||
-iwithprefixbefore/pico-sdk/lib/btstack/src
|
||||
-iwithprefixbefore/pico-sdk/lib/btstack/platform/embedded
|
||||
|
||||
@@ -144,4 +144,70 @@
|
||||
-Wl,--wrap=tanhf
|
||||
-Wl,--wrap=trunc
|
||||
-Wl,--wrap=truncf
|
||||
|
||||
-Wl,--wrap=__getreent
|
||||
|
||||
-Wl,--wrap=malloc
|
||||
-Wl,--wrap=calloc
|
||||
-Wl,--wrap=realloc
|
||||
-Wl,--wrap=free
|
||||
|
||||
-Wl,--wrap=pbuf_header
|
||||
-Wl,--wrap=pbuf_free
|
||||
-Wl,--wrap=pbuf_alloc
|
||||
-Wl,--wrap=pbuf_take
|
||||
-Wl,--wrap=pbuf_copy_partial
|
||||
-Wl,--wrap=pbuf_ref
|
||||
-Wl,--wrap=pbuf_get_at
|
||||
-Wl,--wrap=pbuf_get_contiguous
|
||||
-Wl,--wrap=pbuf_cat
|
||||
|
||||
-Wl,--wrap=tcp_arg
|
||||
-Wl,--wrap=tcp_new
|
||||
-Wl,--wrap=tcp_listen
|
||||
-Wl,--wrap=tcp_listen_with_backlog
|
||||
-Wl,--wrap=tcp_accept
|
||||
-Wl,--wrap=tcp_connect
|
||||
-Wl,--wrap=tcp_write
|
||||
-Wl,--wrap=tcp_sent
|
||||
-Wl,--wrap=tcp_recv
|
||||
-Wl,--wrap=tcp_recved
|
||||
-Wl,--wrap=tcp_poll
|
||||
-Wl,--wrap=tcp_close
|
||||
-Wl,--wrap=tcp_abort
|
||||
-Wl,--wrap=tcp_err
|
||||
-Wl,--wrap=tcp_output
|
||||
-Wl,--wrap=tcp_setprio
|
||||
-Wl,--wrap=tcp_backlog_delayed
|
||||
-Wl,--wrap=tcp_backlog_accepted
|
||||
|
||||
-Wl,--wrap=udp_new
|
||||
-Wl,--wrap=udp_remove
|
||||
-Wl,--wrap=udp_bind
|
||||
-Wl,--wrap=udp_connect
|
||||
-Wl,--wrap=udp_disconnect
|
||||
-Wl,--wrap=udp_send
|
||||
-Wl,--wrap=udp_recv
|
||||
-Wl,--wrap=udp_sendto_if
|
||||
|
||||
-Wl,--wrap=sys_check_timeouts
|
||||
|
||||
-Wl,--wrap=dns_gethostbyname
|
||||
-Wl,--wrap=dns_gethostbyname_addrtype
|
||||
|
||||
-Wl,--wrap=raw_new
|
||||
-Wl,--wrap=raw_recv
|
||||
-Wl,--wrap=raw_bind
|
||||
-Wl,--wrap=raw_sendto
|
||||
-Wl,--wrap=raw_remove
|
||||
|
||||
-Wl,--wrap=cyw43_cb_process_ethernet
|
||||
-Wl,--wrap=cyw43_cb_tcpip_set_link_up
|
||||
-Wl,--wrap=cyw43_cb_tcpip_set_link_down
|
||||
-Wl,--wrap=cyw43_tcpip_link_status
|
||||
-Wl,--wrap=cyw43_cb_tcpip_init
|
||||
-Wl,--wrap=cyw43_cb_tcpip_deinit
|
||||
|
||||
-Wl,--wrap=get_rand_64
|
||||
-Wl,--wrap=get_rand_32
|
||||
-Wl,--wrap=sleep_until
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Mono analog microphone example using electret mike on A0
|
||||
Run using the Arduino Serial Plotter to see waveform.
|
||||
Released to the Public Domain by Earle F. Philhower, III
|
||||
|
||||
Wire the mike's VCC to 3.3V on the Pico, connect the mike's
|
||||
GND to a convenient Pico GND, and then connect mike OUT to A0
|
||||
*/
|
||||
|
||||
#include <ADCInput.h>
|
||||
|
||||
ADCInput mike(A0);
|
||||
// For stereo/dual mikes, could use this line instead
|
||||
// ADCInput(A0, A1);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
mike.begin(8000);
|
||||
|
||||
while (1) {
|
||||
Serial.printf("%d\n", mike.read());
|
||||
// For stereo/dual mikes, use this line instead
|
||||
// Serial.printf("%d %d\n", mike.read(), mike.read());
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
/* Nothing here */
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
ADCInput KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
begin KEYWORD2
|
||||
end KEYWORD2
|
||||
|
||||
setPins KEYWORD2
|
||||
setFrequency KEYWORD2
|
||||
setBuffers KEYWORD2
|
||||
|
||||
onReceive KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
@@ -0,0 +1,10 @@
|
||||
name=ADCInput
|
||||
version=1.0
|
||||
author=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
sentence=Records ADC values (i.e. microphone, sensors) and presents an I2S-like callback/immediate read interface
|
||||
paragraph=Records ADC values (i.e. microphone, sensors) and presents an I2S-like callback/immediate read interface
|
||||
category=Communication
|
||||
url=http://github.com/earlephilhower/arduino-pico
|
||||
architectures=rp2040
|
||||
dot_a_linkage=true
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
ADCInput
|
||||
Records ADC values (i.e. microphone, sensors) and presents an I2S-like
|
||||
callback/immediate read interface
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "ADCInput.h"
|
||||
#include <hardware/adc.h>
|
||||
|
||||
ADCInput::ADCInput(pin_size_t p0, pin_size_t p1, pin_size_t p2, pin_size_t p3) {
|
||||
_running = false;
|
||||
setPins(p0, p1, p2, p3);
|
||||
_freq = 48000;
|
||||
_arb = nullptr;
|
||||
_cb = nullptr;
|
||||
_buffers = 8;
|
||||
_bufferWords = 0;
|
||||
}
|
||||
|
||||
ADCInput::~ADCInput() {
|
||||
end();
|
||||
}
|
||||
|
||||
bool ADCInput::setBuffers(size_t buffers, size_t bufferWords) {
|
||||
if (_running || (buffers < 3) || (bufferWords < 8)) {
|
||||
return false;
|
||||
}
|
||||
_buffers = buffers;
|
||||
_bufferWords = bufferWords;
|
||||
return true;
|
||||
}
|
||||
|
||||
int ADCInput::_mask(pin_size_t p) {
|
||||
switch (p) {
|
||||
case 26: return 1;
|
||||
case 27: return 2;
|
||||
case 28: return 4;
|
||||
case 29: return 8;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool ADCInput::setPins(pin_size_t pin0, pin_size_t pin1, pin_size_t pin2, pin_size_t pin3) {
|
||||
if (_running) {
|
||||
return false;
|
||||
}
|
||||
_pinMask = _mask(pin0) | _mask(pin1) | _mask(pin2) | _mask(pin3);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ADCInput::setFrequency(int newFreq) {
|
||||
_freq = newFreq * __builtin_popcount(_pinMask); // Want to sample all channels at given frequency
|
||||
adc_set_clkdiv(48000000.0f / _freq - 1.0f);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ADCInput::onReceive(void(*fn)(void)) {
|
||||
_cb = fn;
|
||||
if (_running) {
|
||||
_arb->setCallback(_cb);
|
||||
}
|
||||
}
|
||||
|
||||
bool ADCInput::begin() {
|
||||
if (_running) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_running = true;
|
||||
|
||||
_isHolding = 0;
|
||||
|
||||
if (!_bufferWords) {
|
||||
_bufferWords = 16;
|
||||
}
|
||||
|
||||
// Set up the GPIOs to go to ADC
|
||||
adc_init();
|
||||
int cnt = 0;
|
||||
for (int mask = 1, pin = 26; pin <= 29; mask <<= 1, pin++) {
|
||||
if (_pinMask & mask) {
|
||||
if (!cnt) {
|
||||
adc_select_input(pin - 26);
|
||||
}
|
||||
cnt++;
|
||||
adc_gpio_init(pin);
|
||||
}
|
||||
}
|
||||
adc_set_round_robin(_pinMask);
|
||||
adc_fifo_setup(true, true, 1, false, false);
|
||||
|
||||
setFrequency(_freq);
|
||||
|
||||
_arb = new AudioBufferManager(_buffers, _bufferWords, 0, INPUT, DMA_SIZE_16);
|
||||
if (!_arb->begin(DREQ_ADC, (volatile void*)&adc_hw->fifo)) {
|
||||
delete _arb;
|
||||
_arb = nullptr;
|
||||
return false;
|
||||
}
|
||||
_arb->setCallback(_cb);
|
||||
|
||||
adc_fifo_drain();
|
||||
|
||||
adc_run(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ADCInput::end() {
|
||||
if (_running) {
|
||||
_running = false;
|
||||
delete _arb;
|
||||
_arb = nullptr;
|
||||
}
|
||||
adc_run(false);
|
||||
adc_fifo_drain();
|
||||
}
|
||||
|
||||
int ADCInput::available() {
|
||||
if (!_running) {
|
||||
return 0;
|
||||
} else {
|
||||
return _arb->available();
|
||||
}
|
||||
}
|
||||
|
||||
int ADCInput::read() {
|
||||
if (!_running) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (_hasPeeked) {
|
||||
_hasPeeked = false;
|
||||
return _peekSaved;
|
||||
}
|
||||
|
||||
if (_isHolding <= 0) {
|
||||
_arb->read(&_holdWord, true);
|
||||
_isHolding = 32;
|
||||
}
|
||||
|
||||
int ret = _holdWord & 0x0fff;
|
||||
_holdWord >>= 16;
|
||||
_isHolding -= 16;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ADCInput::peek() {
|
||||
if (!_running) {
|
||||
return -1;
|
||||
}
|
||||
if (!_hasPeeked) {
|
||||
_peekSaved = read();
|
||||
_hasPeeked = true;
|
||||
}
|
||||
return _peekSaved;
|
||||
}
|
||||
|
||||
void ADCInput::flush() {
|
||||
if (_running) {
|
||||
_arb->flush();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
ADCInput
|
||||
Records ADC values (i.e. microphone, sensors) and presents an I2S-like
|
||||
callback/immediate read interface
|
||||
|
||||
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 "AudioBufferManager.h"
|
||||
|
||||
class ADCInput : public Stream {
|
||||
public:
|
||||
ADCInput(pin_size_t pin0, pin_size_t pin1 = 255, pin_size_t pin2 = 255, pin_size_t pin3 = 255);
|
||||
virtual ~ADCInput();
|
||||
|
||||
bool setBuffers(size_t buffers, size_t bufferWords);
|
||||
bool setFrequency(int newFreq);
|
||||
bool setPins(pin_size_t pin0, pin_size_t pin1 = 255, pin_size_t pin2 = 255, pin_size_t pin3 = 255);
|
||||
|
||||
bool begin(long sampleRate) {
|
||||
setFrequency(sampleRate);
|
||||
return begin();
|
||||
}
|
||||
|
||||
bool begin();
|
||||
void end();
|
||||
|
||||
// from Stream
|
||||
virtual int available() override;
|
||||
virtual int read() override;
|
||||
virtual int peek() override;
|
||||
virtual void flush() override;
|
||||
|
||||
// from Print, not supported
|
||||
virtual size_t write(const uint8_t *buffer, size_t size) override {
|
||||
(void) buffer;
|
||||
(void) size;
|
||||
return -1;
|
||||
}
|
||||
virtual size_t write(uint8_t x) override {
|
||||
(void) x;
|
||||
return -1;
|
||||
}
|
||||
virtual int availableForWrite() override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Note that these callback are called from **INTERRUPT CONTEXT** and hence
|
||||
// should be in RAM, not FLASH, and should be quick to execute.
|
||||
void onReceive(void(*)(void));
|
||||
|
||||
private:
|
||||
uint32_t _pinMask;
|
||||
|
||||
int _freq;
|
||||
|
||||
size_t _buffers;
|
||||
size_t _bufferWords;
|
||||
|
||||
bool _running;
|
||||
void (*_cb)();
|
||||
|
||||
bool _hasPeeked;
|
||||
uint32_t _peekSaved;
|
||||
uint32_t _holdWord = 0;
|
||||
int _isHolding = 0;
|
||||
|
||||
int _mask(pin_size_t pin);
|
||||
|
||||
AudioBufferManager *_arb;
|
||||
};
|
||||
Submodule libraries/Adafruit_TinyUSB_Arduino updated: 5be0b21c0a...b17ab37a0e
@@ -110,7 +110,7 @@ void ArduinoOTAClass::begin(bool useMDNS) {
|
||||
_useMDNS = useMDNS;
|
||||
|
||||
if (!_hostname.length()) {
|
||||
char tmp[15];
|
||||
char tmp[2 * PICO_UNIQUE_BOARD_ID_SIZE_BYTES + 6];
|
||||
sprintf(tmp, "pico-%s", rp2040.getChipID());
|
||||
_hostname = tmp;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
name=AudioBufferManager
|
||||
version=1.0.0
|
||||
author=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
sentence=Manages DMA buffers for audio output
|
||||
paragraph=Manages DMA buffers for audio output
|
||||
category=Device Control
|
||||
url=https://github.com/earlephilhower/arduino-pico
|
||||
architectures=rp2040
|
||||
dot_a_linkage=true
|
||||
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
AudioBufferManager for Raspnerry Pi Pico RP2040
|
||||
Implements a DMA controlled linked-list series of buffers
|
||||
|
||||
Copyright (c) 2022 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/dma.h>
|
||||
#include <hardware/irq.h>
|
||||
#include "AudioBufferManager.h"
|
||||
|
||||
static int __channelCount = 0; // # of channels left. When we hit 0, then remove our handler
|
||||
static AudioBufferManager* __channelMap[12]; // Lets the IRQ handler figure out where to dispatch to
|
||||
|
||||
AudioBufferManager::AudioBufferManager(size_t bufferCount, size_t bufferWords, int32_t silenceSample, PinMode direction, enum dma_channel_transfer_size dmaSize) {
|
||||
_running = false;
|
||||
|
||||
// Need at least 2 DMA buffers and 1 user or this isn't going to work at all
|
||||
if (bufferCount < 3) {
|
||||
bufferCount = 3;
|
||||
}
|
||||
|
||||
_bufferCount = bufferCount;
|
||||
_wordsPerBuffer = bufferWords;
|
||||
_isOutput = direction == OUTPUT;
|
||||
_dmaSize = dmaSize;
|
||||
_overunderflow = false;
|
||||
_callback = nullptr;
|
||||
_userOff = 0;
|
||||
|
||||
// Create the silence buffer, fill with appropriate value
|
||||
_silence = new AudioBuffer;
|
||||
_silence->next = nullptr;
|
||||
_silence->buff = new uint32_t[_wordsPerBuffer];
|
||||
for (uint32_t x = 0; x < _wordsPerBuffer; x++) {
|
||||
_silence->buff[x] = silenceSample;
|
||||
}
|
||||
|
||||
// No filled buffers yet
|
||||
_filled = nullptr;
|
||||
|
||||
// Create all buffers on the empty chain
|
||||
_empty = nullptr;
|
||||
for (size_t i = 0; i < bufferCount; i++) {
|
||||
auto ab = new AudioBuffer;
|
||||
ab->buff = new uint32_t[_wordsPerBuffer];
|
||||
bzero(ab->buff, _wordsPerBuffer * 4);
|
||||
ab->next = nullptr;
|
||||
_addToList(&_empty, ab);
|
||||
}
|
||||
|
||||
_active[0] = _silence;
|
||||
_active[1] = _silence;
|
||||
}
|
||||
|
||||
AudioBufferManager::~AudioBufferManager() {
|
||||
noInterrupts();
|
||||
if (_running) {
|
||||
_running = false;
|
||||
for (auto i = 0; i < 2; i++) {
|
||||
dma_channel_cleanup(_channelDMA[i]);
|
||||
__channelMap[_channelDMA[i]] = nullptr;
|
||||
dma_channel_unclaim(_channelDMA[i]);
|
||||
__channelCount--;
|
||||
}
|
||||
if (!__channelCount) {
|
||||
irq_set_enabled(DMA_IRQ_0, false);
|
||||
// TODO - how can we know if there are no other parts of the core using DMA0 IRQ??
|
||||
irq_remove_handler(DMA_IRQ_0, _irq);
|
||||
}
|
||||
}
|
||||
interrupts();
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (_active[i] != _silence) {
|
||||
_deleteAudioBuffer(_active[i]);
|
||||
}
|
||||
}
|
||||
while (_filled) {
|
||||
auto x = _filled->next;
|
||||
_deleteAudioBuffer(_filled);
|
||||
_filled = x;
|
||||
}
|
||||
while (_empty) {
|
||||
auto x = _empty->next;
|
||||
_deleteAudioBuffer(_empty);
|
||||
_empty = x;
|
||||
}
|
||||
_deleteAudioBuffer(_silence);
|
||||
}
|
||||
|
||||
void AudioBufferManager::setCallback(void (*fn)()) {
|
||||
_callback = fn;
|
||||
}
|
||||
|
||||
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);
|
||||
if (_channelDMA[i] == -1) {
|
||||
if (i == 1) {
|
||||
dma_channel_unclaim(_channelDMA[0]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool needSetIRQ = __channelCount == 0;
|
||||
// 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]);
|
||||
channel_config_set_transfer_data_size(&c, _dmaSize); // 16b/32b transfers into PIO FIFO
|
||||
if (_isOutput) {
|
||||
channel_config_set_read_increment(&c, true); // Reading incrementing addresses
|
||||
channel_config_set_write_increment(&c, false); // Writing to the same FIFO address
|
||||
} else {
|
||||
channel_config_set_read_increment(&c, false); // Reading same FIFO address
|
||||
channel_config_set_write_increment(&c, true); // Writing to incrememting buffers
|
||||
}
|
||||
channel_config_set_dreq(&c, dreq); // Wait for the PIO TX FIFO specified
|
||||
channel_config_set_chain_to(&c, _channelDMA[i ^ 1]); // Start other channel when done
|
||||
channel_config_set_irq_quiet(&c, false); // Need IRQs
|
||||
|
||||
if (_isOutput) {
|
||||
dma_channel_configure(_channelDMA[i], &c, pioFIFOAddr, _silence->buff, _wordsPerBuffer * (_dmaSize == DMA_SIZE_16 ? 2 : 1), false);
|
||||
} else {
|
||||
_active[i] = _takeFromList(&_empty);
|
||||
dma_channel_configure(_channelDMA[i], &c, _active[i]->buff, pioFIFOAddr, _wordsPerBuffer * (_dmaSize == DMA_SIZE_16 ? 2 : 1), false);
|
||||
}
|
||||
dma_channel_set_irq0_enabled(_channelDMA[i], true);
|
||||
__channelMap[_channelDMA[i]] = this;
|
||||
__channelCount++;
|
||||
}
|
||||
if (needSetIRQ) {
|
||||
irq_add_shared_handler(DMA_IRQ_0, _irq, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
}
|
||||
|
||||
dma_channel_start(_channelDMA[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Following 2 routines use volatile because the IRQ may update the "this"
|
||||
// pointer and change the list head while we are waiting. Volatile will
|
||||
// cause GCC to keep re-reading from memory and not use cached value read
|
||||
// on the first pass.
|
||||
|
||||
bool AudioBufferManager::write(uint32_t v, bool sync) {
|
||||
if (!_running || !_isOutput) {
|
||||
return false;
|
||||
}
|
||||
AudioBuffer ** volatile p = (AudioBuffer ** volatile)&_empty;
|
||||
if (!*p) {
|
||||
if (!sync) {
|
||||
return false;
|
||||
} else {
|
||||
while (!*p) {
|
||||
/* noop busy wait */
|
||||
}
|
||||
}
|
||||
}
|
||||
(*p)->buff[_userOff++] = v;
|
||||
if (_userOff == _wordsPerBuffer) {
|
||||
_addToList(&_filled, _takeFromList(p));
|
||||
_userOff = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AudioBufferManager::read(uint32_t *v, bool sync) {
|
||||
if (!_running || _isOutput) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AudioBuffer ** volatile p = (AudioBuffer ** volatile)&_filled;
|
||||
if (!*p) {
|
||||
if (!sync) {
|
||||
return false;
|
||||
} else {
|
||||
while (!*p) {
|
||||
/* noop busy wait */
|
||||
}
|
||||
}
|
||||
}
|
||||
auto ret = (*p)->buff[_userOff++];
|
||||
if (_userOff == _wordsPerBuffer) {
|
||||
_addToList(&_empty, _takeFromList(p));
|
||||
_userOff = 0;
|
||||
}
|
||||
*v = ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AudioBufferManager::getOverUnderflow() {
|
||||
bool hold = _overunderflow;
|
||||
_overunderflow = false;
|
||||
return hold;
|
||||
}
|
||||
|
||||
int AudioBufferManager::available() {
|
||||
AudioBuffer *p = _isOutput ? _empty : _filled;
|
||||
|
||||
if (!_running || !p) {
|
||||
// No buffers available...
|
||||
return 0;
|
||||
}
|
||||
|
||||
int avail = _wordsPerBuffer - _userOff; // Currently available in this buffer
|
||||
|
||||
// Each add'l buffer has wpb spaces...
|
||||
auto x = p->next;
|
||||
while (x) {
|
||||
avail += _wordsPerBuffer;
|
||||
x = x->next;
|
||||
}
|
||||
return avail;
|
||||
}
|
||||
|
||||
void AudioBufferManager::flush() {
|
||||
AudioBuffer ** volatile a = (AudioBuffer ** volatile)&_active[0];
|
||||
AudioBuffer ** volatile b = (AudioBuffer ** volatile)&_active[1];
|
||||
AudioBuffer ** volatile c = (AudioBuffer ** volatile)&_filled;
|
||||
while (*c && (*b != (AudioBuffer * volatile)_silence) && (*a != (AudioBuffer * volatile)_silence)) {
|
||||
// busy wait until all user written data enroute
|
||||
}
|
||||
}
|
||||
|
||||
void __not_in_flash_func(AudioBufferManager::_dmaIRQ)(int channel) {
|
||||
if (!_running) {
|
||||
return;
|
||||
}
|
||||
if (_isOutput) {
|
||||
if (_active[0] != _silence) {
|
||||
_addToList(&_empty, _active[0]);
|
||||
}
|
||||
_active[0] = _active[1];
|
||||
if (!_filled) {
|
||||
_active[1] = _silence;
|
||||
} else {
|
||||
_active[1] = _takeFromList(&_filled);
|
||||
}
|
||||
_overunderflow = _overunderflow | (_active[1] == _silence);
|
||||
dma_channel_set_read_addr(channel, _active[1]->buff, false);
|
||||
} else {
|
||||
if (_empty) {
|
||||
_addToList(&_filled, _active[0]);
|
||||
_active[0] = _active[1];
|
||||
_active[1] = _takeFromList(&_empty);
|
||||
} else {
|
||||
_overunderflow = true;
|
||||
}
|
||||
dma_channel_set_write_addr(channel, _active[1]->buff, false);
|
||||
}
|
||||
dma_channel_set_trans_count(channel, _wordsPerBuffer * (_dmaSize == DMA_SIZE_16 ? 2 : 1), false);
|
||||
dma_channel_acknowledge_irq0(channel);
|
||||
if (_callback) {
|
||||
_callback();
|
||||
}
|
||||
}
|
||||
|
||||
void __not_in_flash_func(AudioBufferManager::_irq)() {
|
||||
for (size_t i = 0; i < sizeof(__channelMap); i++) {
|
||||
if (dma_channel_get_irq0_status(i) && __channelMap[i]) {
|
||||
__channelMap[i]->_dmaIRQ(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user