Compare commits
@@ -6,31 +6,23 @@ name: Arduino-Pico CI
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
|
||||
jobs:
|
||||
|
||||
# Me no spell so good
|
||||
code-spell:
|
||||
name: Check spelling
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- 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
|
||||
ignore_words_list: ser,dout
|
||||
|
||||
# Consistent style
|
||||
# Consistent style, spelling
|
||||
astyle:
|
||||
name: Style, Boards, Package
|
||||
name: Spelling, Style, Boards, Package
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: false
|
||||
- name: Run codespell
|
||||
uses: codespell-project/actions-codespell@v2
|
||||
with:
|
||||
skip: ./ArduinoCore-API,./libraries/ESP8266SdFat,./libraries/Adafruit_TinyUSB_Arduino,./libraries/LittleFS/lib,./tools/pyserial,./pico-sdk,./.github,./docs/i2s.rst,./cores/rp2040/api,./libraries/FreeRTOS,./tools/libbearssl/bearssl,./include,./libraries/WiFi/examples/BearSSL_Server,./ota/uzlib,./libraries/http-parser/lib,./libraries/WebServer/examples/HelloServerBearSSL/HelloServerBearSSL.ino,./libraries/HTTPUpdateServer/examples/SecureBearSSLUpdater/SecureBearSSLUpdater.ino,./.git,./libraries/FatFS/lib/fatfs,./libraries/FatFS/src/diskio.h,./libraries/FatFS/src/ff.cpp,./libraries/FatFS/src/ffconf.h,./libraries/FatFS/src/ffsystem.cpp,./libraries/FatFS/src/ff.h,./libraries/lwIP_WINC1500/src/driver,./libraries/lwIP_WINC1500/src/common,./libraries/lwIP_WINC1500/src/bus_wrapper,./libraries/lwIP_WINC1500/src/spi_flash
|
||||
ignore_words_list: ser,dout,shiftIn,acount
|
||||
- name: Get submodules for following tests
|
||||
run: git submodule update --init
|
||||
- name: Check package references
|
||||
run: |
|
||||
./tests/ci/pkgrefs_test.sh
|
||||
@@ -46,10 +38,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:
|
||||
@@ -59,22 +47,20 @@ jobs:
|
||||
matrix:
|
||||
chunk: [0, 1, 2, 3, 4, 5]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v2
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Cache Linux toolchain
|
||||
id: cache-linux
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
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 }}
|
||||
@@ -84,27 +70,87 @@ jobs:
|
||||
cd ..
|
||||
bash ./tests/build.sh
|
||||
|
||||
# Build TinyUSB examples, requires custom build command line
|
||||
build-tinyusb:
|
||||
name: Build TinyUSB Examples
|
||||
# Build all rp2350 examples on linux (core and Arduino IDE)
|
||||
build-rp2350-linux:
|
||||
name: Build RP2350 ${{ matrix.chunk }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
chunk: [0, 1, 2, 3, 4, 5]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v2
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Cache Linux toolchain
|
||||
id: cache-linux
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ./tools/dist
|
||||
key: ${{ runner.os }}-${{ hashFiles('package/package_pico_index.template.json', 'tests/common.sh') }}
|
||||
- name: Build Sketches
|
||||
env:
|
||||
BUILD_PARITY: custom
|
||||
mod: 6
|
||||
rem: ${{ matrix.chunk }}
|
||||
run: |
|
||||
cd pico-sdk
|
||||
git submodule update --init
|
||||
cd ..
|
||||
bash ./tests/build-rp2350.sh
|
||||
|
||||
# Build all rp2350-riscv examples on linux (core and Arduino IDE)
|
||||
build-rp2350-riscv-linux:
|
||||
name: Build RP2350-RISCV ${{ matrix.chunk }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
chunk: [0, 1, 2, 3, 4, 5]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Cache Linux toolchain
|
||||
id: cache-linux
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ./tools/dist
|
||||
key: ${{ runner.os }}-${{ hashFiles('package/package_pico_index.template.json', 'tests/common.sh') }}
|
||||
- name: Build Sketches
|
||||
env:
|
||||
BUILD_PARITY: custom
|
||||
mod: 6
|
||||
rem: ${{ matrix.chunk }}
|
||||
run: |
|
||||
cd pico-sdk
|
||||
git submodule update --init
|
||||
cd ..
|
||||
bash ./tests/build-rp2350-riscv.sh
|
||||
|
||||
# Build TinyUSB examples, requires custom build command line
|
||||
build-tinyusb:
|
||||
name: Build TinyUSB Examples
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Cache Linux toolchain
|
||||
id: cache-linux
|
||||
uses: actions/cache@v4
|
||||
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
|
||||
@@ -117,22 +163,20 @@ jobs:
|
||||
name: Windows
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v2
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Cache Windows toolchain
|
||||
id: cache-windows
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
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,40 +194,47 @@ jobs:
|
||||
# Single build under macOS to ensure the Mac toolchain is good.
|
||||
build-mac:
|
||||
name: Mac
|
||||
runs-on: macOS-latest
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macOS-12, macOS-14]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v2
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Cache Mac toolchain
|
||||
id: cache-mac
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
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
|
||||
rem: 1
|
||||
run: |
|
||||
brew update
|
||||
brew install bash
|
||||
/usr/bin/env bash --version
|
||||
uname -a
|
||||
cd pico-sdk
|
||||
git submodule update --init
|
||||
cd ..
|
||||
bash ./tests/build.sh
|
||||
/usr/bin/env bash ./tests/build.sh
|
||||
./system/picotool/picotool version
|
||||
otool -L ./system/picotool/picotool
|
||||
|
||||
# Build a few examples with PlatformIO to test if integration works
|
||||
build-platformio:
|
||||
name: Build PlatformIO Examples
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: 'true'
|
||||
- name: Initialize needed submodules
|
||||
@@ -194,27 +245,33 @@ jobs:
|
||||
git submodule update --init
|
||||
cd ../..
|
||||
- name: Cache pip
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
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@v4
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install PlatformIO
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install --upgrade platformio
|
||||
pio pkg install --global --platform https://github.com/maxgerhardt/platform-raspberrypi.git
|
||||
pio pkg update --global --platform https://github.com/maxgerhardt/platform-raspberrypi.git
|
||||
pio pkg install --global --tool symlink://.
|
||||
cp -f /home/runner/work/arduino-pico/arduino-pico/tools/json/*.json /home/runner/.platformio/platforms/raspberrypi/boards/.
|
||||
- name: Build Multicore Example
|
||||
run: pio ci --board=rpipico --board=adafruit_feather -O "platform_packages=framework-arduinopico@symlink:///home/runner/work/arduino-pico/arduino-pico" libraries/rp2040/examples/Multicore/Multicore.ino
|
||||
run: pio ci -v --board=rpipico --board=rpipico2 --board=adafruit_feather -O "platform_packages=framework-arduinopico@symlink:///home/runner/work/arduino-pico/arduino-pico" libraries/rp2040/examples/Multicore/Multicore.ino
|
||||
- name: Build Multicore Example (RISC-V)
|
||||
run: pio ci -v --board=rpipico2 -O "board_build.mcu = rp2350-riscv" -O "platform_packages=framework-arduinopico@symlink:///home/runner/work/arduino-pico/arduino-pico" libraries/rp2040/examples/Multicore/Multicore.ino
|
||||
- name: Build Fade Example
|
||||
run: pio ci --board=rpipico --board=adafruit_feather -O "platform_packages=framework-arduinopico@symlink:///home/runner/work/arduino-pico/arduino-pico" libraries/rp2040/examples/Fade/Fade.ino
|
||||
- name: Build TinyUSB Example
|
||||
@@ -223,3 +280,58 @@ 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
|
||||
|
||||
# Build every variant using PIO for simplicity
|
||||
build-variants:
|
||||
name: Build Every Variant ${{ matrix.chunk }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
chunk: [0, 1]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: 'true'
|
||||
- name: Initialize needed submodules
|
||||
run: |
|
||||
cd pico-sdk
|
||||
git submodule update --init
|
||||
cd ../libraries/Adafruit_TinyUSB_Arduino
|
||||
git submodule update --init
|
||||
cd ../..
|
||||
- name: Cache pip
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
- name: Cache PlatformIO
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install PlatformIO
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install --upgrade platformio
|
||||
pio pkg install --global --platform https://github.com/maxgerhardt/platform-raspberrypi.git
|
||||
pio pkg update --global --platform https://github.com/maxgerhardt/platform-raspberrypi.git
|
||||
pio pkg install --global --tool symlink://.
|
||||
cp -f /home/runner/work/arduino-pico/arduino-pico/tools/json/*.json /home/runner/.platformio/platforms/raspberrypi/boards/.
|
||||
- name: Build Every Variant
|
||||
run: |
|
||||
cnt=0
|
||||
for b in $(cut -f1 -d. /home/runner/work/arduino-pico/arduino-pico/boards.txt | sed 's/#.*//' | sed 's/^menu$//' | sort -u); do
|
||||
cnt=$((cnt + 1))
|
||||
rem=$((cnt % 2))
|
||||
if [ $rem == ${{ matrix.chunk }} ]; then
|
||||
pio ci --board=$b -O "platform_packages=framework-arduinopico@symlink:///home/runner/work/arduino-pico/arduino-pico" libraries/rp2040/examples/Bootsel/Bootsel.ino
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -9,31 +9,30 @@ jobs:
|
||||
name: Update master JSON file
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Cache pip
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
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@v5
|
||||
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@v4
|
||||
# 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 }}
|
||||
BUILD_TYPE: package
|
||||
CI_GITHUB_API_KEY: ${{ secrets.GITHUB_TOKEN }}
|
||||
PLATFORMIO_AUTH_TOKEN: ${{ secrets.PLATFORMIO_AUTH_TOKEN }}
|
||||
@@ -43,5 +42,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,16 +15,15 @@ jobs:
|
||||
name: Package
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-python@v2
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Build package JSON
|
||||
env:
|
||||
TRAVIS_BUILD_DIR: ${{ github.workspace }}
|
||||
BUILD_TYPE: package
|
||||
CI_GITHUB_API_KEY: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
|
||||
+19
-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
|
||||
@@ -37,3 +37,15 @@
|
||||
[submodule "libraries/http_parser/lib/http-parser"]
|
||||
path = libraries/http-parser/lib/http-parser
|
||||
url = https://github.com/nodejs/http-parser.git
|
||||
[submodule "libraries/FatFS/lib/SPIFTL"]
|
||||
path = libraries/FatFS/lib/SPIFTL
|
||||
url = https://github.com/earlephilhower/SPIFTL.git
|
||||
[submodule "libraries/AsyncUDP"]
|
||||
path = libraries/AsyncUDP
|
||||
url = https://github.com/earlephilhower/AsyncUDP.git
|
||||
[submodule "cores/rp2040/tlsf"]
|
||||
path = lib/tlsf
|
||||
url = https://github.com/earlephilhower/tlsf.git
|
||||
[submodule "libraries/ESPHost"]
|
||||
path = libraries/ESPHost
|
||||
url = https://github.com/Networking-for-Arduino/ESPHost.git
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# Read the Docs configuration file for Sphinx projects
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Set the OS, Python version and other tools you might need
|
||||
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: "3.12"
|
||||
jobs:
|
||||
post_create_environment:
|
||||
- python -m pip install sphinx_rtd_theme
|
||||
# You can also specify other tool versions:
|
||||
# nodejs: "20"
|
||||
# rust: "1.70"
|
||||
# golang: "1.20"
|
||||
|
||||
|
||||
# Build documentation in the "docs/" directory with Sphinx
|
||||
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
# You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
|
||||
# builder: "dirhtml"
|
||||
# Fail on all warnings to avoid broken references
|
||||
# fail_on_warning: true
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
# formats:
|
||||
# - pdf
|
||||
# - epub
|
||||
|
||||
# Optional but recommended, declare the Python requirements required
|
||||
# to build your documentation
|
||||
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
||||
# python:
|
||||
# install:
|
||||
# - requirements: docs/requirements.txt
|
||||
+1
-1
Submodule ArduinoCore-API updated: d955d75747...ece6e68f29
@@ -2,61 +2,171 @@
|
||||
[](https://github.com/earlephilhower/arduino-pico/releases)
|
||||
[](https://gitter.im/arduino-pico/community)
|
||||
|
||||
Raspberry Pi Pico Arduino core, for all RP2040 boards
|
||||
Raspberry Pi Pico Arduino core, for all RP2040 and RP2350 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 Arduino to the RP2040 (Raspberry Pi Pico processor) and RP2350 (Raspberry Pi Pico 2 processor). It uses the bare Raspberry Pi Pico SDK and a custom GCC 14.2/Newlib 4.3 toolchain and supports ARM and RISC-V cores.
|
||||
|
||||
# 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
|
||||
* Raspberry Pi Pico 2
|
||||
* 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
|
||||
* Amken Bunny
|
||||
* Amken Revelop
|
||||
* Amken Revelop Plus
|
||||
* Amken Revelop eS
|
||||
* Architeuthis Flux Jumperless
|
||||
* Architeuthis Flux Jumperless V5
|
||||
* Arduino Nano RP2040 Connect
|
||||
* ArtronShop RP2 Nano
|
||||
* Breadstick Raspberry
|
||||
* BridgeTek IDM2040-7A
|
||||
* BridgeTek IDM2040-43A
|
||||
* Cytron IRIV IO Controller
|
||||
* Cytron Maker Pi RP2040
|
||||
* Cytron Maker Nano RP2040
|
||||
* Degz Mizu
|
||||
* Cytron Maker Uno RP2040
|
||||
* Cytron Motion 2350 Pro
|
||||
* Datanoise PicoADK v1
|
||||
* Datanoise PicoADK v2 (RP2350)
|
||||
* Degz Suibo RP2040
|
||||
* DeRuiLab FlyBoard2040 Core
|
||||
* DFRobot Beetle RP2040
|
||||
* ElectronicCats Hunter Cat NFC
|
||||
* EVN Alpha
|
||||
* ExtremeElectronics RC2040
|
||||
* GroundStudio Marble Pico
|
||||
* 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 Challenger RP2350 BConnect
|
||||
* Invector Labs Challenger RP2350 WiFi/BLE
|
||||
* Invector Labs RPICO32
|
||||
* Melopero Cookie RP2040
|
||||
* Melopero Shake RP2040
|
||||
* METE HOCA Akana R1
|
||||
* Neko Systems BL2040 Mini
|
||||
* Olimex RP2040-Pico30
|
||||
* Newsan Archi
|
||||
* nullbits Bit-C PRO
|
||||
* Pimoroni PGA2040
|
||||
* Pimoroni Pico Plus 2
|
||||
* Pimoroni Plasma2040
|
||||
* Pimoroni Tiny2040
|
||||
* Pimoroni Tiny2350
|
||||
* Pintronix PinMax
|
||||
* RAKwireless RAK11300
|
||||
* Redscorp RP2040-Eins
|
||||
* Redscorp RP2040-ProMini
|
||||
* Sea-Picro
|
||||
* Seeed Indicator RP2040
|
||||
* Seeed XIAO RP2040
|
||||
* Seeed XIAO RP2350
|
||||
* Silicognition RP2040-Shim
|
||||
* Solder Party RP2040 Stamp
|
||||
* Solder Party RP2350 Stamp
|
||||
* Solder Party RP2350 Stamp XL
|
||||
* SparkFun MicroMod RP2040
|
||||
* SparkFun ProMicro RP2040
|
||||
* SparkFun ProMicro RP2350
|
||||
* 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
|
||||
* Waveshare RP2040 Matrix
|
||||
* Waveshare RP2040 PiZero
|
||||
* WIZnet W5100S-EVB-Pico
|
||||
* WIZnet W5500-EVB-Pico
|
||||
* WIZnet WizFi360-EVB-Pico
|
||||
* Generic (configurable flash, I/O pins)
|
||||
* Generic RP2040 (configurable flash, I/O pins)
|
||||
* Generic RP2350 (configurable flash, I/O pins)
|
||||
|
||||
# Features
|
||||
* Adafruit TinyUSB Arduino (USB mouse, keyboard, flash drive, generic HID, CDC Serial, MIDI, WebUSB, others)
|
||||
* Bluetooth on the PicoW (Classic and BLE) with Keyboard, Mouse, Joystick, and Virtual Serial
|
||||
* Bluetooth Classic and BLE HID master mode (connect to BT keyboard, mouse, or joystick)
|
||||
* Generic Arduino USB Serial, Keyboard, Joystick, and Mouse emulation
|
||||
* WiFi (Pico W, ESP32-based ESPHost, Atmel WINC1500)
|
||||
* Ethernet (Wired WizNet W6100, WizNet W5500, WizNet W5100, ENC28J60)
|
||||
* HTTP client and server (WebServer)
|
||||
* SSL/TLS/HTTPS
|
||||
* Over-the-Air (OTA) upgrades
|
||||
* Filesystems (LittleFS and SD/SDFS)
|
||||
* Multicore support (setup1() and loop1())
|
||||
* 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
|
||||
* Bluetooth A2DP audio source (output) and sink (input) on the PicoW
|
||||
* USB drive mode for data loggers (SingleFileDrive, FatFSUSB)
|
||||
* Peripherals: SPI master/slave, Wire(I2C) master/slave, dual UART, emulated EEPROM, I2S audio input/output, Servo
|
||||
* printf (i.e. debug) output over USB serial
|
||||
* Transparent use of PSRAM globals and heap (RP2350 only)
|
||||
* ARM or RISC-V (Hazard3) support for the RP2350
|
||||
|
||||
The RP2040 PIO state machines (SMs) are used to generate jitter-free:
|
||||
* Servos
|
||||
* Tones
|
||||
* I2S Input
|
||||
* I2S Output
|
||||
* Software UARTs (Serial ports)
|
||||
|
||||
|
||||
# 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:
|
||||
@@ -74,6 +184,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
|
||||
@@ -86,11 +202,6 @@ cd ../tools
|
||||
python3 ./get.py
|
||||
`````
|
||||
|
||||
# Installing both Arduino and CMake
|
||||
Tom's Hardware presented a very nice writeup on installing `arduino-pico` on both Windows and Linux, available at https://www.tomshardware.com/how-to/program-raspberry-pi-pico-with-arduino-ide
|
||||
|
||||
If you follow Les' step-by-step you will also have a fully functional `CMake`-based environment to build Pico apps on if you outgrow the Arduino ecosystem.
|
||||
|
||||
# Uploading Sketches
|
||||
To upload your first sketch, you will need to hold the BOOTSEL button down while plugging in the Pico to your computer.
|
||||
Then hit the upload button and the sketch should be transferred and start to run.
|
||||
@@ -111,76 +222,30 @@ To install, follow the directions in
|
||||
* https://github.com/earlephilhower/arduino-pico-littlefs-plugin/blob/master/README.md
|
||||
|
||||
For detailed usage information, please check the ESP8266 repo documentation (ignore SPIFFS related notes) available at
|
||||
* https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html
|
||||
* https://arduino-pico.readthedocs.io/en/latest/fs.html
|
||||
|
||||
# Uploading Sketches with Picoprobe
|
||||
# Uploading Sketches with Picoprobe/Debugprobe
|
||||
If you have built a Raspberry Pi Picoprobe, you can use OpenOCD to handle your sketch uploads and for debugging with GDB.
|
||||
|
||||
Under Windows a local admin user should be able to access the Picoprobe port automatically, but under Linux `udev` must be told about the device and to allow normal users access.
|
||||
|
||||
To set up user-level access to Picoprobes on Ubuntu (and other OSes which use `udev`):
|
||||
````
|
||||
echo 'SUBSYSTEMS=="usb", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="0004", GROUP="users", MODE="0666"' | sudo tee -a /etc/udev/rules.d/98-PicoProbe.rules
|
||||
echo 'SUBSYSTEM=="usb", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="0004", MODE="660", GROUP-"plugdev"' | sudo tee -a /etc/udev/rules.d/98-PicoProbe.rules
|
||||
echo 'SUBSYSTEM=="usb", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="000a", MODE="660", GROUP="plugdev"' | sudo tee -a /etc/udev/rules.d/98-PicoProbe.rules
|
||||
echo 'SUBSYSTEM=="usb", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="000f", MODE="660", GROUP="plugdev"' | sudo tee -a /etc/udev/rules.d/98-PicoProbe.rules
|
||||
sudo udevadm control --reload
|
||||
sudo udevadm trigger -w -s usb
|
||||
````
|
||||
|
||||
The first line creates a file with the USB vendor and ID of the Picoprobe and tells UDEV to give users full access to it. The second causes `udev` to load this new rule. Note that you will need to unplug and re-plug in your device the first time you create this file, to allow udev to make the device node properly.
|
||||
The first line creates a device file in `/dev` matching the USB vendor and product ID of the Picoprobe, and it enables global read+write permissions. The second line causes `udev` to load this new rule. The third line requests the kernel generate "device change" events that will cause our new `udev` rule to run.
|
||||
|
||||
If for some reason the device file does not appear, manually unplug and re-plug the USB connection and check again. The output from `dmesg` can reveal useful diagnostics if the device file remains absent.
|
||||
|
||||
Once Picoprobe permissions are set up properly, then select the board "Raspberry Pi Pico (Picoprobe)" in the Tools menu and upload as normal.
|
||||
|
||||
# Uploading Sketches with pico-debug
|
||||
[pico-debug](https://github.com/majbthrd/pico-debug/) differs from Picoprobe in that pico-debug is a virtual debug pod that runs side-by-side on the same RP2040 that you run your code on; so, you only need one RP2040 board instead of two. pico-debug also differs from Picoprobe in that pico-debug is standards-based; it uses the CMSIS-DAP protocol, which means even software not specially written for the Raspberry Pi Pico can support it. pico-debug uses OpenOCD to handle your sketch uploads, and debugging can be accomplished with CMSIS-DAP capable debuggers including GDB.
|
||||
|
||||
Under Windows and macOS, any user should be able to access pico-debug automatically, but under Linux `udev` must be told about the device and to allow normal users access.
|
||||
|
||||
To set up user-level access to all CMSIS-DAP adapters on Ubuntu (and other OSes which use `udev`):
|
||||
````
|
||||
echo 'ATTRS{product}=="*CMSIS-DAP*", MODE="664", GROUP="plugdev"' | sudo tee -a /etc/udev/rules.d/98-CMSIS-DAP.rules
|
||||
sudo udevadm control --reload
|
||||
````
|
||||
|
||||
The first line creates a file that recognizes all CMSIS-DAP adapters and tells UDEV to give users full access to it. The second causes `udev` to load this new rule. Note that you will need to unplug and re-plug in your device the first time you create this file, to allow udev to make the device node properly.
|
||||
|
||||
Once CMSIS-DAP permissions are set up properly, then select the board "Raspberry Pi Pico (pico-debug)" in the Tools menu.
|
||||
|
||||
When first connecting the USB port to your PC, you must copy [pico-debug-gimmecache.uf2](https://github.com/majbthrd/pico-debug/releases/) to the Pi Pico to load pico-debug into RAM; after this, upload as normal.
|
||||
|
||||
# Debugging with Picoprobe/pico-debug, OpenOCD, and GDB
|
||||
The installed tools include a version of OpenOCD (in the pqt-openocd directory) and GDB (in the pqt-gcc directory). These may be used to run GDB in an interactive window as documented in the Pico Getting Started manuals from the Raspberry Pi Foundation. For [pico-debug](https://github.com/majbthrd/pico-debug/), replace the raspberrypi-swd and picoprobe example OpenOCD arguments of "-f interface/raspberrypi-swd.cfg -f target/rp2040.cfg" or "-f interface/picoprobe.cfg -f target/rp2040.cfg" respectively in the Pico Getting Started manual with "-f board/pico-debug.cfg".
|
||||
|
||||
# Features
|
||||
* Adafruit TinyUSB Arduino (USB mouse, keyboard, flash drive, generic HID, CDC Serial, MIDI, WebUSB, others)
|
||||
* Generic Arduino USB Serial, Keyboard, and Mouse emulation
|
||||
* WiFi (Pico W)
|
||||
* HTTP client and server (WebServer)
|
||||
* SSL/TLS/HTTPS
|
||||
* Over-the-Air (OTA) upgrades
|
||||
* Filesystems (LittleFS and SD/SDFS)
|
||||
* Multicore support (setup1() and loop1())
|
||||
* FreeRTOS SMP support
|
||||
* Overclocking and underclocking from the menus
|
||||
* digitalWrite/Read, shiftIn/Out, tone, analogWrite(PWM)/Read, temperature
|
||||
* 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
|
||||
|
||||
The RP2040 PIO state machines (SMs) are used to generate jitter-free:
|
||||
* Servos
|
||||
* Tones
|
||||
* I2S Input
|
||||
* I2S Output
|
||||
* Software UARTs (Serial ports)
|
||||
|
||||
# Tutorials from Across the Web
|
||||
Here are some links to coverage and additional tutorials for using `arduino-pico`
|
||||
* The File:: class is taken from the ESP8266. See https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html
|
||||
* Arduino Support for the Pi Pico available! And how fast is the Pico? - https://youtu.be/-XHh17cuH5E
|
||||
* Pre-release Adafruit QT Py RP2040 - https://www.youtube.com/watch?v=sfC1msqXX0I
|
||||
* 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
|
||||
|
||||
# Contributing
|
||||
If you want to contribute or have bugfixes, drop me a note at <earlephilhower@yahoo.com> or open an issue/PR here.
|
||||
# Debugging with Picoprobe, OpenOCD, and GDB
|
||||
The installed tools include a version of OpenOCD (in the pqt-openocd directory) and GDB (in the pqt-gcc directory). These may be used to run GDB in an interactive window as documented in the Pico Getting Started manuals from the Raspberry Pi Foundation. Use the command line `./system/openocd/bin/openocd -f ./lib/rp2040/picoprobe_cmsis_dap.tcl` or `./system/openocd/bin/openocd -f ./lib/rp2350/picoprobe_cmsis_dap.tcl` from the `git` installation directory.
|
||||
|
||||
# Licensing and Credits
|
||||
* The [Arduino IDE and ArduinoCore-API](https://arduino.cc) are developed and maintained by the Arduino team. The IDE is licensed under GPL.
|
||||
@@ -191,14 +256,17 @@ If you want to contribute or have bugfixes, drop me a note at <earlephilhower@ya
|
||||
* [UF2CONV.PY](https://github.com/microsoft/uf2) is by Microsoft Corporation and licensed under the MIT license.
|
||||
* Networking and filesystem code taken from the [ESP8266 Arduino Core](https://github.com/esp8266/Arduino) and licensed under the LGPL.
|
||||
* DHCP server for AP host mode from the [Micropython Project](https://micropython.org), distributed under the MIT License.
|
||||
* [FreeRTOS](https://freertos.org) is Copyright Amazon.com, Inc. or its affiliates, and distributed under the MIT license.
|
||||
* [FreeRTOS](https://freertos.org) is copyright Amazon.com, Inc. or its affiliates, and distributed under the MIT license.
|
||||
* [lwIP](https://savannah.nongnu.org/projects/lwip/) is (c) the Swedish Institute of Computer Science and licenced under the BSD license.
|
||||
* [BearSSL](https://bearssl.org) library written by Thomas Pornin, is distributed under the [MIT License](https://bearssl.org/#legal-details).
|
||||
* [UZLib](https://github.com/pfalcon/uzlib) is copyright (c) 2003 Joergen Ibsen and distributed under the zlib license.
|
||||
* [LEAmDNS](https://github.com/LaborEtArs/ESP8266mDNS) is copyright multiple authors and distributed under the MIT license.
|
||||
* [http-parser](https://github.com/nodejs/http-parser) is copyright Joyent, Inc. and other Node contributors.
|
||||
* WebServer code modified from the [ESP32 WebServer](https://github.com/espressif/arduino-esp32/tree/master/libraries/WebServer) and is copyright (c) 2015 Ivan Grokhotkov and others
|
||||
|
||||
* WebServer code modified from the [ESP32 WebServer](https://github.com/espressif/arduino-esp32/tree/master/libraries/WebServer) and is copyright (c) 2015 Ivan Grokhotkov and others.
|
||||
* [Xoshiro-cpp](https://github.com/Reputeless/Xoshiro-cpp) is copyright (c) 2020 Ryo Suzuki and distributed under the MIT license.
|
||||
* [FatFS low-level filesystem](http://elm-chan.org/fsw/ff/) code is Copyright (C) 2024, ChaN, all rights reserved.
|
||||
* [TLSF memory manager for PSRAM from Espressif fork](https://github.com/espressif/tlsf) of [original](https://github.com/mattconte/tlsf) by Matthew Conte is copyright Matthew Conte and licensed under the MIT license.
|
||||
* [ESPHost library](https://github.com/Networking-for-Arduino/ESPHost) is LGPL licensed by its maintainers.
|
||||
|
||||
-Earle F. Philhower, III
|
||||
earlephilhower@yahoo.com
|
||||
|
||||
+23633
-16245
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
||||
// Padded and checksummed version of: generated\Winbond\W25Q32JVxQ\boot2.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
|
||||
@@ -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,4 @@
|
||||
.section .boot2, "ax"
|
||||
|
||||
.global __boot2_entry_point
|
||||
__boot2_entry_point:
|
||||
@@ -0,0 +1,4 @@
|
||||
.section .boot2, "ax"
|
||||
|
||||
.global __boot2_entry_point
|
||||
__boot2_entry_point:
|
||||
+50
-12
@@ -28,9 +28,8 @@
|
||||
#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
|
||||
|
||||
// Try and make the best of the old Arduino abs() macro. When in C++, use
|
||||
// the sane std::abs() call, but for C code use their macro since stdlib abs()
|
||||
@@ -38,7 +37,7 @@
|
||||
#ifdef abs
|
||||
#undef abs
|
||||
#endif // abs
|
||||
#ifdef __cplusplus
|
||||
#if defined(__cplusplus) && !defined(__riscv)
|
||||
using std::abs;
|
||||
using std::round;
|
||||
#else
|
||||
@@ -59,32 +58,47 @@ extern "C" {
|
||||
void interrupts();
|
||||
void noInterrupts();
|
||||
|
||||
// Only implemented on some RP2350 boards, not the OG Pico 2
|
||||
#ifdef RP2350_PSRAM_CS
|
||||
void *pmalloc(size_t size);
|
||||
void *pcalloc(size_t count, size_t size);
|
||||
#else
|
||||
[[deprecated("This chip does not have PSRAM, pmalloc will always fail")]] void *pmalloc(size_t size);
|
||||
[[deprecated("This chip does not have PSRAM, pcalloc will always fail")]] void *pcalloc(size_t count, size_t size);
|
||||
#endif
|
||||
|
||||
// AVR compatibility macros...naughty and accesses the HW directly
|
||||
#define digitalPinToPort(pin) (0)
|
||||
#define digitalPinToBitMask(pin) (1UL << (pin))
|
||||
#define digitalPinToTimer(pin) (0)
|
||||
#define digitalPinToInterrupt(pin) (pin)
|
||||
#define NOT_AN_INTERRUPT (-1)
|
||||
#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 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 +115,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"
|
||||
@@ -116,7 +133,28 @@ extern bool __isFreeRTOS;
|
||||
// Template which will evaluate at *compile time* to a single 32b number
|
||||
// with the specified bits set.
|
||||
template <size_t N>
|
||||
constexpr uint32_t __bitset(const int (&a)[N], size_t i = 0U) {
|
||||
return i < N ? (1L << a[i]) | __bitset(a, i + 1) : 0;
|
||||
constexpr uint64_t __bitset(const int (&a)[N], size_t i = 0U) {
|
||||
return i < N ? (1LL << 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")
|
||||
|
||||
// PSRAM decorator
|
||||
#define PSRAM __attribute__((section("\".psram\"")))
|
||||
|
||||
// General GPIO/ADC layout info
|
||||
#ifdef PICO_RP2350B
|
||||
#define __GPIOCNT 48
|
||||
#define __FIRSTANALOGGPIO 40
|
||||
#else
|
||||
#define __GPIOCNT 30
|
||||
#define __FIRSTANALOGGPIO 26
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
Enable BTStack debugging to a Print-able object
|
||||
|
||||
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(ENABLE_CLASSIC) || defined(ENABLE_BLE)
|
||||
#include <Arduino.h>
|
||||
#include <btstack.h>
|
||||
#include <hci_dump.h>
|
||||
|
||||
static Print *_print;
|
||||
|
||||
static void _log_packet(uint8_t packet_type, uint8_t in, uint8_t *packet, uint16_t len) {
|
||||
if (!_print) {
|
||||
return;
|
||||
}
|
||||
_print->printf("[BT @%lu] ", millis());
|
||||
|
||||
switch (packet_type) {
|
||||
case HCI_COMMAND_DATA_PACKET:
|
||||
_print->printf("CMD => ");
|
||||
break;
|
||||
case HCI_EVENT_PACKET:
|
||||
_print->printf("EVT <= ");
|
||||
break;
|
||||
case HCI_ACL_DATA_PACKET:
|
||||
_print->printf("ACL %s ", in ? "<=" : "=>");
|
||||
break;
|
||||
case HCI_SCO_DATA_PACKET:
|
||||
_print->printf("SCO %s ", in ? "<=" : "=>");
|
||||
break;
|
||||
case HCI_ISO_DATA_PACKET:
|
||||
_print->printf("ISO %s ", in ? "<=" : "=>");
|
||||
break;
|
||||
case LOG_MESSAGE_PACKET:
|
||||
_print->printf("LOG -- %s\n", (char*) packet);
|
||||
return;
|
||||
default:
|
||||
_print->printf("UNK(%x) %s ", packet_type, in ? "<=" : "=>");
|
||||
break;
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < len; i++) {
|
||||
_print->printf("%02X ", packet[i]);
|
||||
}
|
||||
_print->printf("\n");
|
||||
}
|
||||
|
||||
static void _log_message(int log_level, const char * format, va_list argptr) {
|
||||
(void)log_level;
|
||||
char log_message_buffer[HCI_DUMP_MAX_MESSAGE_LEN];
|
||||
if (!_print) {
|
||||
return;
|
||||
}
|
||||
vsnprintf(log_message_buffer, sizeof(log_message_buffer), format, argptr);
|
||||
_print->printf("[BT @%lu] LOG -- %s\n", millis(), log_message_buffer);
|
||||
}
|
||||
|
||||
|
||||
static const hci_dump_t hci_dump_instance = {
|
||||
NULL,
|
||||
_log_packet,
|
||||
_log_message
|
||||
};
|
||||
|
||||
|
||||
void __EnableBluetoothDebug(Print &print) {
|
||||
_print = &print;
|
||||
hci_dump_init(&hci_dump_instance);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -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.
|
||||
//
|
||||
@@ -26,7 +26,9 @@ static bool __no_inline_not_in_flash_func(get_bootsel_button)() {
|
||||
|
||||
// Must disable interrupts, as interrupt handlers may be in flash, and we
|
||||
// are about to temporarily disable flash access!
|
||||
noInterrupts();
|
||||
if (!__isFreeRTOS) {
|
||||
noInterrupts();
|
||||
}
|
||||
rp2040.idleOtherCore();
|
||||
|
||||
// Set chip select to Hi-Z
|
||||
@@ -39,7 +41,12 @@ static bool __no_inline_not_in_flash_func(get_bootsel_button)() {
|
||||
|
||||
// The HI GPIO registers in SIO can observe and control the 6 QSPI pins.
|
||||
// Note the button pulls the pin *low* when pressed.
|
||||
bool button_state = !(sio_hw->gpio_hi_in & (1u << CS_PIN_INDEX));
|
||||
#if PICO_RP2040
|
||||
#define CS_BIT (1u << 1)
|
||||
#else
|
||||
#define CS_BIT SIO_GPIO_HI_IN_QSPI_CSN_BITS
|
||||
#endif
|
||||
bool button_state = !(sio_hw->gpio_hi_in & CS_BIT);
|
||||
|
||||
// Need to restore the state of chip select, else we are going to have a
|
||||
// bad time when we return to code in flash!
|
||||
@@ -48,7 +55,9 @@ static bool __no_inline_not_in_flash_func(get_bootsel_button)() {
|
||||
IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS);
|
||||
|
||||
rp2040.resumeOtherCore();
|
||||
interrupts();
|
||||
if (!__isFreeRTOS) {
|
||||
interrupts();
|
||||
}
|
||||
|
||||
return button_state;
|
||||
}
|
||||
|
||||
@@ -24,17 +24,28 @@
|
||||
#include "Arduino.h"
|
||||
#include "CoreMutex.h"
|
||||
|
||||
CoreMutex::CoreMutex(mutex_t *mutex, bool debugEnable) {
|
||||
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);
|
||||
__freertos_mutex_take(m);
|
||||
|
||||
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 (debugEnable) {
|
||||
if (_option & DebugEnable) {
|
||||
DEBUGCORE("CoreMutex - Deadlock detected!\n");
|
||||
}
|
||||
return;
|
||||
@@ -49,7 +60,11 @@ CoreMutex::~CoreMutex() {
|
||||
if (_acquired) {
|
||||
if (__isFreeRTOS) {
|
||||
auto m = __get_freertos_mutex_for_ptr(_mutex);
|
||||
__freertos_mutex_give(m);
|
||||
if (__freertos_check_if_in_isr()) {
|
||||
__freertos_mutex_give_from_isr(m, &_pxHigherPriorityTaskWoken);
|
||||
} else {
|
||||
__freertos_mutex_give(m);
|
||||
}
|
||||
} else {
|
||||
mutex_exit(_mutex);
|
||||
}
|
||||
|
||||
@@ -23,12 +23,16 @@
|
||||
|
||||
#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);
|
||||
CoreMutex(mutex_t *mutex, uint8_t option = DebugEnable);
|
||||
~CoreMutex();
|
||||
|
||||
operator bool() {
|
||||
@@ -38,4 +42,6 @@ public:
|
||||
private:
|
||||
mutex_t *_mutex;
|
||||
bool _acquired;
|
||||
uint8_t _option;
|
||||
BaseType_t _pxHigherPriorityTaskWoken;
|
||||
};
|
||||
|
||||
+32
-14
@@ -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;
|
||||
}
|
||||
|
||||
@@ -226,6 +225,21 @@ void File::setTimeCallback(time_t (*cb)(void)) {
|
||||
_timeCallback = cb;
|
||||
}
|
||||
|
||||
bool File::stat(FSStat *st) {
|
||||
if (!_p) {
|
||||
return false;
|
||||
}
|
||||
size_t pos = position();
|
||||
seek(0, SeekEnd);
|
||||
st->size = position() - pos;
|
||||
seek(pos, SeekSet);
|
||||
st->blocksize = 0; // Not set here
|
||||
st->ctime = getCreationTime();
|
||||
st->atime = getLastWrite();
|
||||
st->isDir = isDirectory();
|
||||
return true;
|
||||
}
|
||||
|
||||
File Dir::openFile(const char* mode) {
|
||||
if (!_impl) {
|
||||
return File();
|
||||
@@ -367,13 +381,6 @@ bool FS::info(FSInfo& info) {
|
||||
return _impl->info(info);
|
||||
}
|
||||
|
||||
bool FS::info64(FSInfo64& info) {
|
||||
if (!_impl) {
|
||||
return false;
|
||||
}
|
||||
return _impl->info64(info);
|
||||
}
|
||||
|
||||
File FS::open(const String& path, const char* mode) {
|
||||
return open(path.c_str(), mode);
|
||||
}
|
||||
@@ -463,6 +470,17 @@ bool FS::rename(const String& pathFrom, const String& pathTo) {
|
||||
return rename(pathFrom.c_str(), pathTo.c_str());
|
||||
}
|
||||
|
||||
bool FS::stat(const char *path, FSStat *st) {
|
||||
if (!_impl) {
|
||||
return false;
|
||||
}
|
||||
return _impl->stat(path, st);
|
||||
}
|
||||
|
||||
bool FS::stat(const String& path, FSStat *st) {
|
||||
return stat(path.c_str(), st);
|
||||
}
|
||||
|
||||
time_t FS::getCreationTime() {
|
||||
if (!_impl) {
|
||||
return 0;
|
||||
|
||||
+14
-13
@@ -48,6 +48,14 @@ enum SeekMode {
|
||||
SeekEnd = 2
|
||||
};
|
||||
|
||||
struct FSStat {
|
||||
size_t size;
|
||||
size_t blocksize;
|
||||
time_t ctime;
|
||||
time_t atime;
|
||||
bool isDir;
|
||||
};
|
||||
|
||||
class File : public Stream {
|
||||
public:
|
||||
File(FileImplPtr p = FileImplPtr(), FS *baseFS = nullptr) : _p(p), _fakeDir(nullptr), _baseFS(baseFS) {
|
||||
@@ -119,6 +127,8 @@ public:
|
||||
time_t getCreationTime();
|
||||
void setTimeCallback(time_t (*cb)(void));
|
||||
|
||||
bool stat(FSStat *st);
|
||||
|
||||
protected:
|
||||
FileImplPtr _p;
|
||||
time_t (*_timeCallback)(void) = nullptr;
|
||||
@@ -152,18 +162,8 @@ protected:
|
||||
time_t (*_timeCallback)(void) = nullptr;
|
||||
};
|
||||
|
||||
// Backwards compatible, <4GB filesystem usage
|
||||
struct FSInfo {
|
||||
size_t totalBytes;
|
||||
size_t usedBytes;
|
||||
size_t blockSize;
|
||||
size_t pageSize;
|
||||
size_t maxOpenFiles;
|
||||
size_t maxPathLength;
|
||||
};
|
||||
|
||||
// Support > 4GB filesystems (SD, etc.)
|
||||
struct FSInfo64 {
|
||||
struct FSInfo {
|
||||
uint64_t totalBytes;
|
||||
uint64_t usedBytes;
|
||||
size_t blockSize;
|
||||
@@ -172,7 +172,6 @@ struct FSInfo64 {
|
||||
size_t maxPathLength;
|
||||
};
|
||||
|
||||
|
||||
class FSConfig {
|
||||
public:
|
||||
static constexpr uint32_t FSId = 0x00000000;
|
||||
@@ -201,7 +200,6 @@ public:
|
||||
|
||||
bool format();
|
||||
bool info(FSInfo& info);
|
||||
bool info64(FSInfo64& info);
|
||||
|
||||
File open(const char* path, const char* mode);
|
||||
File open(const String& path, const char* mode);
|
||||
@@ -224,6 +222,9 @@ public:
|
||||
bool rmdir(const char* path);
|
||||
bool rmdir(const String& path);
|
||||
|
||||
bool stat(const char *path, FSStat *st);
|
||||
bool stat(const String& path, FSStat *st);
|
||||
|
||||
// Low-level FS routines, not needed by most applications
|
||||
bool gc();
|
||||
bool check();
|
||||
|
||||
@@ -119,7 +119,6 @@ public:
|
||||
virtual void end() = 0;
|
||||
virtual bool format() = 0;
|
||||
virtual bool info(FSInfo& info) = 0;
|
||||
virtual bool info64(FSInfo64& info) = 0;
|
||||
virtual FileImplPtr open(const char* path, OpenMode openMode, AccessMode accessMode) = 0;
|
||||
virtual bool exists(const char* path) = 0;
|
||||
virtual DirImplPtr openDir(const char* path) = 0;
|
||||
@@ -127,6 +126,7 @@ public:
|
||||
virtual bool remove(const char* path) = 0;
|
||||
virtual bool mkdir(const char* path) = 0;
|
||||
virtual bool rmdir(const char* path) = 0;
|
||||
virtual bool stat(const char *path, FSStat *st) = 0;
|
||||
virtual bool gc() {
|
||||
return true; // May not be implemented in all file systems.
|
||||
}
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
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>
|
||||
#include <hardware/claim.h>
|
||||
|
||||
#if defined(PICO_RP2350)
|
||||
#define PIOS pio0, pio1, pio2
|
||||
#define PIOCNT 3
|
||||
#elif defined(PICO_RP2040)
|
||||
#define PIOS pio0, pio1
|
||||
#define PIOCNT 2
|
||||
#endif
|
||||
|
||||
static std::map<const pio_program_t *, int> __pioMap[PIOCNT];
|
||||
static bool __pioAllocated[PIOCNT];
|
||||
static bool __pioHighGPIO[PIOCNT];
|
||||
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, int start, int cnt) {
|
||||
CoreMutex m(&_pioMutex);
|
||||
PIO pi[PIOCNT] = { PIOS };
|
||||
|
||||
#if 0
|
||||
uint usm;
|
||||
uint uoff;
|
||||
auto ret = pio_claim_free_sm_and_add_program_for_gpio_range(_pgm, pio, &usm, &uoff, start, cnt, true);
|
||||
*sm = usm;
|
||||
*offset = uoff;
|
||||
DEBUGV("clain %d\n", ret);
|
||||
return ret;
|
||||
#endif
|
||||
|
||||
bool needsHigh = (start + cnt) >= 32;
|
||||
DEBUGV("PIOProgram %p: Searching for high=%d, pins %d-%d\n", _pgm, needsHigh ? 1 : 0, start, start + cnt - 1);
|
||||
|
||||
// If it's already loaded into PIO IRAM, try and allocate in that specific PIO
|
||||
for (int o = 0; o < PIOCNT; o++) {
|
||||
auto p = __pioMap[o].find(_pgm);
|
||||
if ((p != __pioMap[o].end()) && (__pioHighGPIO[o] == needsHigh)) {
|
||||
int idx = pio_claim_unused_sm(pi[o], false);
|
||||
if (idx >= 0) {
|
||||
DEBUGV("PIOProgram %p: Reusing IMEM ON PIO %p(high=%d) for pins %d-%d\n", _pgm, pi[o], __pioHighGPIO[o] ? 1 : 0, start, start + cnt - 1);
|
||||
_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 < PIOCNT; o++) {
|
||||
if (__pioAllocated[o] && (__pioHighGPIO[o] == needsHigh)) {
|
||||
DEBUGV("PIOProgram: Checking PIO %p\n", pi[o]);
|
||||
if (pio_can_add_program(pi[o], _pgm)) {
|
||||
int idx = pio_claim_unused_sm(pi[o], false);
|
||||
if (idx >= 0) {
|
||||
DEBUGV("PIOProgram %p: Adding IMEM ON PIO %p(high=%d) for pins %d-%d\n", _pgm, pi[o], __pioHighGPIO[o] ? 1 : 0, start, start + cnt - 1);
|
||||
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;
|
||||
} else {
|
||||
DEBUGV("PIOProgram: can't claim unused SM\n");
|
||||
}
|
||||
} else {
|
||||
DEBUGV("PIOProgram: can't add program\n");
|
||||
}
|
||||
} else {
|
||||
DEBUGV("PIOProgram: Skipping PIO %p, wrong allocated/needhi\n", pi[o]);
|
||||
}
|
||||
}
|
||||
|
||||
// No existing PIOs can meet, is there an unallocated one we can allocate?
|
||||
PIO p;
|
||||
uint idx;
|
||||
uint off;
|
||||
auto rc = pio_claim_free_sm_and_add_program_for_gpio_range(_pgm, &p, &idx, &off, start, cnt, true);
|
||||
if (rc) {
|
||||
int o = 0;
|
||||
while (p != pi[o]) {
|
||||
o++;
|
||||
}
|
||||
assert(!__pioAllocated[o]);
|
||||
__pioAllocated[o] = true;
|
||||
__pioHighGPIO[o] = needsHigh;
|
||||
DEBUGV("PIOProgram %p: Allocating new PIO %p(high=%d) for pins %d-%d\n", _pgm, pi[o], __pioHighGPIO[o] ? 1 : 0, start, start + cnt - 1);
|
||||
__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;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <hardware/pio.h>
|
||||
|
||||
// Wrapper class for PIO programs, abstracting common operations out
|
||||
class PIOProgram {
|
||||
public:
|
||||
PIOProgram(const pio_program_t *pgm);
|
||||
~PIOProgram();
|
||||
// Possibly load into a PIO and allocate a SM
|
||||
bool prepare(PIO *pio, int *sm, int *offset, int gpio_start = 0, int gpio_cnt = 1);
|
||||
|
||||
private:
|
||||
const pio_program_t *_pgm;
|
||||
PIO _pio;
|
||||
int _sm;
|
||||
};
|
||||
+201
-86
@@ -22,13 +22,22 @@
|
||||
#include <hardware/irq.h>
|
||||
#include <hardware/pio.h>
|
||||
#include <pico/unique_id.h>
|
||||
#ifdef PICO_RP2350
|
||||
#include <hardware/regs/powman.h>
|
||||
#else
|
||||
#include <hardware/regs/vreg_and_chip_reset.h>
|
||||
#endif
|
||||
#include <hardware/exception.h>
|
||||
#include <hardware/watchdog.h>
|
||||
#include <hardware/structs/rosc.h>
|
||||
#include <hardware/structs/systick.h>
|
||||
#include <pico/multicore.h>
|
||||
#include <hardware/dma.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>
|
||||
|
||||
@@ -56,8 +65,13 @@ public:
|
||||
void registerCore() {
|
||||
if (!__isFreeRTOS) {
|
||||
multicore_fifo_clear_irq();
|
||||
#ifdef PICO_RP2350
|
||||
irq_set_exclusive_handler(SIO_IRQ_FIFO, _irq);
|
||||
irq_set_enabled(SIO_IRQ_FIFO, true);
|
||||
#else
|
||||
irq_set_exclusive_handler(SIO_IRQ_PROC0 + get_core_num(), _irq);
|
||||
irq_set_enabled(SIO_IRQ_PROC0 + get_core_num(), true);
|
||||
#endif
|
||||
}
|
||||
// FreeRTOS port.c will handle the IRQ hooking
|
||||
}
|
||||
@@ -90,15 +104,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 +121,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,64 +166,12 @@ 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;
|
||||
};
|
||||
extern "C" void setup1() __attribute__((weak));
|
||||
extern "C" void loop1() __attribute__((weak));
|
||||
extern "C" bool core1_separate_stack;
|
||||
extern "C" uint32_t* core1_separate_stack_address;
|
||||
|
||||
class RP2040 {
|
||||
public:
|
||||
@@ -221,18 +180,22 @@ public:
|
||||
|
||||
void begin() {
|
||||
_epoch = 0;
|
||||
#if !defined(__riscv)
|
||||
if (!__isFreeRTOS) {
|
||||
// Enable SYSTICK exception
|
||||
exception_set_exclusive_handler(SYSTICK_EXCEPTION, _SystickHandler);
|
||||
systick_hw->csr = 0x7;
|
||||
systick_hw->rvr = 0x00FFFFFF;
|
||||
} else {
|
||||
#endif
|
||||
int off = 0;
|
||||
_ccountPgm = new PIOProgram(&ccount_program);
|
||||
_ccountPgm->prepare(&_pio, &_sm, &off);
|
||||
ccount_program_init(_pio, _sm, off);
|
||||
pio_sm_set_enabled(_pio, _sm, true);
|
||||
#if !defined(__riscv)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Convert from microseconds to PIO clock cycles
|
||||
@@ -246,9 +209,15 @@ 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() {
|
||||
#if !defined(__riscv)
|
||||
if (!__isFreeRTOS) {
|
||||
uint32_t epoch;
|
||||
uint32_t ctr;
|
||||
@@ -258,11 +227,15 @@ public:
|
||||
} while (epoch != (uint32_t)_epoch);
|
||||
return epoch + (1 << 24) - ctr; /* CTR counts down from 1<<24-1 */
|
||||
} else {
|
||||
#endif
|
||||
return ccount_read(_pio, _sm);
|
||||
#if !defined(__riscv)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
inline uint64_t getCycleCount64() {
|
||||
#if !defined(__riscv)
|
||||
if (!__isFreeRTOS) {
|
||||
uint64_t epoch;
|
||||
uint64_t ctr;
|
||||
@@ -272,8 +245,11 @@ public:
|
||||
} while (epoch != _epoch);
|
||||
return epoch + (1LL << 24) - ctr;
|
||||
} else {
|
||||
#endif
|
||||
return ccount_read(_pio, _sm);
|
||||
#if !defined(__riscv)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
inline int getFreeHeap() {
|
||||
@@ -289,6 +265,60 @@ public:
|
||||
return &__StackLimit - &__bss_end__;
|
||||
}
|
||||
|
||||
inline int getFreePSRAMHeap() {
|
||||
return getTotalPSRAMHeap() - getUsedPSRAMHeap();
|
||||
}
|
||||
|
||||
inline int getUsedPSRAMHeap() {
|
||||
#if defined(RP2350_PSRAM_CS)
|
||||
extern size_t __psram_total_used();
|
||||
return __psram_total_used();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline int getTotalPSRAMHeap() {
|
||||
#if defined(RP2350_PSRAM_CS)
|
||||
extern size_t __psram_total_space();
|
||||
return __psram_total_space();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline uint32_t getStackPointer() {
|
||||
uint32_t *sp;
|
||||
#if defined(__riscv)
|
||||
asm volatile("mv %0, sp" : "=r"(sp));
|
||||
#else
|
||||
asm volatile("mov %0, sp" : "=r"(sp));
|
||||
#endif
|
||||
return (uint32_t)sp;
|
||||
}
|
||||
|
||||
inline int getFreeStack() {
|
||||
const unsigned int sp = getStackPointer();
|
||||
uint32_t ref = 0x20040000;
|
||||
if (setup1 || loop1) {
|
||||
if (core1_separate_stack) {
|
||||
ref = cpuid() ? (unsigned int)core1_separate_stack_address : 0x20040000;
|
||||
} else {
|
||||
ref = cpuid() ? 0x20040000 : 0x20041000;
|
||||
}
|
||||
}
|
||||
return sp - ref;
|
||||
}
|
||||
|
||||
inline size_t getPSRAMSize() {
|
||||
#if defined(RP2350_PSRAM_CS)
|
||||
extern size_t __psram_size;
|
||||
return __psram_size;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void idleOtherCore() {
|
||||
fifo.idleOtherCore();
|
||||
}
|
||||
@@ -314,6 +344,13 @@ public:
|
||||
reboot();
|
||||
}
|
||||
|
||||
inline void rebootToBootloader() {
|
||||
reset_usb_boot(0, 0);
|
||||
while (1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
static void enableDoubleResetBootloader();
|
||||
|
||||
void wdt_begin(uint32_t delay_ms) {
|
||||
@@ -324,40 +361,117 @@ public:
|
||||
watchdog_update();
|
||||
}
|
||||
|
||||
enum resetReason_t {UNKNOWN_RESET, PWRON_RESET, RUN_PIN_RESET, SOFT_RESET, WDT_RESET, DEBUG_RESET, GLITCH_RESET, BROWNOUT_RESET};
|
||||
|
||||
resetReason_t getResetReason(void) {
|
||||
io_rw_32 *WD_reason_reg = (io_rw_32 *)(WATCHDOG_BASE + WATCHDOG_REASON_OFFSET);
|
||||
|
||||
if (watchdog_caused_reboot() && watchdog_enable_caused_reboot()) { // watchdog timer
|
||||
return WDT_RESET;
|
||||
}
|
||||
|
||||
if (*WD_reason_reg & WATCHDOG_REASON_TIMER_BITS) { // soft reset() or reboot()
|
||||
return SOFT_RESET;
|
||||
}
|
||||
|
||||
#ifdef PICO_RP2350
|
||||
// **** RP2350 is untested ****
|
||||
io_rw_32 *rrp = (io_rw_32 *)(POWMAN_BASE + POWMAN_CHIP_RESET_OFFSET);
|
||||
|
||||
if (*rrp & POWMAN_CHIP_RESET_HAD_POR_BITS) { // POR: power-on reset (brownout is separately detected on RP2350)
|
||||
return PWRON_RESET;
|
||||
}
|
||||
|
||||
if (*rrp & POWMAN_CHIP_RESET_HAD_RUN_LOW_BITS) { // RUN pin
|
||||
return RUN_PIN_RESET;
|
||||
}
|
||||
|
||||
if ((*rrp & POWMAN_CHIP_RESET_HAD_DP_RESET_REQ_BITS) || (*rrp & POWMAN_CHIP_RESET_HAD_RESCUE_BITS) || (*rrp & POWMAN_CHIP_RESET_HAD_HZD_SYS_RESET_REQ_BITS)) { // DEBUG port
|
||||
return DEBUG_RESET;
|
||||
}
|
||||
|
||||
if (*rrp & POWMAN_CHIP_RESET_HAD_GLITCH_DETECT_BITS) { // power supply glitch
|
||||
return GLITCH_RESET;
|
||||
}
|
||||
|
||||
if (*rrp & POWMAN_CHIP_RESET_HAD_BOR_BITS) { // power supply brownout reset
|
||||
return BROWNOUT_RESET;
|
||||
}
|
||||
|
||||
#else
|
||||
io_rw_32 *rrp = (io_rw_32 *)(VREG_AND_CHIP_RESET_BASE + VREG_AND_CHIP_RESET_CHIP_RESET_OFFSET);
|
||||
|
||||
if (*rrp & VREG_AND_CHIP_RESET_CHIP_RESET_HAD_POR_BITS) { // POR: power-on reset or brown-out detection
|
||||
return PWRON_RESET;
|
||||
}
|
||||
|
||||
if (*rrp & VREG_AND_CHIP_RESET_CHIP_RESET_HAD_RUN_BITS) { // RUN pin
|
||||
return RUN_PIN_RESET;
|
||||
}
|
||||
|
||||
if (*rrp & VREG_AND_CHIP_RESET_CHIP_RESET_HAD_PSM_RESTART_BITS) { // DEBUG port
|
||||
return DEBUG_RESET; // **** untested **** debug reset may just cause a rebootToBootloader()
|
||||
}
|
||||
#endif
|
||||
return UNKNOWN_RESET;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("Os")
|
||||
void *memcpyDMA(void *dest, const void *src, size_t n) {
|
||||
// Allocate a DMA channel on 1st call, reuse it every call after
|
||||
if (memcpyDMAChannel < 1) {
|
||||
memcpyDMAChannel = dma_claim_unused_channel(true);
|
||||
dma_channel_config c = dma_channel_get_default_config(memcpyDMAChannel);
|
||||
channel_config_set_transfer_data_size(&c, DMA_SIZE_32);
|
||||
channel_config_set_read_increment(&c, true);
|
||||
channel_config_set_write_increment(&c, true);
|
||||
channel_config_set_irq_quiet(&c, true);
|
||||
dma_channel_set_config(memcpyDMAChannel, &c, false);
|
||||
}
|
||||
// If there's any misalignment or too small, use regular memcpy which can handle it
|
||||
if ((n < 64) || (((uint32_t)dest) | ((uint32_t)src) | n) & 3) {
|
||||
return memcpy(dest, src, n);
|
||||
}
|
||||
|
||||
int words = n / 4;
|
||||
dma_channel_set_read_addr(memcpyDMAChannel, src, false);
|
||||
dma_channel_set_write_addr(memcpyDMAChannel, dest, false);
|
||||
dma_channel_set_trans_count(memcpyDMAChannel, words, false);
|
||||
dma_channel_start(memcpyDMAChannel);
|
||||
while (dma_channel_is_busy(memcpyDMAChannel)) {
|
||||
/* busy wait dma */
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
#pragma GCC pop_options
|
||||
|
||||
// Multicore comms FIFO
|
||||
_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;
|
||||
@@ -365,4 +479,5 @@ private:
|
||||
PIO _pio;
|
||||
int _sm;
|
||||
PIOProgram *_ccountPgm;
|
||||
int memcpyDMAChannel = -1;
|
||||
};
|
||||
|
||||
+280
-30
@@ -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_gamepad16.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,20 @@ 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,
|
||||
|
||||
|
||||
int usb_hid_poll_interval __attribute__((weak)) = 10;
|
||||
|
||||
const uint8_t *tud_descriptor_device_cb(void) {
|
||||
static tusb_desc_device_t usbd_desc_device = {
|
||||
@@ -89,7 +102,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 +110,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 +131,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 +158,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_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_absmouse[] = { TUD_HID_REPORT_DESC_ABSMOUSE(HID_REPORT_ID(1)) };
|
||||
uint8_t desc_hid_report_joystick[] = { TUD_HID_REPORT_DESC_GAMEPAD16(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 +169,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 +199,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,14 +219,17 @@ 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)) };
|
||||
uint8_t desc_local[] = { TUD_HID_REPORT_DESC_GAMEPAD16(HID_REPORT_ID(reportid)) };
|
||||
memcpy(__hid_report + offset, desc_local, sizeof(desc_local));
|
||||
}
|
||||
}
|
||||
@@ -221,9 +251,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
|
||||
@@ -235,10 +265,23 @@ void __SetupUSBDescriptor() {
|
||||
uint8_t hid_itf = __USBInstallSerial ? 2 : 0;
|
||||
uint8_t hid_desc[TUD_HID_DESC_LEN] = {
|
||||
// Interface number, string index, protocol, report descriptor len, EP In & Out address, size & polling interval
|
||||
TUD_HID_DESCRIPTOR(hid_itf, 0, HID_ITF_PROTOCOL_NONE, hid_report_len, EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 10)
|
||||
TUD_HID_DESCRIPTOR(hid_itf, 0, HID_ITF_PROTOCOL_NONE, hid_report_len, EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, (uint8_t)usb_hid_poll_interval)
|
||||
};
|
||||
|
||||
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 +303,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 +396,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();
|
||||
delayMicroseconds(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 +425,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 +435,186 @@ 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
|
||||
|
||||
#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_MINOR 6
|
||||
#define ARDUINO_PICO_REVISION 2
|
||||
#define ARDUINO_PICO_VERSION_STR "2.6.2"
|
||||
#define ARDUINO_PICO_MAJOR 4
|
||||
#define ARDUINO_PICO_MINOR 2
|
||||
#define ARDUINO_PICO_REVISION 0
|
||||
#define ARDUINO_PICO_VERSION_STR "4.2.0"
|
||||
|
||||
@@ -33,6 +33,7 @@ static std::map<int, PIOProgram*> _rxMap;
|
||||
// Duplicate a program and replace the first insn with a "set x, repl"
|
||||
static pio_program_t *pio_make_uart_prog(int repl, const pio_program_t *pg) {
|
||||
pio_program_t *p = new pio_program_t;
|
||||
memcpy(p, pg, sizeof(*p));
|
||||
p->length = pg->length;
|
||||
p->origin = pg->origin;
|
||||
uint16_t *insn = (uint16_t *)malloc(p->length * 2);
|
||||
@@ -78,9 +79,9 @@ static int _parity(int bits, int data) {
|
||||
|
||||
// We need to cache generated SerialPIOs so we can add data to them from
|
||||
// the shared handler
|
||||
static SerialPIO *_pioSP[2][4];
|
||||
static SerialPIO *_pioSP[3][4];
|
||||
static void __not_in_flash_func(_fifoIRQ)() {
|
||||
for (int p = 0; p < 2; p++) {
|
||||
for (int p = 0; p < 3; p++) {
|
||||
for (int sm = 0; sm < 4; sm++) {
|
||||
SerialPIO *s = _pioSP[p][sm];
|
||||
if (s) {
|
||||
@@ -138,6 +139,8 @@ SerialPIO::SerialPIO(pin_size_t tx, pin_size_t rx, size_t fifoSize) {
|
||||
_fifoSize = fifoSize + 1; // Always one unused entry
|
||||
_queue = new uint8_t[_fifoSize];
|
||||
mutex_init(&_mutex);
|
||||
_invertTX = false;
|
||||
_invertRX = false;
|
||||
}
|
||||
|
||||
SerialPIO::~SerialPIO() {
|
||||
@@ -145,6 +148,21 @@ SerialPIO::~SerialPIO() {
|
||||
delete[] _queue;
|
||||
}
|
||||
|
||||
static int pio_irq_0(PIO p) {
|
||||
switch (pio_get_index(p)) {
|
||||
case 0:
|
||||
return PIO0_IRQ_0;
|
||||
case 1:
|
||||
return PIO1_IRQ_0;
|
||||
#if defined(PICO_RP2350)
|
||||
case 2:
|
||||
return PIO2_IRQ_0;
|
||||
#endif
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void SerialPIO::begin(unsigned long baud, uint16_t config) {
|
||||
_overflow = false;
|
||||
_baud = baud;
|
||||
@@ -191,7 +209,7 @@ void SerialPIO::begin(unsigned long baud, uint16_t config) {
|
||||
_txBits = _bits + _stop + (_parity != UART_PARITY_NONE ? 1 : 0) + 1/*start bit*/;
|
||||
_txPgm = _getTxProgram(_txBits);
|
||||
int off;
|
||||
if (!_txPgm->prepare(&_txPIO, &_txSM, &off)) {
|
||||
if (!_txPgm->prepare(&_txPIO, &_txSM, &off, _tx, 1)) {
|
||||
DEBUGCORE("ERROR: Unable to allocate PIO TX UART, out of PIO resources\n");
|
||||
// ERROR, no free slots
|
||||
return;
|
||||
@@ -209,6 +227,7 @@ void SerialPIO::begin(unsigned long baud, uint16_t config) {
|
||||
pio_sm_exec(_txPIO, _txSM, pio_encode_mov(pio_isr, pio_osr));
|
||||
|
||||
// Start running!
|
||||
gpio_set_outover(_tx, _invertTX);
|
||||
pio_sm_set_enabled(_txPIO, _txSM, true);
|
||||
}
|
||||
if (_rx != NOPIN) {
|
||||
@@ -218,7 +237,7 @@ void SerialPIO::begin(unsigned long baud, uint16_t config) {
|
||||
_rxBits = 2 * (_bits + _stop + (_parity != UART_PARITY_NONE ? 1 : 0) + 1) - 1;
|
||||
_rxPgm = _getRxProgram(_rxBits);
|
||||
int off;
|
||||
if (!_rxPgm->prepare(&_rxPIO, &_rxSM, &off)) {
|
||||
if (!_rxPgm->prepare(&_rxPIO, &_rxSM, &off, _rx, 1)) {
|
||||
DEBUGCORE("ERROR: Unable to allocate PIO RX UART, out of PIO resources\n");
|
||||
return;
|
||||
}
|
||||
@@ -230,7 +249,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
|
||||
@@ -243,10 +262,11 @@ void SerialPIO::begin(unsigned long baud, uint16_t config) {
|
||||
case 2: pio_set_irq0_source_enabled(_rxPIO, pis_sm2_rx_fifo_not_empty, true); break;
|
||||
case 3: pio_set_irq0_source_enabled(_rxPIO, pis_sm3_rx_fifo_not_empty, true); break;
|
||||
}
|
||||
auto irqno = pio_get_index(_rxPIO) == 0 ? PIO0_IRQ_0 : PIO1_IRQ_0;
|
||||
auto irqno = pio_irq_0(_rxPIO);
|
||||
irq_set_exclusive_handler(irqno, _fifoIRQ);
|
||||
irq_set_enabled(irqno, true);
|
||||
|
||||
gpio_set_inover(_rx, _invertRX);
|
||||
pio_sm_set_enabled(_rxPIO, _rxSM, true);
|
||||
}
|
||||
|
||||
@@ -259,9 +279,12 @@ void SerialPIO::end() {
|
||||
}
|
||||
if (_tx != NOPIN) {
|
||||
pio_sm_set_enabled(_txPIO, _txSM, false);
|
||||
pio_sm_unclaim(_txPIO, _txSM);
|
||||
gpio_set_outover(_tx, 0);
|
||||
}
|
||||
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);
|
||||
@@ -270,9 +293,10 @@ void SerialPIO::end() {
|
||||
used = used || !!_pioSP[pioNum][i];
|
||||
}
|
||||
if (!used) {
|
||||
auto irqno = pioNum == 0 ? PIO0_IRQ_0 : PIO1_IRQ_0;
|
||||
auto irqno = pio_irq_0(_rxPIO);
|
||||
irq_set_enabled(irqno, false);
|
||||
}
|
||||
gpio_set_inover(_rx, 0);
|
||||
}
|
||||
_running = false;
|
||||
}
|
||||
|
||||
@@ -41,6 +41,23 @@ public:
|
||||
void begin(unsigned long baud, uint16_t config) override;
|
||||
void end() override;
|
||||
|
||||
void setInverted(bool invTx = true, bool invRx = true) {
|
||||
setInvertTX(invTx);
|
||||
setInvertRX(invRx);
|
||||
}
|
||||
bool setInvertTX(bool invert = true) {
|
||||
if (!_running) {
|
||||
_invertTX = invert;
|
||||
}
|
||||
return !_running;
|
||||
}
|
||||
bool setInvertRX(bool invert = true) {
|
||||
if (!_running) {
|
||||
_invertRX = invert;
|
||||
}
|
||||
return !_running;
|
||||
}
|
||||
|
||||
virtual int peek() override;
|
||||
virtual int read() override;
|
||||
virtual int available() override;
|
||||
@@ -51,7 +68,7 @@ public:
|
||||
using Print::write;
|
||||
operator bool() override;
|
||||
|
||||
// Not to be called by users, only from the IRQ handler. In public so that the C-language IQR callback can access it
|
||||
// Not to be called by users, only from the IRQ handler. In public so that the C-language IRQ callback can access it
|
||||
void _handleIRQ();
|
||||
|
||||
protected:
|
||||
@@ -63,6 +80,8 @@ protected:
|
||||
int _stop;
|
||||
bool _overflow;
|
||||
mutex_t _mutex;
|
||||
bool _invertTX;
|
||||
bool _invertRX;
|
||||
|
||||
PIOProgram *_txPgm;
|
||||
PIO _txPIO;
|
||||
|
||||
+117
-26
@@ -32,14 +32,24 @@ extern void serialEvent1() __attribute__((weak));
|
||||
extern void serialEvent2() __attribute__((weak));
|
||||
|
||||
bool SerialUART::setRX(pin_size_t pin) {
|
||||
constexpr uint32_t valid[2] = { __bitset({1, 13, 17, 29}) /* UART0 */,
|
||||
#ifdef PICO_RP2350B
|
||||
constexpr uint64_t valid[2] = { __bitset({1, 13, 17, 29, 33, 45}) /* UART0 */,
|
||||
__bitset({5, 9, 21, 25, 37, 41}) /* UART1 */
|
||||
};
|
||||
#else
|
||||
constexpr uint64_t valid[2] = { __bitset({1, 13, 17, 29}) /* UART0 */,
|
||||
__bitset({5, 9, 21, 25}) /* UART1 */
|
||||
};
|
||||
if ((!_running) && ((1 << pin) & valid[uart_get_index(_uart)])) {
|
||||
#endif
|
||||
if ((!_running) && ((1LL << pin) & valid[uart_get_index(_uart)])) {
|
||||
_rx = 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 {
|
||||
@@ -49,14 +59,24 @@ bool SerialUART::setRX(pin_size_t pin) {
|
||||
}
|
||||
|
||||
bool SerialUART::setTX(pin_size_t pin) {
|
||||
constexpr uint32_t valid[2] = { __bitset({0, 12, 16, 28}) /* UART0 */,
|
||||
#ifdef PICO_RP2350B
|
||||
constexpr uint64_t valid[2] = { __bitset({0, 12, 16, 28, 32, 44}) /* UART0 */,
|
||||
__bitset({4, 8, 20, 24, 36, 40}) /* UART1 */
|
||||
};
|
||||
#else
|
||||
constexpr uint64_t valid[2] = { __bitset({0, 12, 16, 28}) /* UART0 */,
|
||||
__bitset({4, 8, 20, 24}) /* UART1 */
|
||||
};
|
||||
if ((!_running) && ((1 << pin) & valid[uart_get_index(_uart)])) {
|
||||
#endif
|
||||
if ((!_running) && ((1LL << pin) & valid[uart_get_index(_uart)])) {
|
||||
_tx = 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 {
|
||||
@@ -66,14 +86,24 @@ bool SerialUART::setTX(pin_size_t pin) {
|
||||
}
|
||||
|
||||
bool SerialUART::setRTS(pin_size_t pin) {
|
||||
constexpr uint32_t valid[2] = { __bitset({3, 15, 19}) /* UART0 */,
|
||||
#ifdef PICO_RP2350B
|
||||
constexpr uint64_t valid[2] = { __bitset({3, 15, 19, 31, 35, 47}) /* UART0 */,
|
||||
__bitset({7, 11, 23, 27, 39, 43}) /* UART1 */
|
||||
};
|
||||
#else
|
||||
constexpr uint64_t valid[2] = { __bitset({3, 15, 19}) /* UART0 */,
|
||||
__bitset({7, 11, 23, 27}) /* UART1 */
|
||||
};
|
||||
if ((!_running) && ((pin == UART_PIN_NOT_DEFINED) || ((1 << pin) & valid[uart_get_index(_uart)]))) {
|
||||
#endif
|
||||
if ((!_running) && ((pin == UART_PIN_NOT_DEFINED) || ((1LL << 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 {
|
||||
@@ -83,14 +113,24 @@ bool SerialUART::setRTS(pin_size_t pin) {
|
||||
}
|
||||
|
||||
bool SerialUART::setCTS(pin_size_t pin) {
|
||||
constexpr uint32_t valid[2] = { __bitset({2, 14, 18}) /* UART0 */,
|
||||
#ifdef PICO_RP2350B
|
||||
constexpr uint64_t valid[2] = { __bitset({2, 14, 18, 30, 34, 46}) /* UART0 */,
|
||||
__bitset({6, 10, 22, 26, 38, 42}) /* UART1 */
|
||||
};
|
||||
#else
|
||||
constexpr uint64_t valid[2] = { __bitset({2, 14, 18}) /* UART0 */,
|
||||
__bitset({6, 10, 22, 26}) /* UART1 */
|
||||
};
|
||||
if ((!_running) && ((pin == UART_PIN_NOT_DEFINED) || ((1 << pin) & valid[uart_get_index(_uart)]))) {
|
||||
#endif
|
||||
if ((!_running) && ((pin == UART_PIN_NOT_DEFINED) || ((1LL << 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 {
|
||||
@@ -122,6 +162,9 @@ SerialUART::SerialUART(uart_inst_t *uart, pin_size_t tx, pin_size_t rx, pin_size
|
||||
_cts = cts;
|
||||
mutex_init(&_mutex);
|
||||
mutex_init(&_fifoMutex);
|
||||
_invertTX = false;
|
||||
_invertRX = false;
|
||||
_invertControl = false;
|
||||
}
|
||||
|
||||
static void _uart0IRQ();
|
||||
@@ -134,6 +177,24 @@ 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_outover(_tx, _invertTX ? 1 : 0);
|
||||
gpio_set_function(_rx, GPIO_FUNC_UART);
|
||||
gpio_set_inover(_rx, _invertRX ? 1 : 0);
|
||||
if (_rts != UART_PIN_NOT_DEFINED) {
|
||||
_fcnRts = gpio_get_function(_rts);
|
||||
gpio_set_function(_rts, GPIO_FUNC_UART);
|
||||
gpio_set_outover(_rts, _invertControl ? 1 : 0);
|
||||
}
|
||||
if (_cts != UART_PIN_NOT_DEFINED) {
|
||||
_fcnCts = gpio_get_function(_cts);
|
||||
gpio_set_function(_cts, GPIO_FUNC_UART);
|
||||
gpio_set_inover(_cts, _invertControl ? 1 : 0);
|
||||
}
|
||||
|
||||
uart_init(_uart, baud);
|
||||
int bits, stop;
|
||||
uart_parity_t parity;
|
||||
@@ -171,19 +232,7 @@ 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);
|
||||
uart_set_hw_flow(_uart, _cts != UART_PIN_NOT_DEFINED, _rts != UART_PIN_NOT_DEFINED);
|
||||
_writer = 0;
|
||||
_reader = 0;
|
||||
|
||||
@@ -200,6 +249,7 @@ void SerialUART::begin(unsigned long baud, uint16_t config) {
|
||||
} else {
|
||||
// Polling mode has no IRQs used
|
||||
}
|
||||
_break = false;
|
||||
_running = true;
|
||||
}
|
||||
|
||||
@@ -227,12 +277,16 @@ void SerialUART::end() {
|
||||
|
||||
// Restore pin functions
|
||||
gpio_set_function(_tx, _fcnTx);
|
||||
gpio_set_outover(_tx, 0);
|
||||
gpio_set_function(_rx, _fcnRx);
|
||||
gpio_set_inover(_rx, 0);
|
||||
if (_rts != UART_PIN_NOT_DEFINED) {
|
||||
gpio_set_function(_rts, _fcnRts);
|
||||
gpio_set_outover(_rts, 0);
|
||||
}
|
||||
if (_cts != UART_PIN_NOT_DEFINED) {
|
||||
gpio_set_function(_cts, _fcnCts);
|
||||
gpio_set_inover(_cts, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,13 +342,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 +428,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 +472,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;
|
||||
|
||||
@@ -43,6 +43,26 @@ public:
|
||||
ret &= setTX(tx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool setInvertTX(bool invert = true) {
|
||||
if (!_running) {
|
||||
_invertTX = invert;
|
||||
}
|
||||
return !_running;
|
||||
}
|
||||
bool setInvertRX(bool invert = true) {
|
||||
if (!_running) {
|
||||
_invertRX = invert;
|
||||
}
|
||||
return !_running;
|
||||
}
|
||||
bool setInvertControl(bool invert = true) {
|
||||
if (!_running) {
|
||||
_invertControl = invert;
|
||||
}
|
||||
return !_running;
|
||||
}
|
||||
|
||||
bool setFIFOSize(size_t size);
|
||||
bool setPollingMode(bool mode = true);
|
||||
|
||||
@@ -63,19 +83,30 @@ 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;
|
||||
pin_size_t _tx, _rx;
|
||||
pin_size_t _rts, _cts;
|
||||
enum gpio_function _fcnTx, _fcnRx, _fcnRts, _fcnCts;
|
||||
gpio_function_t _fcnTx, _fcnRx, _fcnRts, _fcnCts;
|
||||
int _baud;
|
||||
mutex_t _mutex;
|
||||
bool _polling = false;
|
||||
bool _overflow;
|
||||
bool _break;
|
||||
bool _invertTX, _invertRX, _invertControl;
|
||||
|
||||
// Lockless, IRQ-handled circular queue
|
||||
uint32_t _writer;
|
||||
|
||||
+34
-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,17 +171,40 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
bool SerialUSB::dtr() {
|
||||
return _dtr;
|
||||
}
|
||||
|
||||
bool SerialUSB::rts() {
|
||||
return _rts;
|
||||
}
|
||||
|
||||
extern "C" void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
|
||||
(void) itf;
|
||||
_dtr = dtr ? true : false;
|
||||
|
||||
@@ -43,9 +43,19 @@ public:
|
||||
virtual size_t write(const uint8_t *p, size_t len) override;
|
||||
using Print::write;
|
||||
operator bool() override;
|
||||
bool dtr();
|
||||
bool rts();
|
||||
|
||||
void ignoreFlowControl(bool ignore = true);
|
||||
|
||||
// ESP8266 compat
|
||||
void setDebugOutput(bool unused) {
|
||||
(void) unused;
|
||||
}
|
||||
|
||||
private:
|
||||
bool _running = false;
|
||||
bool _ignoreFlowControl = false;
|
||||
};
|
||||
|
||||
extern SerialUSB Serial;
|
||||
|
||||
@@ -30,10 +30,6 @@ public:
|
||||
}
|
||||
|
||||
~SoftwareSerial() {
|
||||
if (_invert) {
|
||||
gpio_set_outover(_tx, 0);
|
||||
gpio_set_outover(_rx, 0);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void begin(unsigned long baud = 115200) override {
|
||||
@@ -41,11 +37,9 @@ public:
|
||||
};
|
||||
|
||||
void begin(unsigned long baud, uint16_t config) override {
|
||||
setInvertTX(_invert);
|
||||
setInvertRX(_invert);
|
||||
SerialPIO::begin(baud, config);
|
||||
if (_invert) {
|
||||
gpio_set_outover(_tx, GPIO_OVERRIDE_INVERT);
|
||||
gpio_set_inover(_rx, GPIO_OVERRIDE_INVERT);
|
||||
}
|
||||
}
|
||||
|
||||
void listen() { /* noop */ }
|
||||
|
||||
@@ -0,0 +1,474 @@
|
||||
|
||||
// autogenerated from https://raw.githubusercontent.com/nayarsystems/posix_tz_db/master/zones.csv
|
||||
// by script <rp2040 arduino core>/tools/tzupdate.sh
|
||||
// Sat 20 Jan 2024 08:54:45 PM UTC
|
||||
//
|
||||
// This database is autogenerated from IANA timezone database
|
||||
// https://raw.githubusercontent.com/nayarsystems/posix_tz_db/master/zones.csv
|
||||
// (using https://www.iana.org/time-zones)
|
||||
// and can be updated on demand in this repository
|
||||
// or by yourself using the above script
|
||||
|
||||
#pragma once
|
||||
|
||||
#define TZ_Africa_Abidjan ("GMT0")
|
||||
#define TZ_Africa_Accra ("GMT0")
|
||||
#define TZ_Africa_Addis_Ababa ("EAT-3")
|
||||
#define TZ_Africa_Algiers ("CET-1")
|
||||
#define TZ_Africa_Asmara ("EAT-3")
|
||||
#define TZ_Africa_Bamako ("GMT0")
|
||||
#define TZ_Africa_Bangui ("WAT-1")
|
||||
#define TZ_Africa_Banjul ("GMT0")
|
||||
#define TZ_Africa_Bissau ("GMT0")
|
||||
#define TZ_Africa_Blantyre ("CAT-2")
|
||||
#define TZ_Africa_Brazzaville ("WAT-1")
|
||||
#define TZ_Africa_Bujumbura ("CAT-2")
|
||||
#define TZ_Africa_Cairo ("EET-2EEST,M4.5.5/0,M10.5.4/24")
|
||||
#define TZ_Africa_Casablanca ("<+01>-1")
|
||||
#define TZ_Africa_Ceuta ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Africa_Conakry ("GMT0")
|
||||
#define TZ_Africa_Dakar ("GMT0")
|
||||
#define TZ_Africa_Dar_es_Salaam ("EAT-3")
|
||||
#define TZ_Africa_Djibouti ("EAT-3")
|
||||
#define TZ_Africa_Douala ("WAT-1")
|
||||
#define TZ_Africa_El_Aaiun ("<+01>-1")
|
||||
#define TZ_Africa_Freetown ("GMT0")
|
||||
#define TZ_Africa_Gaborone ("CAT-2")
|
||||
#define TZ_Africa_Harare ("CAT-2")
|
||||
#define TZ_Africa_Johannesburg ("SAST-2")
|
||||
#define TZ_Africa_Juba ("CAT-2")
|
||||
#define TZ_Africa_Kampala ("EAT-3")
|
||||
#define TZ_Africa_Khartoum ("CAT-2")
|
||||
#define TZ_Africa_Kigali ("CAT-2")
|
||||
#define TZ_Africa_Kinshasa ("WAT-1")
|
||||
#define TZ_Africa_Lagos ("WAT-1")
|
||||
#define TZ_Africa_Libreville ("WAT-1")
|
||||
#define TZ_Africa_Lome ("GMT0")
|
||||
#define TZ_Africa_Luanda ("WAT-1")
|
||||
#define TZ_Africa_Lubumbashi ("CAT-2")
|
||||
#define TZ_Africa_Lusaka ("CAT-2")
|
||||
#define TZ_Africa_Malabo ("WAT-1")
|
||||
#define TZ_Africa_Maputo ("CAT-2")
|
||||
#define TZ_Africa_Maseru ("SAST-2")
|
||||
#define TZ_Africa_Mbabane ("SAST-2")
|
||||
#define TZ_Africa_Mogadishu ("EAT-3")
|
||||
#define TZ_Africa_Monrovia ("GMT0")
|
||||
#define TZ_Africa_Nairobi ("EAT-3")
|
||||
#define TZ_Africa_Ndjamena ("WAT-1")
|
||||
#define TZ_Africa_Niamey ("WAT-1")
|
||||
#define TZ_Africa_Nouakchott ("GMT0")
|
||||
#define TZ_Africa_Ouagadougou ("GMT0")
|
||||
#define TZ_Africa_PortomNovo ("WAT-1")
|
||||
#define TZ_Africa_Sao_Tome ("GMT0")
|
||||
#define TZ_Africa_Tripoli ("EET-2")
|
||||
#define TZ_Africa_Tunis ("CET-1")
|
||||
#define TZ_Africa_Windhoek ("CAT-2")
|
||||
#define TZ_America_Adak ("HST10HDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Anchorage ("AKST9AKDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Anguilla ("AST4")
|
||||
#define TZ_America_Antigua ("AST4")
|
||||
#define TZ_America_Araguaina ("<-03>3")
|
||||
#define TZ_America_Argentina_Buenos_Aires ("<-03>3")
|
||||
#define TZ_America_Argentina_Catamarca ("<-03>3")
|
||||
#define TZ_America_Argentina_Cordoba ("<-03>3")
|
||||
#define TZ_America_Argentina_Jujuy ("<-03>3")
|
||||
#define TZ_America_Argentina_La_Rioja ("<-03>3")
|
||||
#define TZ_America_Argentina_Mendoza ("<-03>3")
|
||||
#define TZ_America_Argentina_Rio_Gallegos ("<-03>3")
|
||||
#define TZ_America_Argentina_Salta ("<-03>3")
|
||||
#define TZ_America_Argentina_San_Juan ("<-03>3")
|
||||
#define TZ_America_Argentina_San_Luis ("<-03>3")
|
||||
#define TZ_America_Argentina_Tucuman ("<-03>3")
|
||||
#define TZ_America_Argentina_Ushuaia ("<-03>3")
|
||||
#define TZ_America_Aruba ("AST4")
|
||||
#define TZ_America_Asuncion ("<-04>4<-03>,M10.1.0/0,M3.4.0/0")
|
||||
#define TZ_America_Atikokan ("EST5")
|
||||
#define TZ_America_Bahia ("<-03>3")
|
||||
#define TZ_America_Bahia_Banderas ("CST6")
|
||||
#define TZ_America_Barbados ("AST4")
|
||||
#define TZ_America_Belem ("<-03>3")
|
||||
#define TZ_America_Belize ("CST6")
|
||||
#define TZ_America_BlancmSablon ("AST4")
|
||||
#define TZ_America_Boa_Vista ("<-04>4")
|
||||
#define TZ_America_Bogota ("<-05>5")
|
||||
#define TZ_America_Boise ("MST7MDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Cambridge_Bay ("MST7MDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Campo_Grande ("<-04>4")
|
||||
#define TZ_America_Cancun ("EST5")
|
||||
#define TZ_America_Caracas ("<-04>4")
|
||||
#define TZ_America_Cayenne ("<-03>3")
|
||||
#define TZ_America_Cayman ("EST5")
|
||||
#define TZ_America_Chicago ("CST6CDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Chihuahua ("CST6")
|
||||
#define TZ_America_Costa_Rica ("CST6")
|
||||
#define TZ_America_Creston ("MST7")
|
||||
#define TZ_America_Cuiaba ("<-04>4")
|
||||
#define TZ_America_Curacao ("AST4")
|
||||
#define TZ_America_Danmarkshavn ("GMT0")
|
||||
#define TZ_America_Dawson ("MST7")
|
||||
#define TZ_America_Dawson_Creek ("MST7")
|
||||
#define TZ_America_Denver ("MST7MDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Detroit ("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Dominica ("AST4")
|
||||
#define TZ_America_Edmonton ("MST7MDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Eirunepe ("<-05>5")
|
||||
#define TZ_America_El_Salvador ("CST6")
|
||||
#define TZ_America_Fortaleza ("<-03>3")
|
||||
#define TZ_America_Fort_Nelson ("MST7")
|
||||
#define TZ_America_Glace_Bay ("AST4ADT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Godthab ("<-02>2<-01>,M3.5.0/-1,M10.5.0/0")
|
||||
#define TZ_America_Goose_Bay ("AST4ADT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Grand_Turk ("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Grenada ("AST4")
|
||||
#define TZ_America_Guadeloupe ("AST4")
|
||||
#define TZ_America_Guatemala ("CST6")
|
||||
#define TZ_America_Guayaquil ("<-05>5")
|
||||
#define TZ_America_Guyana ("<-04>4")
|
||||
#define TZ_America_Halifax ("AST4ADT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Havana ("CST5CDT,M3.2.0/0,M11.1.0/1")
|
||||
#define TZ_America_Hermosillo ("MST7")
|
||||
#define TZ_America_Indiana_Indianapolis ("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Indiana_Knox ("CST6CDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Indiana_Marengo ("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Indiana_Petersburg ("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Indiana_Tell_City ("CST6CDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Indiana_Vevay ("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Indiana_Vincennes ("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Indiana_Winamac ("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Inuvik ("MST7MDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Iqaluit ("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Jamaica ("EST5")
|
||||
#define TZ_America_Juneau ("AKST9AKDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Kentucky_Louisville ("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Kentucky_Monticello ("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Kralendijk ("AST4")
|
||||
#define TZ_America_La_Paz ("<-04>4")
|
||||
#define TZ_America_Lima ("<-05>5")
|
||||
#define TZ_America_Los_Angeles ("PST8PDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Lower_Princes ("AST4")
|
||||
#define TZ_America_Maceio ("<-03>3")
|
||||
#define TZ_America_Managua ("CST6")
|
||||
#define TZ_America_Manaus ("<-04>4")
|
||||
#define TZ_America_Marigot ("AST4")
|
||||
#define TZ_America_Martinique ("AST4")
|
||||
#define TZ_America_Matamoros ("CST6CDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Mazatlan ("MST7")
|
||||
#define TZ_America_Menominee ("CST6CDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Merida ("CST6")
|
||||
#define TZ_America_Metlakatla ("AKST9AKDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Mexico_City ("CST6")
|
||||
#define TZ_America_Miquelon ("<-03>3<-02>,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Moncton ("AST4ADT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Monterrey ("CST6")
|
||||
#define TZ_America_Montevideo ("<-03>3")
|
||||
#define TZ_America_Montreal ("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Montserrat ("AST4")
|
||||
#define TZ_America_Nassau ("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_New_York ("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Nipigon ("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Nome ("AKST9AKDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Noronha ("<-02>2")
|
||||
#define TZ_America_North_Dakota_Beulah ("CST6CDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_North_Dakota_Center ("CST6CDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_North_Dakota_New_Salem ("CST6CDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Nuuk ("<-02>2<-01>,M3.5.0/-1,M10.5.0/0")
|
||||
#define TZ_America_Ojinaga ("CST6CDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Panama ("EST5")
|
||||
#define TZ_America_Pangnirtung ("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Paramaribo ("<-03>3")
|
||||
#define TZ_America_Phoenix ("MST7")
|
||||
#define TZ_America_PortmaumPrince ("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Port_of_Spain ("AST4")
|
||||
#define TZ_America_Porto_Velho ("<-04>4")
|
||||
#define TZ_America_Puerto_Rico ("AST4")
|
||||
#define TZ_America_Punta_Arenas ("<-03>3")
|
||||
#define TZ_America_Rainy_River ("CST6CDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Rankin_Inlet ("CST6CDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Recife ("<-03>3")
|
||||
#define TZ_America_Regina ("CST6")
|
||||
#define TZ_America_Resolute ("CST6CDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Rio_Branco ("<-05>5")
|
||||
#define TZ_America_Santarem ("<-03>3")
|
||||
#define TZ_America_Santiago ("<-04>4<-03>,M9.1.6/24,M4.1.6/24")
|
||||
#define TZ_America_Santo_Domingo ("AST4")
|
||||
#define TZ_America_Sao_Paulo ("<-03>3")
|
||||
#define TZ_America_Scoresbysund ("<-01>1<+00>,M3.5.0/0,M10.5.0/1")
|
||||
#define TZ_America_Sitka ("AKST9AKDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_St_Barthelemy ("AST4")
|
||||
#define TZ_America_St_Johns ("NST3:30NDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_St_Kitts ("AST4")
|
||||
#define TZ_America_St_Lucia ("AST4")
|
||||
#define TZ_America_St_Thomas ("AST4")
|
||||
#define TZ_America_St_Vincent ("AST4")
|
||||
#define TZ_America_Swift_Current ("CST6")
|
||||
#define TZ_America_Tegucigalpa ("CST6")
|
||||
#define TZ_America_Thule ("AST4ADT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Thunder_Bay ("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Tijuana ("PST8PDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Toronto ("EST5EDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Tortola ("AST4")
|
||||
#define TZ_America_Vancouver ("PST8PDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Whitehorse ("MST7")
|
||||
#define TZ_America_Winnipeg ("CST6CDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Yakutat ("AKST9AKDT,M3.2.0,M11.1.0")
|
||||
#define TZ_America_Yellowknife ("MST7MDT,M3.2.0,M11.1.0")
|
||||
#define TZ_Antarctica_Casey ("<+11>-11")
|
||||
#define TZ_Antarctica_Davis ("<+07>-7")
|
||||
#define TZ_Antarctica_DumontDUrville ("<+10>-10")
|
||||
#define TZ_Antarctica_Macquarie ("AEST-10AEDT,M10.1.0,M4.1.0/3")
|
||||
#define TZ_Antarctica_Mawson ("<+05>-5")
|
||||
#define TZ_Antarctica_McMurdo ("NZST-12NZDT,M9.5.0,M4.1.0/3")
|
||||
#define TZ_Antarctica_Palmer ("<-03>3")
|
||||
#define TZ_Antarctica_Rothera ("<-03>3")
|
||||
#define TZ_Antarctica_Syowa ("<+03>-3")
|
||||
#define TZ_Antarctica_Troll ("<+00>0<+02>-2,M3.5.0/1,M10.5.0/3")
|
||||
#define TZ_Antarctica_Vostok ("<+06>-6")
|
||||
#define TZ_Arctic_Longyearbyen ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Asia_Aden ("<+03>-3")
|
||||
#define TZ_Asia_Almaty ("<+06>-6")
|
||||
#define TZ_Asia_Amman ("<+03>-3")
|
||||
#define TZ_Asia_Anadyr ("<+12>-12")
|
||||
#define TZ_Asia_Aqtau ("<+05>-5")
|
||||
#define TZ_Asia_Aqtobe ("<+05>-5")
|
||||
#define TZ_Asia_Ashgabat ("<+05>-5")
|
||||
#define TZ_Asia_Atyrau ("<+05>-5")
|
||||
#define TZ_Asia_Baghdad ("<+03>-3")
|
||||
#define TZ_Asia_Bahrain ("<+03>-3")
|
||||
#define TZ_Asia_Baku ("<+04>-4")
|
||||
#define TZ_Asia_Bangkok ("<+07>-7")
|
||||
#define TZ_Asia_Barnaul ("<+07>-7")
|
||||
#define TZ_Asia_Beirut ("EET-2EEST,M3.5.0/0,M10.5.0/0")
|
||||
#define TZ_Asia_Bishkek ("<+06>-6")
|
||||
#define TZ_Asia_Brunei ("<+08>-8")
|
||||
#define TZ_Asia_Chita ("<+09>-9")
|
||||
#define TZ_Asia_Choibalsan ("<+08>-8")
|
||||
#define TZ_Asia_Colombo ("<+0530>-5:30")
|
||||
#define TZ_Asia_Damascus ("<+03>-3")
|
||||
#define TZ_Asia_Dhaka ("<+06>-6")
|
||||
#define TZ_Asia_Dili ("<+09>-9")
|
||||
#define TZ_Asia_Dubai ("<+04>-4")
|
||||
#define TZ_Asia_Dushanbe ("<+05>-5")
|
||||
#define TZ_Asia_Famagusta ("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||
#define TZ_Asia_Gaza ("EET-2EEST,M3.4.4/50,M10.4.4/50")
|
||||
#define TZ_Asia_Hebron ("EET-2EEST,M3.4.4/50,M10.4.4/50")
|
||||
#define TZ_Asia_Ho_Chi_Minh ("<+07>-7")
|
||||
#define TZ_Asia_Hong_Kong ("HKT-8")
|
||||
#define TZ_Asia_Hovd ("<+07>-7")
|
||||
#define TZ_Asia_Irkutsk ("<+08>-8")
|
||||
#define TZ_Asia_Jakarta ("WIB-7")
|
||||
#define TZ_Asia_Jayapura ("WIT-9")
|
||||
#define TZ_Asia_Jerusalem ("IST-2IDT,M3.4.4/26,M10.5.0")
|
||||
#define TZ_Asia_Kabul ("<+0430>-4:30")
|
||||
#define TZ_Asia_Kamchatka ("<+12>-12")
|
||||
#define TZ_Asia_Karachi ("PKT-5")
|
||||
#define TZ_Asia_Kathmandu ("<+0545>-5:45")
|
||||
#define TZ_Asia_Khandyga ("<+09>-9")
|
||||
#define TZ_Asia_Kolkata ("IST-5:30")
|
||||
#define TZ_Asia_Krasnoyarsk ("<+07>-7")
|
||||
#define TZ_Asia_Kuala_Lumpur ("<+08>-8")
|
||||
#define TZ_Asia_Kuching ("<+08>-8")
|
||||
#define TZ_Asia_Kuwait ("<+03>-3")
|
||||
#define TZ_Asia_Macau ("CST-8")
|
||||
#define TZ_Asia_Magadan ("<+11>-11")
|
||||
#define TZ_Asia_Makassar ("WITA-8")
|
||||
#define TZ_Asia_Manila ("PST-8")
|
||||
#define TZ_Asia_Muscat ("<+04>-4")
|
||||
#define TZ_Asia_Nicosia ("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||
#define TZ_Asia_Novokuznetsk ("<+07>-7")
|
||||
#define TZ_Asia_Novosibirsk ("<+07>-7")
|
||||
#define TZ_Asia_Omsk ("<+06>-6")
|
||||
#define TZ_Asia_Oral ("<+05>-5")
|
||||
#define TZ_Asia_Phnom_Penh ("<+07>-7")
|
||||
#define TZ_Asia_Pontianak ("WIB-7")
|
||||
#define TZ_Asia_Pyongyang ("KST-9")
|
||||
#define TZ_Asia_Qatar ("<+03>-3")
|
||||
#define TZ_Asia_Qyzylorda ("<+05>-5")
|
||||
#define TZ_Asia_Riyadh ("<+03>-3")
|
||||
#define TZ_Asia_Sakhalin ("<+11>-11")
|
||||
#define TZ_Asia_Samarkand ("<+05>-5")
|
||||
#define TZ_Asia_Seoul ("KST-9")
|
||||
#define TZ_Asia_Shanghai ("CST-8")
|
||||
#define TZ_Asia_Singapore ("<+08>-8")
|
||||
#define TZ_Asia_Srednekolymsk ("<+11>-11")
|
||||
#define TZ_Asia_Taipei ("CST-8")
|
||||
#define TZ_Asia_Tashkent ("<+05>-5")
|
||||
#define TZ_Asia_Tbilisi ("<+04>-4")
|
||||
#define TZ_Asia_Tehran ("<+0330>-3:30")
|
||||
#define TZ_Asia_Thimphu ("<+06>-6")
|
||||
#define TZ_Asia_Tokyo ("JST-9")
|
||||
#define TZ_Asia_Tomsk ("<+07>-7")
|
||||
#define TZ_Asia_Ulaanbaatar ("<+08>-8")
|
||||
#define TZ_Asia_Urumqi ("<+06>-6")
|
||||
#define TZ_Asia_UstmNera ("<+10>-10")
|
||||
#define TZ_Asia_Vientiane ("<+07>-7")
|
||||
#define TZ_Asia_Vladivostok ("<+10>-10")
|
||||
#define TZ_Asia_Yakutsk ("<+09>-9")
|
||||
#define TZ_Asia_Yangon ("<+0630>-6:30")
|
||||
#define TZ_Asia_Yekaterinburg ("<+05>-5")
|
||||
#define TZ_Asia_Yerevan ("<+04>-4")
|
||||
#define TZ_Atlantic_Azores ("<-01>1<+00>,M3.5.0/0,M10.5.0/1")
|
||||
#define TZ_Atlantic_Bermuda ("AST4ADT,M3.2.0,M11.1.0")
|
||||
#define TZ_Atlantic_Canary ("WET0WEST,M3.5.0/1,M10.5.0")
|
||||
#define TZ_Atlantic_Cape_Verde ("<-01>1")
|
||||
#define TZ_Atlantic_Faroe ("WET0WEST,M3.5.0/1,M10.5.0")
|
||||
#define TZ_Atlantic_Madeira ("WET0WEST,M3.5.0/1,M10.5.0")
|
||||
#define TZ_Atlantic_Reykjavik ("GMT0")
|
||||
#define TZ_Atlantic_South_Georgia ("<-02>2")
|
||||
#define TZ_Atlantic_Stanley ("<-03>3")
|
||||
#define TZ_Atlantic_St_Helena ("GMT0")
|
||||
#define TZ_Australia_Adelaide ("ACST-9:30ACDT,M10.1.0,M4.1.0/3")
|
||||
#define TZ_Australia_Brisbane ("AEST-10")
|
||||
#define TZ_Australia_Broken_Hill ("ACST-9:30ACDT,M10.1.0,M4.1.0/3")
|
||||
#define TZ_Australia_Currie ("AEST-10AEDT,M10.1.0,M4.1.0/3")
|
||||
#define TZ_Australia_Darwin ("ACST-9:30")
|
||||
#define TZ_Australia_Eucla ("<+0845>-8:45")
|
||||
#define TZ_Australia_Hobart ("AEST-10AEDT,M10.1.0,M4.1.0/3")
|
||||
#define TZ_Australia_Lindeman ("AEST-10")
|
||||
#define TZ_Australia_Lord_Howe ("<+1030>-10:30<+11>-11,M10.1.0,M4.1.0")
|
||||
#define TZ_Australia_Melbourne ("AEST-10AEDT,M10.1.0,M4.1.0/3")
|
||||
#define TZ_Australia_Perth ("AWST-8")
|
||||
#define TZ_Australia_Sydney ("AEST-10AEDT,M10.1.0,M4.1.0/3")
|
||||
#define TZ_Europe_Amsterdam ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Andorra ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Astrakhan ("<+04>-4")
|
||||
#define TZ_Europe_Athens ("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||
#define TZ_Europe_Belgrade ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Berlin ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Bratislava ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Brussels ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Bucharest ("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||
#define TZ_Europe_Budapest ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Busingen ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Chisinau ("EET-2EEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Copenhagen ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Dublin ("IST-1GMT0,M10.5.0,M3.5.0/1")
|
||||
#define TZ_Europe_Gibraltar ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Guernsey ("GMT0BST,M3.5.0/1,M10.5.0")
|
||||
#define TZ_Europe_Helsinki ("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||
#define TZ_Europe_Isle_of_Man ("GMT0BST,M3.5.0/1,M10.5.0")
|
||||
#define TZ_Europe_Istanbul ("<+03>-3")
|
||||
#define TZ_Europe_Jersey ("GMT0BST,M3.5.0/1,M10.5.0")
|
||||
#define TZ_Europe_Kaliningrad ("EET-2")
|
||||
#define TZ_Europe_Kiev ("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||
#define TZ_Europe_Kirov ("MSK-3")
|
||||
#define TZ_Europe_Lisbon ("WET0WEST,M3.5.0/1,M10.5.0")
|
||||
#define TZ_Europe_Ljubljana ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_London ("GMT0BST,M3.5.0/1,M10.5.0")
|
||||
#define TZ_Europe_Luxembourg ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Madrid ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Malta ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Mariehamn ("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||
#define TZ_Europe_Minsk ("<+03>-3")
|
||||
#define TZ_Europe_Monaco ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Moscow ("MSK-3")
|
||||
#define TZ_Europe_Oslo ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Paris ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Podgorica ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Prague ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Riga ("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||
#define TZ_Europe_Rome ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Samara ("<+04>-4")
|
||||
#define TZ_Europe_San_Marino ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Sarajevo ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Saratov ("<+04>-4")
|
||||
#define TZ_Europe_Simferopol ("MSK-3")
|
||||
#define TZ_Europe_Skopje ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Sofia ("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||
#define TZ_Europe_Stockholm ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Tallinn ("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||
#define TZ_Europe_Tirane ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Ulyanovsk ("<+04>-4")
|
||||
#define TZ_Europe_Uzhgorod ("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||
#define TZ_Europe_Vaduz ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Vatican ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Vienna ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Vilnius ("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||
#define TZ_Europe_Volgograd ("MSK-3")
|
||||
#define TZ_Europe_Warsaw ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Zagreb ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Europe_Zaporozhye ("EET-2EEST,M3.5.0/3,M10.5.0/4")
|
||||
#define TZ_Europe_Zurich ("CET-1CEST,M3.5.0,M10.5.0/3")
|
||||
#define TZ_Indian_Antananarivo ("EAT-3")
|
||||
#define TZ_Indian_Chagos ("<+06>-6")
|
||||
#define TZ_Indian_Christmas ("<+07>-7")
|
||||
#define TZ_Indian_Cocos ("<+0630>-6:30")
|
||||
#define TZ_Indian_Comoro ("EAT-3")
|
||||
#define TZ_Indian_Kerguelen ("<+05>-5")
|
||||
#define TZ_Indian_Mahe ("<+04>-4")
|
||||
#define TZ_Indian_Maldives ("<+05>-5")
|
||||
#define TZ_Indian_Mauritius ("<+04>-4")
|
||||
#define TZ_Indian_Mayotte ("EAT-3")
|
||||
#define TZ_Indian_Reunion ("<+04>-4")
|
||||
#define TZ_Pacific_Apia ("<+13>-13")
|
||||
#define TZ_Pacific_Auckland ("NZST-12NZDT,M9.5.0,M4.1.0/3")
|
||||
#define TZ_Pacific_Bougainville ("<+11>-11")
|
||||
#define TZ_Pacific_Chatham ("<+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45")
|
||||
#define TZ_Pacific_Chuuk ("<+10>-10")
|
||||
#define TZ_Pacific_Easter ("<-06>6<-05>,M9.1.6/22,M4.1.6/22")
|
||||
#define TZ_Pacific_Efate ("<+11>-11")
|
||||
#define TZ_Pacific_Enderbury ("<+13>-13")
|
||||
#define TZ_Pacific_Fakaofo ("<+13>-13")
|
||||
#define TZ_Pacific_Fiji ("<+12>-12")
|
||||
#define TZ_Pacific_Funafuti ("<+12>-12")
|
||||
#define TZ_Pacific_Galapagos ("<-06>6")
|
||||
#define TZ_Pacific_Gambier ("<-09>9")
|
||||
#define TZ_Pacific_Guadalcanal ("<+11>-11")
|
||||
#define TZ_Pacific_Guam ("ChST-10")
|
||||
#define TZ_Pacific_Honolulu ("HST10")
|
||||
#define TZ_Pacific_Kiritimati ("<+14>-14")
|
||||
#define TZ_Pacific_Kosrae ("<+11>-11")
|
||||
#define TZ_Pacific_Kwajalein ("<+12>-12")
|
||||
#define TZ_Pacific_Majuro ("<+12>-12")
|
||||
#define TZ_Pacific_Marquesas ("<-0930>9:30")
|
||||
#define TZ_Pacific_Midway ("SST11")
|
||||
#define TZ_Pacific_Nauru ("<+12>-12")
|
||||
#define TZ_Pacific_Niue ("<-11>11")
|
||||
#define TZ_Pacific_Norfolk ("<+11>-11<+12>,M10.1.0,M4.1.0/3")
|
||||
#define TZ_Pacific_Noumea ("<+11>-11")
|
||||
#define TZ_Pacific_Pago_Pago ("SST11")
|
||||
#define TZ_Pacific_Palau ("<+09>-9")
|
||||
#define TZ_Pacific_Pitcairn ("<-08>8")
|
||||
#define TZ_Pacific_Pohnpei ("<+11>-11")
|
||||
#define TZ_Pacific_Port_Moresby ("<+10>-10")
|
||||
#define TZ_Pacific_Rarotonga ("<-10>10")
|
||||
#define TZ_Pacific_Saipan ("ChST-10")
|
||||
#define TZ_Pacific_Tahiti ("<-10>10")
|
||||
#define TZ_Pacific_Tarawa ("<+12>-12")
|
||||
#define TZ_Pacific_Tongatapu ("<+13>-13")
|
||||
#define TZ_Pacific_Wake ("<+12>-12")
|
||||
#define TZ_Pacific_Wallis ("<+12>-12")
|
||||
#define TZ_Etc_GMT ("GMT0")
|
||||
#define TZ_Etc_GMTm0 ("GMT0")
|
||||
#define TZ_Etc_GMTm1 ("<+01>-1")
|
||||
#define TZ_Etc_GMTm2 ("<+02>-2")
|
||||
#define TZ_Etc_GMTm3 ("<+03>-3")
|
||||
#define TZ_Etc_GMTm4 ("<+04>-4")
|
||||
#define TZ_Etc_GMTm5 ("<+05>-5")
|
||||
#define TZ_Etc_GMTm6 ("<+06>-6")
|
||||
#define TZ_Etc_GMTm7 ("<+07>-7")
|
||||
#define TZ_Etc_GMTm8 ("<+08>-8")
|
||||
#define TZ_Etc_GMTm9 ("<+09>-9")
|
||||
#define TZ_Etc_GMTm10 ("<+10>-10")
|
||||
#define TZ_Etc_GMTm11 ("<+11>-11")
|
||||
#define TZ_Etc_GMTm12 ("<+12>-12")
|
||||
#define TZ_Etc_GMTm13 ("<+13>-13")
|
||||
#define TZ_Etc_GMTm14 ("<+14>-14")
|
||||
#define TZ_Etc_GMT0 ("GMT0")
|
||||
#define TZ_Etc_GMTp0 ("GMT0")
|
||||
#define TZ_Etc_GMTp1 ("<-01>1")
|
||||
#define TZ_Etc_GMTp2 ("<-02>2")
|
||||
#define TZ_Etc_GMTp3 ("<-03>3")
|
||||
#define TZ_Etc_GMTp4 ("<-04>4")
|
||||
#define TZ_Etc_GMTp5 ("<-05>5")
|
||||
#define TZ_Etc_GMTp6 ("<-06>6")
|
||||
#define TZ_Etc_GMTp7 ("<-07>7")
|
||||
#define TZ_Etc_GMTp8 ("<-08>8")
|
||||
#define TZ_Etc_GMTp9 ("<-09>9")
|
||||
#define TZ_Etc_GMTp10 ("<-10>10")
|
||||
#define TZ_Etc_GMTp11 ("<-11>11")
|
||||
#define TZ_Etc_GMTp12 ("<-12>12")
|
||||
#define TZ_Etc_UCT ("UTC0")
|
||||
#define TZ_Etc_UTC ("UTC0")
|
||||
#define TZ_Etc_Greenwich ("GMT0")
|
||||
#define TZ_Etc_Universal ("UTC0")
|
||||
#define TZ_Etc_Zulu ("UTC0")
|
||||
@@ -56,7 +56,7 @@ int64_t _stopTonePIO(alarm_id_t id, void *user_data) {
|
||||
}
|
||||
|
||||
void tone(uint8_t pin, unsigned int frequency, unsigned long duration) {
|
||||
if (pin > 29) {
|
||||
if (pin >= __GPIOCNT) {
|
||||
DEBUGCORE("ERROR: Illegal pin in tone (%d)\n", pin);
|
||||
return;
|
||||
}
|
||||
@@ -81,7 +81,7 @@ void tone(uint8_t pin, unsigned int frequency, unsigned long duration) {
|
||||
newTone = new Tone();
|
||||
newTone->pin = pin;
|
||||
pinMode(pin, OUTPUT);
|
||||
if (!_tone2Pgm.prepare(&newTone->pio, &newTone->sm, &newTone->off)) {
|
||||
if (!_tone2Pgm.prepare(&newTone->pio, &newTone->sm, &newTone->off, pin, 1)) {
|
||||
DEBUGCORE("ERROR: tone unable to start, out of PIO resources\n");
|
||||
// ERROR, no free slots
|
||||
delete newTone;
|
||||
@@ -100,6 +100,8 @@ void tone(uint8_t pin, unsigned int frequency, unsigned long duration) {
|
||||
}
|
||||
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_exec(newTone->pio, newTone->sm, pio_encode_pull(false, false));
|
||||
pio_sm_exec(newTone->pio, newTone->sm, pio_encode_mov(pio_x, pio_osr));
|
||||
pio_sm_set_enabled(newTone->pio, newTone->sm, true);
|
||||
|
||||
_toneMap.insert({pin, newTone});
|
||||
@@ -109,7 +111,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);
|
||||
}
|
||||
}
|
||||
@@ -118,7 +120,7 @@ void tone(uint8_t pin, unsigned int frequency, unsigned long duration) {
|
||||
void noTone(uint8_t pin) {
|
||||
CoreMutex m(&_toneMutex);
|
||||
|
||||
if ((pin > 29) || !m) {
|
||||
if ((pin > __GPIOCNT) || !m) {
|
||||
DEBUGCORE("ERROR: Illegal pin in tone (%d)\n", pin);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -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,9 +29,9 @@ 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);
|
||||
_map = (FMMap *)calloc(16, sizeof(FMMap));
|
||||
}
|
||||
// Pre-existing map
|
||||
for (int i = 0; i < 16; i++) {
|
||||
@@ -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;
|
||||
|
||||
+12
-15
@@ -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
|
||||
@@ -31,34 +31,31 @@ extern bool __isFreeRTOS;
|
||||
extern volatile bool __freeRTOSinitted;
|
||||
|
||||
extern "C" {
|
||||
#ifndef INC_FREERTOS_H
|
||||
struct QueueDefinition; /* Using old naming convention so as not to break kernel aware debuggers. */
|
||||
typedef struct QueueDefinition * QueueHandle_t;
|
||||
typedef QueueHandle_t SemaphoreHandle_t;
|
||||
#endif
|
||||
typedef int32_t BaseType_t;
|
||||
|
||||
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));
|
||||
extern void __freertos_idle_other_core() __attribute__((weak));
|
||||
extern void __freertos_resume_other_core() __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_task_exit_critical() __attribute__((weak));
|
||||
extern void __freertos_task_enter_critical() __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.");
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,6 +14,7 @@
|
||||
|
||||
#define ccount_wrap_target 0
|
||||
#define ccount_wrap 1
|
||||
#define ccount_pio_version 0
|
||||
|
||||
static const uint16_t ccount_program_instructions[] = {
|
||||
// .wrap_target
|
||||
@@ -27,6 +28,10 @@ static const struct pio_program ccount_program = {
|
||||
.instructions = ccount_program_instructions,
|
||||
.length = 2,
|
||||
.origin = -1,
|
||||
.pio_version = 0,
|
||||
#if PICO_PIO_VERSION > 0
|
||||
.used_gpio_ranges = 0x0
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline pio_sm_config ccount_program_get_default_config(uint offset) {
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// Use to create a callback thunk from C to C++
|
||||
// #define CCALLBACKNAME to a unique per-file name and #include this file
|
||||
// To make a CB use a define of the form:
|
||||
/*
|
||||
#define PACKETHANDLERCB(class, cbFcn) \
|
||||
(CCALLBACKNAME<void(uint8_t, uint16_t, uint8_t*, uint16_t), __COUNTER__>::func = std::bind(&class::cbFcn, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), \
|
||||
static_cast<btstack_packet_handler_t>(<CCALLBACKNAMEvoid(uint8_t, uint16_t, uint8_t*, uint16_t), __COUNTER__ - 1>::callback))
|
||||
*/
|
||||
|
||||
#include <functional>
|
||||
|
||||
// Thank you to https://stackoverflow.com/questions/66474621/multiple-non-static-callbacks-of-c-member-functions for the following beautiful hack
|
||||
|
||||
#ifndef CCALLBACKNAME
|
||||
#define CCALLBACKNAME _CCallback
|
||||
#endif
|
||||
|
||||
template <typename T, int tag>
|
||||
struct CCALLBACKNAME;
|
||||
|
||||
template <typename Ret, typename... Params, int tag>
|
||||
struct CCALLBACKNAME<Ret(Params...), tag> {
|
||||
template <typename... Args>
|
||||
static Ret callback(Args... args) {
|
||||
return func(args...);
|
||||
}
|
||||
int _tag = tag;
|
||||
static std::function<Ret(Params...)> func;
|
||||
};
|
||||
|
||||
template <typename Ret, typename... Params, int tag>
|
||||
std::function<Ret(Params...)> CCALLBACKNAME<Ret(Params...), tag>::func;
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
Flash wrappers to protect PSRAM access on the RP2350
|
||||
|
||||
Copyright (c) 2024 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <Arduino.h>
|
||||
#include <hardware/flash.h>
|
||||
|
||||
#ifdef PICO_RP2350
|
||||
#include <hardware/structs/qmi.h>
|
||||
#endif
|
||||
|
||||
#if defined(PICO_RP2350) && defined(RP2350_PSRAM_CS)
|
||||
static void __no_inline_not_in_flash_func(flushcache)() {
|
||||
// From https://forums.raspberrypi.com/viewtopic.php?t=378249#p2263677
|
||||
// Perform clean-by-set/way on all lines
|
||||
for (uint32_t i = 0; i < 2048; ++i) {
|
||||
// Use the upper 16k of the maintenance space (0x1bffc000 through 0x1bffffff):
|
||||
*(volatile uint8_t*)(XIP_SRAM_BASE + (XIP_MAINTENANCE_BASE - XIP_BASE) + i * 8u + 0x1u) = 0;
|
||||
}
|
||||
}
|
||||
#elif defined(PICO_RP2350)
|
||||
static void __no_inline_not_in_flash_func(flushcache)() {
|
||||
// Null
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
extern "C" {
|
||||
extern void __real_flash_range_erase(uint32_t flash_offs, size_t count);
|
||||
void __wrap_flash_range_erase(uint32_t flash_offs, size_t count) {
|
||||
#ifdef PICO_RP2350
|
||||
auto s = qmi_hw->m[1];
|
||||
flushcache();
|
||||
#endif
|
||||
__real_flash_range_erase(flash_offs, count);
|
||||
#ifdef PICO_RP2350
|
||||
qmi_hw->m[1] = s;
|
||||
__compiler_memory_barrier();
|
||||
#endif
|
||||
}
|
||||
|
||||
extern void __real_flash_range_program(uint32_t flash_offs, const uint8_t *data, size_t count);
|
||||
void __wrap_flash_range_program(uint32_t flash_offs, const uint8_t *data, size_t count) {
|
||||
#ifdef PICO_RP2350
|
||||
auto s = qmi_hw->m[1];
|
||||
flushcache();
|
||||
#endif
|
||||
__real_flash_range_program(flash_offs, data, count);
|
||||
#ifdef PICO_RP2350
|
||||
qmi_hw->m[1] = s;
|
||||
__compiler_memory_barrier();
|
||||
#endif
|
||||
}
|
||||
|
||||
extern void __real_flash_get_unique_id(uint8_t *id_out);
|
||||
void __wrap_flash_get_unique_id(uint8_t *id_out) {
|
||||
#ifdef PICO_RP2350
|
||||
auto s = qmi_hw->m[1];
|
||||
flushcache();
|
||||
#endif
|
||||
__real_flash_get_unique_id(id_out);
|
||||
#ifdef PICO_RP2350
|
||||
qmi_hw->m[1] = s;
|
||||
__compiler_memory_barrier();
|
||||
#endif
|
||||
}
|
||||
|
||||
extern void __real_flash_do_cmd(const uint8_t *txbuf, uint8_t *rxbuf, size_t count);
|
||||
void __wrap_flash_do_cmd(const uint8_t *txbuf, uint8_t *rxbuf, size_t count) {
|
||||
#ifdef PICO_RP2350
|
||||
auto s = qmi_hw->m[1];
|
||||
flushcache();
|
||||
#endif
|
||||
__real_flash_do_cmd(txbuf, rxbuf, count);
|
||||
#ifdef PICO_RP2350
|
||||
qmi_hw->m[1] = s;
|
||||
__compiler_memory_barrier();
|
||||
#endif
|
||||
}
|
||||
};
|
||||
+18
-6
@@ -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,20 +96,32 @@ 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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void __retarget_lock_init_recursive(_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);
|
||||
}
|
||||
|
||||
+81
-32
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
LWIP wrappers to protect against timer-based re-entrancy
|
||||
|
||||
Copyright (c) 202s Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
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
|
||||
@@ -18,48 +18,84 @@
|
||||
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 <Arduino.h>
|
||||
#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>
|
||||
#ifdef PICO_RP2040
|
||||
#include <pico/cyw43_arch.h>
|
||||
#endif
|
||||
#include <pico/mutex.h>
|
||||
#include <sys/lock.h>
|
||||
#include "_xoshiro.h"
|
||||
|
||||
// Global indicator that we're inside an LWIP block
|
||||
extern "C" {
|
||||
volatile bool __inLWIP = false;
|
||||
}
|
||||
extern void ethernet_arch_lwip_begin() __attribute__((weak));
|
||||
extern void ethernet_arch_lwip_end() __attribute__((weak));
|
||||
extern void ethernet_arch_lwip_gpio_mask() __attribute__((weak));
|
||||
extern void ethernet_arch_lwip_gpio_unmask() __attribute__((weak));
|
||||
|
||||
auto_init_recursive_mutex(__mtxLWIP);
|
||||
auto_init_recursive_mutex(__lwipMutex); // Only for case with no Ethernet or PicoW, but still doing LWIP (PPP?)
|
||||
|
||||
class LWIPMutex {
|
||||
public:
|
||||
LWIPMutex() {
|
||||
noInterrupts();
|
||||
recursive_mutex_enter_blocking(&__mtxLWIP);
|
||||
__inLWIP = true;
|
||||
_ref++;
|
||||
interrupts();
|
||||
if (ethernet_arch_lwip_gpio_mask) {
|
||||
ethernet_arch_lwip_gpio_mask();
|
||||
}
|
||||
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
|
||||
if (rp2040.isPicoW()) {
|
||||
cyw43_arch_lwip_begin();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (ethernet_arch_lwip_begin) {
|
||||
ethernet_arch_lwip_begin();
|
||||
} else {
|
||||
recursive_mutex_enter_blocking(&__lwipMutex);
|
||||
}
|
||||
}
|
||||
|
||||
~LWIPMutex() {
|
||||
noInterrupts();
|
||||
if (0 == --_ref) {
|
||||
__inLWIP = false;
|
||||
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
|
||||
if (rp2040.isPicoW()) {
|
||||
cyw43_arch_lwip_end();
|
||||
} else {
|
||||
#endif
|
||||
if (ethernet_arch_lwip_end) {
|
||||
ethernet_arch_lwip_end();
|
||||
} else {
|
||||
recursive_mutex_exit(&__lwipMutex);
|
||||
}
|
||||
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
|
||||
}
|
||||
#endif
|
||||
if (ethernet_arch_lwip_gpio_unmask) {
|
||||
ethernet_arch_lwip_gpio_unmask();
|
||||
}
|
||||
recursive_mutex_exit(&__mtxLWIP);
|
||||
interrupts();
|
||||
}
|
||||
|
||||
private:
|
||||
static int _ref;
|
||||
};
|
||||
int LWIPMutex::_ref = 0;
|
||||
|
||||
|
||||
extern "C" {
|
||||
|
||||
static XoshiroCpp::Xoshiro256PlusPlus *_lwip_rng = nullptr;
|
||||
// Random number generator for LWIP
|
||||
unsigned long __lwip_rand() {
|
||||
return (unsigned long)(*_lwip_rng)();
|
||||
}
|
||||
|
||||
// Avoid calling lwip_init multiple times
|
||||
extern void __real_lwip_init();
|
||||
void __wrap_lwip_init() {
|
||||
if (!_lwip_rng) {
|
||||
_lwip_rng = new XoshiroCpp::Xoshiro256PlusPlus(micros() * rp2040.getCycleCount());
|
||||
__real_lwip_init();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -213,19 +249,19 @@ extern "C" {
|
||||
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);
|
||||
__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);
|
||||
__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);
|
||||
__real_tcp_backlog_accepted(pcb);
|
||||
}
|
||||
extern struct udp_pcb *__real_udp_new(void);
|
||||
struct udp_pcb *__wrap_udp_new(void) {
|
||||
@@ -275,7 +311,8 @@ extern "C" {
|
||||
return __real_udp_sendto_if(pcb, p, dst_ip, dst_port, netif);
|
||||
}
|
||||
|
||||
extern void __real_sys_check_timeouts(void);
|
||||
// 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();
|
||||
@@ -323,4 +360,16 @@ extern "C" {
|
||||
__real_raw_remove(pcb);
|
||||
}
|
||||
|
||||
extern struct netif *__real_netif_add(struct netif *netif, const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw, void *state, netif_init_fn init, netif_input_fn input);
|
||||
struct netif *__wrap_netif_add(struct netif *netif, const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw, void *state, netif_init_fn init, netif_input_fn input) {
|
||||
LWIPMutex m;
|
||||
return __real_netif_add(netif, ipaddr, netmask, gw, state, init, input);
|
||||
}
|
||||
|
||||
extern void __real_netif_remove(struct netif *netif);
|
||||
void __wrap_netif_remove(struct netif *netif) {
|
||||
LWIPMutex m;
|
||||
__real_netif_remove(netif);
|
||||
}
|
||||
|
||||
}; // extern "C"
|
||||
|
||||
+42
-11
@@ -27,11 +27,9 @@
|
||||
RP2040 rp2040;
|
||||
extern "C" {
|
||||
volatile bool __otherCoreIdled = false;
|
||||
int __holdUpPendSV = 0;
|
||||
uint32_t* core1_separate_stack_address = nullptr;
|
||||
};
|
||||
|
||||
mutex_t _pioMutex;
|
||||
|
||||
extern void setup();
|
||||
extern void loop();
|
||||
|
||||
@@ -41,12 +39,14 @@ extern void startFreeRTOS() __attribute__((weak));
|
||||
bool __isFreeRTOS;
|
||||
volatile bool __freeRTOSinitted;
|
||||
|
||||
extern void __EnableBluetoothDebug(Print &);
|
||||
|
||||
// Weak empty variant initialization. May be redefined by variant files.
|
||||
void initVariant() __attribute__((weak));
|
||||
void initVariant() { }
|
||||
|
||||
// Optional 2nd core setup and loop
|
||||
bool core1_separate_stack __attribute__((weak)) = false;
|
||||
extern void setup1() __attribute__((weak));
|
||||
extern void loop1() __attribute__((weak));
|
||||
extern "C" void main1() {
|
||||
@@ -79,7 +79,7 @@ extern void __loop() {
|
||||
static struct _reent *_impure_ptr1 = nullptr;
|
||||
|
||||
extern "C" int main() {
|
||||
#if F_CPU != 125000000
|
||||
#if (defined(PICO_RP2040) && (F_CPU != 125000000)) || (defined(PICO_RP2350) && (F_CPU != 150000000))
|
||||
set_sys_clock_khz(F_CPU / 1000, true);
|
||||
#endif
|
||||
|
||||
@@ -88,12 +88,10 @@ extern "C" int main() {
|
||||
|
||||
// Allocate impure_ptr (newlib temps) if there is a 2nd core running
|
||||
if (!__isFreeRTOS && (setup1 || loop1)) {
|
||||
_impure_ptr1 = (struct _reent*)calloc(sizeof(struct _reent), 1);
|
||||
_impure_ptr1 = (struct _reent*)calloc(1, sizeof(struct _reent));
|
||||
_REENT_INIT_PTR(_impure_ptr1);
|
||||
}
|
||||
|
||||
mutex_init(&_pioMutex);
|
||||
|
||||
rp2040.begin();
|
||||
|
||||
initVariant();
|
||||
@@ -122,10 +120,12 @@ extern "C" int main() {
|
||||
#if defined DEBUG_RP2040_PORT
|
||||
if (!__isFreeRTOS) {
|
||||
DEBUG_RP2040_PORT.begin(115200);
|
||||
#if (defined(ENABLE_BLUETOOTH) || defined(ENABLE_BLE)) && defined(DEBUG_RP2040_BLUETOOTH)
|
||||
__EnableBluetoothDebug(DEBUG_RP2040_PORT);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef NO_USB
|
||||
if (!__isFreeRTOS) {
|
||||
if (setup1 || loop1) {
|
||||
rp2040.fifo.begin(2);
|
||||
@@ -134,12 +134,15 @@ extern "C" int main() {
|
||||
}
|
||||
rp2040.fifo.registerCore();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!__isFreeRTOS) {
|
||||
if (setup1 || loop1) {
|
||||
delay(1); // Needed to make Picoprobe upload start 2nd core
|
||||
multicore_launch_core1(main1);
|
||||
if (core1_separate_stack) {
|
||||
core1_separate_stack_address = (uint32_t*)malloc(0x2000);
|
||||
multicore_launch_core1_with_stack(main1, core1_separate_stack_address, 0x2000);
|
||||
} else {
|
||||
multicore_launch_core1(main1);
|
||||
}
|
||||
}
|
||||
setup();
|
||||
while (true) {
|
||||
@@ -165,6 +168,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;
|
||||
@@ -172,3 +176,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 = "";
|
||||
|
||||
@@ -19,11 +19,25 @@
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <malloc.h>
|
||||
#include <reent.h>
|
||||
#include "psram.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" struct mallinfo __real_mallinfo();
|
||||
|
||||
#ifdef RP2350_PSRAM_CS
|
||||
extern "C" {
|
||||
extern uint8_t __psram_start__;
|
||||
extern uint8_t __psram_heap_start__;
|
||||
void __malloc_lock(struct _reent *ptr);
|
||||
void __malloc_unlock(struct _reent *ptr);
|
||||
static void *__ram_start = (void *)0x20000000; // TODO - Is there a SDK exposed variable/macro?
|
||||
}
|
||||
#endif
|
||||
|
||||
extern "C" void *__wrap_malloc(size_t size) {
|
||||
noInterrupts();
|
||||
@@ -39,15 +53,72 @@ extern "C" void *__wrap_calloc(size_t count, size_t size) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
extern "C" void *__wrap_realloc(void *mem, size_t size) {
|
||||
#ifdef RP2350_PSRAM_CS
|
||||
// Utilize the existing malloc lock infrastructure and interrupt blocking
|
||||
// to work with multicore and FreeRTOS
|
||||
extern "C" void *pmalloc(size_t size) {
|
||||
noInterrupts();
|
||||
void *rc = __real_realloc(mem, size);
|
||||
__malloc_lock(__getreent());
|
||||
auto rc = __psram_malloc(size);
|
||||
__malloc_unlock(__getreent());
|
||||
interrupts();
|
||||
return rc;
|
||||
}
|
||||
|
||||
extern "C" void *pcalloc(size_t count, size_t size) {
|
||||
noInterrupts();
|
||||
__malloc_lock(__getreent());
|
||||
auto rc = __psram_calloc(count, size);
|
||||
__malloc_unlock(__getreent());
|
||||
interrupts();
|
||||
return rc;
|
||||
}
|
||||
#else
|
||||
// No PSRAM, always fail
|
||||
extern "C" void *pmalloc(size_t size) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
extern "C" void *pcalloc(size_t count, size_t size) {
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
extern "C" void *__wrap_realloc(void *mem, size_t size) {
|
||||
void *rc;
|
||||
noInterrupts();
|
||||
#ifdef RP2350_PSRAM_CS
|
||||
if (mem && (mem < __ram_start)) {
|
||||
rc = __psram_realloc(mem, size);
|
||||
} else {
|
||||
rc = __real_realloc(mem, size);
|
||||
}
|
||||
#else
|
||||
rc = __real_realloc(mem, size);
|
||||
#endif
|
||||
interrupts();
|
||||
return rc;
|
||||
}
|
||||
|
||||
extern "C" void __wrap_free(void *mem) {
|
||||
noInterrupts();
|
||||
#ifdef RP2350_PSRAM_CS
|
||||
if (mem && (mem < __ram_start)) {
|
||||
__psram_free(mem);
|
||||
} else {
|
||||
__real_free(mem);
|
||||
}
|
||||
#else
|
||||
__real_free(mem);
|
||||
#endif
|
||||
interrupts();
|
||||
}
|
||||
|
||||
extern "C" struct mallinfo __wrap_mallinfo() {
|
||||
noInterrupts();
|
||||
__malloc_lock(__getreent());
|
||||
auto ret = __real_mallinfo();
|
||||
__malloc_unlock(__getreent());
|
||||
interrupts();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
@@ -39,15 +39,13 @@ 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) {
|
||||
// Tell PIO to initially drive output-high on the selected pin, then map PIO
|
||||
// onto that pin with the IO muxes.
|
||||
pio_sm_set_pins_with_mask(pio, sm, 1u << pin_tx, 1u << pin_tx);
|
||||
pio_sm_set_pindirs_with_mask(pio, sm, 1u << pin_tx, 1u << pin_tx);
|
||||
pio_sm_set_set_pins(pio, sm, pin_tx, 1);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, pin_tx, 1, true);
|
||||
pio_gpio_init(pio, pin_tx);
|
||||
|
||||
pio_sm_config c = pio_tx_program_get_default_config(offset);
|
||||
@@ -68,7 +66,7 @@ static inline void pio_tx_program_init(PIO pio, uint sm, uint offset, uint pin_t
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
|
||||
|
||||
.program pio_rx
|
||||
|
||||
@@ -90,7 +88,6 @@ wait_half:
|
||||
|
||||
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
|
||||
@@ -12,6 +14,7 @@
|
||||
|
||||
#define pio_tx_wrap_target 0
|
||||
#define pio_tx_wrap 5
|
||||
#define pio_tx_pio_version 0
|
||||
|
||||
static const uint16_t pio_tx_program_instructions[] = {
|
||||
// .wrap_target
|
||||
@@ -29,6 +32,10 @@ static const struct pio_program pio_tx_program = {
|
||||
.instructions = pio_tx_program_instructions,
|
||||
.length = 6,
|
||||
.origin = -1,
|
||||
.pio_version = 0,
|
||||
#if PICO_PIO_VERSION > 0
|
||||
.used_gpio_ranges = 0x0
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline pio_sm_config pio_tx_program_get_default_config(uint offset) {
|
||||
@@ -41,8 +48,8 @@ static inline pio_sm_config pio_tx_program_get_default_config(uint offset) {
|
||||
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
|
||||
// onto that pin with the IO muxes.
|
||||
pio_sm_set_pins_with_mask(pio, sm, 1u << pin_tx, 1u << pin_tx);
|
||||
pio_sm_set_pindirs_with_mask(pio, sm, 1u << pin_tx, 1u << pin_tx);
|
||||
pio_sm_set_set_pins(pio, sm, pin_tx, 1);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, pin_tx, 1, true);
|
||||
pio_gpio_init(pio, pin_tx);
|
||||
pio_sm_config c = pio_tx_program_get_default_config(offset);
|
||||
// OUT shifts to right, no autopull
|
||||
@@ -65,6 +72,7 @@ static inline void pio_tx_program_init(PIO pio, uint sm, uint offset, uint pin_t
|
||||
|
||||
#define pio_rx_wrap_target 0
|
||||
#define pio_rx_wrap 6
|
||||
#define pio_rx_pio_version 0
|
||||
|
||||
static const uint16_t pio_rx_program_instructions[] = {
|
||||
// .wrap_target
|
||||
@@ -83,6 +91,10 @@ static const struct pio_program pio_rx_program = {
|
||||
.instructions = pio_rx_program_instructions,
|
||||
.length = 7,
|
||||
.origin = -1,
|
||||
.pio_version = 0,
|
||||
#if PICO_PIO_VERSION > 0
|
||||
.used_gpio_ranges = 0x0
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline pio_sm_config pio_rx_program_get_default_config(uint offset) {
|
||||
|
||||
+39
-13
@@ -33,7 +33,9 @@
|
||||
|
||||
extern "C" int errno;
|
||||
|
||||
extern "C" ssize_t _write(int fd, const void *buf, size_t count) {
|
||||
extern "C"
|
||||
__attribute((weak))
|
||||
ssize_t _write(int fd, const void *buf, size_t count) {
|
||||
#if defined DEBUG_RP2040_PORT
|
||||
(void) fd;
|
||||
return DEBUG_RP2040_PORT.write((const char *)buf, count);
|
||||
@@ -45,7 +47,9 @@ extern "C" ssize_t _write(int fd, const void *buf, size_t count) {
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" int _chown(const char *path, uid_t owner, gid_t group) {
|
||||
extern "C"
|
||||
__attribute((weak))
|
||||
int _chown(const char *path, uid_t owner, gid_t group) {
|
||||
(void) path;
|
||||
(void) owner;
|
||||
(void) group;
|
||||
@@ -53,7 +57,9 @@ extern "C" int _chown(const char *path, uid_t owner, gid_t group) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" int _close(int fd) {
|
||||
extern "C"
|
||||
__attribute((weak))
|
||||
int _close(int fd) {
|
||||
(void) fd;
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
@@ -72,7 +78,9 @@ extern "C" int _fork(void) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" int _fstat(int fd, struct stat *st) {
|
||||
extern "C"
|
||||
__attribute((weak))
|
||||
int _fstat(int fd, struct stat *st) {
|
||||
(void) fd;
|
||||
(void) st;
|
||||
errno = ENOSYS;
|
||||
@@ -115,7 +123,9 @@ extern "C" void __setSystemTime(unsigned long long sec, unsigned long usec) {
|
||||
__timedelta_us = newnow_us - now_us;
|
||||
}
|
||||
|
||||
extern "C" int _isatty(int file) {
|
||||
extern "C"
|
||||
__attribute((weak))
|
||||
int _isatty(int file) {
|
||||
(void) file;
|
||||
errno = ENOSYS;
|
||||
return 0;
|
||||
@@ -128,14 +138,18 @@ extern "C" int _kill(int pid, int sig) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" int _link(char *existing, char *newlink) {
|
||||
extern "C"
|
||||
__attribute((weak))
|
||||
int _link(char *existing, char *newlink) {
|
||||
(void) existing;
|
||||
(void) newlink;
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" int _lseek(int file, int ptr, int dir) {
|
||||
extern "C"
|
||||
__attribute((weak))
|
||||
int _lseek(int file, int ptr, int dir) {
|
||||
(void) file;
|
||||
(void) ptr;
|
||||
(void) dir;
|
||||
@@ -143,7 +157,9 @@ extern "C" int _lseek(int file, int ptr, int dir) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" int _open(char *file, int flags, int mode) {
|
||||
extern "C"
|
||||
__attribute((weak))
|
||||
int _open(char *file, int flags, int mode) {
|
||||
(void) file;
|
||||
(void) flags;
|
||||
(void) mode;
|
||||
@@ -151,7 +167,9 @@ extern "C" int _open(char *file, int flags, int mode) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" int _read(int file, char *ptr, int len) {
|
||||
extern "C"
|
||||
__attribute((weak))
|
||||
int _read(int file, char *ptr, int len) {
|
||||
(void) file;
|
||||
(void) ptr;
|
||||
(void) len;
|
||||
@@ -159,7 +177,9 @@ extern "C" int _read(int file, char *ptr, int len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" int _readlink(const char *path, char *buf, size_t bufsize) {
|
||||
extern "C"
|
||||
__attribute((weak))
|
||||
int _readlink(const char *path, char *buf, size_t bufsize) {
|
||||
(void) path;
|
||||
(void) buf;
|
||||
(void) bufsize;
|
||||
@@ -167,14 +187,18 @@ extern "C" int _readlink(const char *path, char *buf, size_t bufsize) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" int _stat(const char *file, struct stat *st) {
|
||||
extern "C"
|
||||
__attribute((weak))
|
||||
int _stat(const char *file, struct stat *st) {
|
||||
(void) file;
|
||||
(void) st;
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" int _symlink(const char *path1, const char *path2) {
|
||||
extern "C"
|
||||
__attribute((weak))
|
||||
int _symlink(const char *path1, const char *path2) {
|
||||
(void) path1;
|
||||
(void) path2;
|
||||
errno = ENOSYS;
|
||||
@@ -187,7 +211,9 @@ extern "C" clock_t _times(struct tms *buf) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" int _unlink(char *name) {
|
||||
extern "C"
|
||||
__attribute((weak))
|
||||
int _unlink(char *name) {
|
||||
(void) name;
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
|
||||
@@ -0,0 +1,437 @@
|
||||
// Originally from https://github.com/sparkfun/sparkfun-pico
|
||||
/**
|
||||
@file sfe_psram.c
|
||||
|
||||
@brief This file contains a function that is used to detect and initialize PSRAM on
|
||||
SparkFun rp2350 boards.
|
||||
*/
|
||||
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2024 SparkFun Electronics
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions: The
|
||||
above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
|
||||
"AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Hacked by Earle Philhower to work with the Arduino-Pico core
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef RP2350_PSRAM_CS
|
||||
|
||||
#include <hardware/address_mapped.h>
|
||||
#include <hardware/clocks.h>
|
||||
#include <hardware/gpio.h>
|
||||
#include <hardware/regs/addressmap.h>
|
||||
#include <hardware/spi.h>
|
||||
#include <hardware/structs/qmi.h>
|
||||
#include <hardware/structs/xip_ctrl.h>
|
||||
#include <pico/runtime_init.h>
|
||||
|
||||
// Include TLSF in this compilation unit
|
||||
#include "../../lib/tlsf/tlsf.c"
|
||||
static tlsf_t _mem_heap = nullptr;
|
||||
static pool_t _mem_psram_pool = nullptr;
|
||||
|
||||
// PSRAM heap minus PSRAM global/static variables from the linker
|
||||
extern "C" {
|
||||
extern uint8_t __psram_start__;
|
||||
extern uint8_t __psram_heap_start__;
|
||||
}
|
||||
|
||||
static bool _bInitalized = false;
|
||||
size_t __psram_size = 0;
|
||||
size_t __psram_heap_size = 0;
|
||||
|
||||
#define PICO_RUNTIME_INIT_PSRAM "11001" // Towards the end, after alarms
|
||||
|
||||
#ifndef RP2350_PSRAM_MAX_SELECT_FS64
|
||||
#define RP2350_PSRAM_MAX_SELECT_FS64 (125'000'000)
|
||||
#endif
|
||||
|
||||
#ifndef RP2350_PSRAM_MIN_DESELECT_FS
|
||||
#define RP2350_PSRAM_MIN_DESELECT_FS (50'000'000)
|
||||
#endif
|
||||
|
||||
#ifndef RP2350_PSRAM_MAX_SCK_HZ
|
||||
#define RP2350_PSRAM_MAX_SCK_HZ (109'000'000)
|
||||
#endif
|
||||
|
||||
#ifndef RP2350_PSRAM_ID
|
||||
#define RP2350_PSRAM_ID (0x5D)
|
||||
#endif
|
||||
|
||||
// DETAILS/
|
||||
//
|
||||
// SparkFun RP2350 boards use the following PSRAM IC:
|
||||
//
|
||||
// apmemory APS6404L-3SQR-ZR
|
||||
// https://www.mouser.com/ProductDetail/AP-Memory/APS6404L-3SQR-ZR?qs=IS%252B4QmGtzzpDOdsCIglviw%3D%3D
|
||||
//
|
||||
// The origin of this logic is from the Circuit Python code that was downloaded from:
|
||||
// https://github.com/raspberrypi/pico-sdk-rp2350/issues/12#issuecomment-2055274428
|
||||
//
|
||||
|
||||
// Details on the PSRAM IC that are used during setup/configuration of PSRAM on SparkFun RP2350 boards.
|
||||
|
||||
// For PSRAM timing calculations - to use int math, we work in femto seconds (fs) (1e-15),
|
||||
// NOTE: This idea is from micro python work on psram..
|
||||
|
||||
#define SFE_SEC_TO_FS 1000000000000000ll
|
||||
|
||||
// max select pulse width = 8us => 8e6 ns => 8000 ns => 8000 * 1e6 fs => 8000e6 fs
|
||||
// Additionally, the MAX select is in units of 64 clock cycles - will use a constant that
|
||||
// takes this into account - so 8000e6 fs / 64 = 125e6 fs
|
||||
|
||||
const uint32_t SFE_PSRAM_MAX_SELECT_FS64 = RP2350_PSRAM_MAX_SELECT_FS64;
|
||||
|
||||
// min deselect pulse width = 50ns => 50 * 1e6 fs => 50e7 fs
|
||||
const uint32_t SFE_PSRAM_MIN_DESELECT_FS = RP2350_PSRAM_MIN_DESELECT_FS;
|
||||
|
||||
// from psram datasheet - max Freq with VDDat 3.3v - SparkFun RP2350 boards run at 3.3v.
|
||||
// If VDD = 3.0 Max Freq is 133 Mhz
|
||||
const uint32_t SFE_PSRAM_MAX_SCK_HZ = RP2350_PSRAM_MAX_SCK_HZ;
|
||||
|
||||
// PSRAM SPI command codes
|
||||
const uint8_t PSRAM_CMD_QUAD_END = 0xF5;
|
||||
const uint8_t PSRAM_CMD_QUAD_ENABLE = 0x35;
|
||||
const uint8_t PSRAM_CMD_READ_ID = 0x9F;
|
||||
const uint8_t PSRAM_CMD_RSTEN = 0x66;
|
||||
const uint8_t PSRAM_CMD_RST = 0x99;
|
||||
const uint8_t PSRAM_CMD_QUAD_READ = 0xEB;
|
||||
const uint8_t PSRAM_CMD_QUAD_WRITE = 0x38;
|
||||
const uint8_t PSRAM_CMD_NOOP = 0xFF;
|
||||
|
||||
const uint8_t PSRAM_ID = RP2350_PSRAM_ID;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @brief Communicate directly with the PSRAM IC - validate it is present and return the size
|
||||
///
|
||||
/// @return size_t The size of the PSRAM
|
||||
///
|
||||
/// @note This function expects the CS pin set
|
||||
static size_t __no_inline_not_in_flash_func(get_psram_size)(void) {
|
||||
size_t psram_size = 0;
|
||||
uint32_t intr_stash = save_and_disable_interrupts();
|
||||
|
||||
// Try and read the PSRAM ID via direct_csr.
|
||||
qmi_hw->direct_csr = 30 << QMI_DIRECT_CSR_CLKDIV_LSB | QMI_DIRECT_CSR_EN_BITS;
|
||||
|
||||
// Need to poll for the cooldown on the last XIP transfer to expire
|
||||
// (via direct-mode BUSY flag) before it is safe to perform the first
|
||||
// direct-mode operation
|
||||
while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) {
|
||||
}
|
||||
|
||||
// Exit out of QMI in case we've inited already
|
||||
qmi_hw->direct_csr |= QMI_DIRECT_CSR_ASSERT_CS1N_BITS;
|
||||
|
||||
// Transmit the command to exit QPI quad mode - read ID as standard SPI
|
||||
qmi_hw->direct_tx =
|
||||
QMI_DIRECT_TX_OE_BITS | QMI_DIRECT_TX_IWIDTH_VALUE_Q << QMI_DIRECT_TX_IWIDTH_LSB | PSRAM_CMD_QUAD_END;
|
||||
|
||||
while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) {
|
||||
}
|
||||
|
||||
(void)qmi_hw->direct_rx;
|
||||
qmi_hw->direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS);
|
||||
|
||||
// Read the id
|
||||
qmi_hw->direct_csr |= QMI_DIRECT_CSR_ASSERT_CS1N_BITS;
|
||||
uint8_t kgd = 0;
|
||||
uint8_t eid = 0;
|
||||
for (size_t i = 0; i < 7; i++) {
|
||||
qmi_hw->direct_tx = (i == 0 ? PSRAM_CMD_READ_ID : PSRAM_CMD_NOOP);
|
||||
|
||||
while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_TXEMPTY_BITS) == 0) {
|
||||
}
|
||||
while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) {
|
||||
}
|
||||
if (i == 5) {
|
||||
kgd = qmi_hw->direct_rx;
|
||||
} else if (i == 6) {
|
||||
eid = qmi_hw->direct_rx;
|
||||
} else {
|
||||
(void)qmi_hw->direct_rx; // just read and discard
|
||||
}
|
||||
}
|
||||
|
||||
// Disable direct csr.
|
||||
qmi_hw->direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS | QMI_DIRECT_CSR_EN_BITS);
|
||||
|
||||
// is this the PSRAM we're looking for obi-wan?
|
||||
if (kgd == PSRAM_ID) {
|
||||
// PSRAM size
|
||||
psram_size = 1024 * 1024; // 1 MiB
|
||||
uint8_t size_id = eid >> 5;
|
||||
if (eid == 0x26 || size_id == 2) {
|
||||
psram_size *= 8;
|
||||
} else if (size_id == 0) {
|
||||
psram_size *= 2;
|
||||
} else if (size_id == 1) {
|
||||
psram_size *= 4;
|
||||
}
|
||||
}
|
||||
restore_interrupts(intr_stash);
|
||||
return psram_size;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @brief Update the PSRAM timing configuration based on system clock
|
||||
///
|
||||
/// @note This function expects interrupts to be enabled on entry
|
||||
|
||||
static void __no_inline_not_in_flash_func(set_psram_timing)(void) {
|
||||
// Get secs / cycle for the system clock - get before disabling interrupts.
|
||||
uint32_t sysHz = (uint32_t)clock_get_hz(clk_sys);
|
||||
|
||||
// Calculate the clock divider - goal to get clock used for PSRAM <= what
|
||||
// the PSRAM IC can handle - which is defined in SFE_PSRAM_MAX_SCK_HZ
|
||||
volatile uint8_t clockDivider = (sysHz + SFE_PSRAM_MAX_SCK_HZ - 1) / SFE_PSRAM_MAX_SCK_HZ;
|
||||
|
||||
uint32_t intr_stash = save_and_disable_interrupts();
|
||||
|
||||
// Get the clock femto seconds per cycle.
|
||||
|
||||
uint32_t fsPerCycle = SFE_SEC_TO_FS / sysHz;
|
||||
|
||||
// the maxSelect value is defined in units of 64 clock cycles
|
||||
// So maxFS / (64 * fsPerCycle) = maxSelect = SFE_PSRAM_MAX_SELECT_FS64/fsPerCycle
|
||||
volatile uint8_t maxSelect = SFE_PSRAM_MAX_SELECT_FS64 / fsPerCycle;
|
||||
|
||||
// minDeselect time - in system clock cycle
|
||||
// Must be higher than 50ns (min deselect time for PSRAM) so add a fsPerCycle - 1 to round up
|
||||
// So minFS/fsPerCycle = minDeselect = SFE_PSRAM_MIN_DESELECT_FS/fsPerCycle
|
||||
|
||||
volatile uint8_t minDeselect = (SFE_PSRAM_MIN_DESELECT_FS + fsPerCycle - 1) / fsPerCycle;
|
||||
|
||||
// printf("Max Select: %d, Min Deselect: %d, clock divider: %d\n", maxSelect, minDeselect, clockDivider);
|
||||
|
||||
qmi_hw->m[1].timing = QMI_M1_TIMING_PAGEBREAK_VALUE_1024 << QMI_M1_TIMING_PAGEBREAK_LSB | // Break between pages.
|
||||
3 << QMI_M1_TIMING_SELECT_HOLD_LSB | // Delay releasing CS for 3 extra system cycles.
|
||||
1 << QMI_M1_TIMING_COOLDOWN_LSB | 1 << QMI_M1_TIMING_RXDELAY_LSB |
|
||||
maxSelect << QMI_M1_TIMING_MAX_SELECT_LSB | minDeselect << QMI_M1_TIMING_MIN_DESELECT_LSB |
|
||||
clockDivider << QMI_M1_TIMING_CLKDIV_LSB;
|
||||
|
||||
restore_interrupts(intr_stash);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// @brief The setup_psram function - note that this is not in flash
|
||||
///
|
||||
///
|
||||
static void __no_inline_not_in_flash_func(runtime_init_setup_psram)(/*uint32_t psram_cs_pin*/) {
|
||||
// Set the PSRAM CS pin in the SDK
|
||||
gpio_set_function(RP2350_PSRAM_CS, GPIO_FUNC_XIP_CS1);
|
||||
|
||||
// start with zero size
|
||||
size_t psram_size = get_psram_size();
|
||||
|
||||
// No PSRAM - no dice
|
||||
if (psram_size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t intr_stash = save_and_disable_interrupts();
|
||||
// Enable quad mode.
|
||||
qmi_hw->direct_csr = 30 << QMI_DIRECT_CSR_CLKDIV_LSB | QMI_DIRECT_CSR_EN_BITS;
|
||||
|
||||
// Need to poll for the cooldown on the last XIP transfer to expire
|
||||
// (via direct-mode BUSY flag) before it is safe to perform the first
|
||||
// direct-mode operation
|
||||
while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) {
|
||||
}
|
||||
|
||||
// RESETEN, RESET and quad enable
|
||||
for (uint8_t i = 0; i < 3; i++) {
|
||||
qmi_hw->direct_csr |= QMI_DIRECT_CSR_ASSERT_CS1N_BITS;
|
||||
if (i == 0) {
|
||||
qmi_hw->direct_tx = PSRAM_CMD_RSTEN;
|
||||
} else if (i == 1) {
|
||||
qmi_hw->direct_tx = PSRAM_CMD_RST;
|
||||
} else {
|
||||
qmi_hw->direct_tx = PSRAM_CMD_QUAD_ENABLE;
|
||||
}
|
||||
|
||||
while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) {
|
||||
}
|
||||
qmi_hw->direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS);
|
||||
for (size_t j = 0; j < 20; j++) {
|
||||
asm("nop");
|
||||
}
|
||||
|
||||
(void)qmi_hw->direct_rx;
|
||||
}
|
||||
|
||||
// Disable direct csr.
|
||||
qmi_hw->direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS | QMI_DIRECT_CSR_EN_BITS);
|
||||
|
||||
// check our interrupts and setup the timing
|
||||
restore_interrupts(intr_stash);
|
||||
set_psram_timing();
|
||||
|
||||
// and now stash interrupts again
|
||||
intr_stash = save_and_disable_interrupts();
|
||||
|
||||
qmi_hw->m[1].rfmt = (QMI_M1_RFMT_PREFIX_WIDTH_VALUE_Q << QMI_M1_RFMT_PREFIX_WIDTH_LSB |
|
||||
QMI_M1_RFMT_ADDR_WIDTH_VALUE_Q << QMI_M1_RFMT_ADDR_WIDTH_LSB |
|
||||
QMI_M1_RFMT_SUFFIX_WIDTH_VALUE_Q << QMI_M1_RFMT_SUFFIX_WIDTH_LSB |
|
||||
QMI_M1_RFMT_DUMMY_WIDTH_VALUE_Q << QMI_M1_RFMT_DUMMY_WIDTH_LSB |
|
||||
QMI_M1_RFMT_DUMMY_LEN_VALUE_24 << QMI_M1_RFMT_DUMMY_LEN_LSB |
|
||||
QMI_M1_RFMT_DATA_WIDTH_VALUE_Q << QMI_M1_RFMT_DATA_WIDTH_LSB |
|
||||
QMI_M1_RFMT_PREFIX_LEN_VALUE_8 << QMI_M1_RFMT_PREFIX_LEN_LSB |
|
||||
QMI_M1_RFMT_SUFFIX_LEN_VALUE_NONE << QMI_M1_RFMT_SUFFIX_LEN_LSB);
|
||||
|
||||
qmi_hw->m[1].rcmd = PSRAM_CMD_QUAD_READ << QMI_M1_RCMD_PREFIX_LSB | 0 << QMI_M1_RCMD_SUFFIX_LSB;
|
||||
|
||||
qmi_hw->m[1].wfmt = (QMI_M1_WFMT_PREFIX_WIDTH_VALUE_Q << QMI_M1_WFMT_PREFIX_WIDTH_LSB |
|
||||
QMI_M1_WFMT_ADDR_WIDTH_VALUE_Q << QMI_M1_WFMT_ADDR_WIDTH_LSB |
|
||||
QMI_M1_WFMT_SUFFIX_WIDTH_VALUE_Q << QMI_M1_WFMT_SUFFIX_WIDTH_LSB |
|
||||
QMI_M1_WFMT_DUMMY_WIDTH_VALUE_Q << QMI_M1_WFMT_DUMMY_WIDTH_LSB |
|
||||
QMI_M1_WFMT_DUMMY_LEN_VALUE_NONE << QMI_M1_WFMT_DUMMY_LEN_LSB |
|
||||
QMI_M1_WFMT_DATA_WIDTH_VALUE_Q << QMI_M1_WFMT_DATA_WIDTH_LSB |
|
||||
QMI_M1_WFMT_PREFIX_LEN_VALUE_8 << QMI_M1_WFMT_PREFIX_LEN_LSB |
|
||||
QMI_M1_WFMT_SUFFIX_LEN_VALUE_NONE << QMI_M1_WFMT_SUFFIX_LEN_LSB);
|
||||
|
||||
qmi_hw->m[1].wcmd = PSRAM_CMD_QUAD_WRITE << QMI_M1_WCMD_PREFIX_LSB | 0 << QMI_M1_WCMD_SUFFIX_LSB;
|
||||
|
||||
// Mark that we can write to PSRAM.
|
||||
xip_ctrl_hw->ctrl |= XIP_CTRL_WRITABLE_M1_BITS;
|
||||
|
||||
restore_interrupts(intr_stash);
|
||||
|
||||
__psram_size = psram_size;
|
||||
|
||||
uint32_t used_psram_size = &__psram_heap_start__ - &__psram_start__;
|
||||
__psram_heap_size = __psram_size - used_psram_size;
|
||||
}
|
||||
PICO_RUNTIME_INIT_FUNC_RUNTIME(runtime_init_setup_psram, PICO_RUNTIME_INIT_PSRAM);
|
||||
|
||||
// update timing -- used if the system clock/timing was changed.
|
||||
void psram_reinit_timing() {
|
||||
set_psram_timing();
|
||||
}
|
||||
|
||||
static bool __psram_heap_init() {
|
||||
if (_bInitalized) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!__psram_heap_size) {
|
||||
return false;
|
||||
}
|
||||
_mem_heap = NULL;
|
||||
_mem_psram_pool = NULL;
|
||||
_mem_heap = tlsf_create_with_pool((void *)&__psram_heap_start__, __psram_heap_size, 16 * 1024 * 1024);
|
||||
if (!_mem_heap) {
|
||||
return false;
|
||||
}
|
||||
_mem_psram_pool = tlsf_get_pool(_mem_heap);
|
||||
if (!_mem_psram_pool) {
|
||||
return false;
|
||||
}
|
||||
_bInitalized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void *__psram_malloc(size_t size) {
|
||||
if (!__psram_heap_init() || !_mem_heap) {
|
||||
return NULL;
|
||||
}
|
||||
return tlsf_malloc(_mem_heap, size);
|
||||
}
|
||||
|
||||
void __psram_free(void *ptr) {
|
||||
if (!__psram_heap_init() || !_mem_heap) {
|
||||
return;
|
||||
}
|
||||
tlsf_free(_mem_heap, ptr);
|
||||
}
|
||||
|
||||
void *__psram_realloc(void *ptr, size_t size) {
|
||||
if (!__psram_heap_init() || !_mem_heap) {
|
||||
return NULL;
|
||||
}
|
||||
return tlsf_realloc(_mem_heap, ptr, size);
|
||||
}
|
||||
|
||||
void *__psram_calloc(size_t num, size_t size) {
|
||||
if (!__psram_heap_init() || !_mem_heap) {
|
||||
return NULL;
|
||||
}
|
||||
void *ptr = tlsf_malloc(_mem_heap, num * size);
|
||||
if (ptr) {
|
||||
bzero(ptr, num * size);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static bool max_free_walker(void *ptr, size_t size, int used, void *user) {
|
||||
size_t *max_size = (size_t *)user;
|
||||
if (!used && *max_size < size) {
|
||||
*max_size = size;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t __psram_largest_free_block() {
|
||||
if (!__psram_heap_init() || !_mem_heap) {
|
||||
return 0;
|
||||
}
|
||||
size_t max_free = 0;
|
||||
if (_mem_psram_pool) {
|
||||
tlsf_walk_pool(_mem_psram_pool, max_free_walker, &max_free);
|
||||
}
|
||||
return max_free;
|
||||
}
|
||||
|
||||
static bool memory_size_walker(void *ptr, size_t size, int used, void *user) {
|
||||
*((size_t *)user) += size;
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t __psram_total_space() {
|
||||
if (!__psram_heap_init() || !_mem_heap) {
|
||||
return 0;
|
||||
}
|
||||
size_t total_size = 0;
|
||||
if (_mem_psram_pool) {
|
||||
tlsf_walk_pool(_mem_psram_pool, memory_size_walker, &total_size);
|
||||
}
|
||||
return total_size;
|
||||
}
|
||||
|
||||
static bool memory_used_walker(void *ptr, size_t size, int used, void *user) {
|
||||
if (used) {
|
||||
*((size_t *)user) += size;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t __psram_total_used() {
|
||||
if (!__psram_heap_init() || !_mem_heap) {
|
||||
return 0;
|
||||
}
|
||||
size_t total_size = 0;
|
||||
if (_mem_psram_pool) {
|
||||
tlsf_walk_pool(_mem_psram_pool, memory_used_walker, &total_size);
|
||||
}
|
||||
return total_size;
|
||||
}
|
||||
|
||||
#endif // RP2350_PSRAM_CS
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
@file sfe_psram.c
|
||||
|
||||
@brief This file contains a function that is used to detect and initialize PSRAM on
|
||||
SparkFun rp2350 boards.
|
||||
*/
|
||||
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2024 SparkFun Electronics
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions: The
|
||||
above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
|
||||
"AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
void psram_reinit_timing();
|
||||
void *__psram_malloc(size_t size);
|
||||
void __psram_free(void *ptr);
|
||||
void *__psram_realloc(void *ptr, size_t size);
|
||||
void *__psram_calloc(size_t num, size_t size);
|
||||
size_t __psram_largest_free_block();
|
||||
size_t __psram_total_space();
|
||||
size_t __psram_total_used();
|
||||
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
* Copyright (c) 2011 ARM Ltd
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the company may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ARM LTD ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL ARM LTD BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#if defined(PICO_RP2350) && defined(__arm__)
|
||||
/* Prototype: void *memcpy (void *dst, const void *src, size_t count). */
|
||||
/* Use the version of memcpy implemented using LDRD and STRD.
|
||||
This version is tuned for Cortex-A15.
|
||||
This might not be the best for other ARMv7-A CPUs,
|
||||
but there is no predefine to distinguish between
|
||||
different CPUs in the same architecture,
|
||||
and this version is better than the plain memcpy provided in newlib.
|
||||
Therefore, we use this version for all ARMv7-A CPUS. */
|
||||
/* To make the same code compile for both ARM and Thumb instruction
|
||||
sets, switch to unified syntax at the beginning of this function.
|
||||
However, by using the same code, we may be missing optimization
|
||||
opportunities. For instance, in LDRD/STRD instructions, the first
|
||||
destination register must be even and the second consecutive in
|
||||
ARM state, but not in Thumb state. */
|
||||
.syntax unified
|
||||
#if defined (__thumb__)
|
||||
.thumb
|
||||
.thumb_func
|
||||
#endif
|
||||
#ifdef __native_client__
|
||||
#define SFI_BREG(reg) sfi_breg reg,
|
||||
#define IT(insn)
|
||||
#ifdef __thumb__
|
||||
#error "thumb and native_client are not compatible!"
|
||||
#endif
|
||||
.p2align 4
|
||||
#else
|
||||
#define SFI_BREG(reg)
|
||||
#define IT(insn) insn
|
||||
#endif
|
||||
.global __wrap_memcpy
|
||||
.type __wrap_memcpy, %function
|
||||
// .section .time_critical.memcpy // Actually slows down a bit because RAM and program RAM conflict
|
||||
__wrap_memcpy:
|
||||
/* Assumes that n >= 0, and dst, src are valid pointers.
|
||||
If there is at least 8 bytes to copy, use LDRD/STRD.
|
||||
If src and dst are misaligned with different offsets,
|
||||
first copy byte by byte until dst is aligned,
|
||||
and then copy using LDRD/STRD and shift if needed.
|
||||
When less than 8 left, copy a word and then byte by byte. */
|
||||
/* Save registers (r0 holds the return value):
|
||||
optimized push {r0, r4, r5, lr}.
|
||||
To try and improve performance, stack layout changed,
|
||||
i.e., not keeping the stack looking like users expect
|
||||
(highest numbered register at highest address). */
|
||||
push {r0, lr}
|
||||
strd r4, r5, [sp, #-8]!
|
||||
/* TODO: Add debug frame directives.
|
||||
We don't need exception unwind directives, because the code below
|
||||
does not throw any exceptions and does not call any other functions.
|
||||
Generally, newlib functions like this lack debug information for
|
||||
assembler source. */
|
||||
/* Get copying of tiny blocks out of the way first. */
|
||||
/* Is there at least 4 bytes to copy? */
|
||||
subs r2, r2, #4
|
||||
blt copy_less_than_4 /* If n < 4. */
|
||||
/* Check word alignment. */
|
||||
ands ip, r0, #3 /* ip = last 2 bits of dst. */
|
||||
bne dst_not_word_aligned /* If dst is not word-aligned. */
|
||||
/* Get here if dst is word-aligned. */
|
||||
ands ip, r1, #3 /* ip = last 2 bits of src. */
|
||||
bne src_not_word_aligned /* If src is not word-aligned. */
|
||||
word_aligned:
|
||||
/* Get here if source and dst both are word-aligned.
|
||||
The number of bytes remaining to copy is r2+4. */
|
||||
/* Is there is at least 64 bytes to copy? */
|
||||
subs r2, r2, #60
|
||||
blt copy_less_than_64 /* If r2 + 4 < 64. */
|
||||
/* First, align the destination buffer to 8-bytes,
|
||||
to make sure double loads and stores don't cross cache line boundary,
|
||||
as they are then more expensive even if the data is in the cache
|
||||
(require two load/store issue cycles instead of one).
|
||||
If only one of the buffers is not 8-bytes aligned,
|
||||
then it's more important to align dst than src,
|
||||
because there is more penalty for stores
|
||||
than loads that cross cacheline boundary.
|
||||
This check and realignment are only worth doing
|
||||
if there is a lot to copy. */
|
||||
/* Get here if dst is word aligned,
|
||||
i.e., the 2 least significant bits are 0.
|
||||
If dst is not 2w aligned (i.e., the 3rd bit is not set in dst),
|
||||
then copy 1 word (4 bytes). */
|
||||
ands r3, r0, #4
|
||||
beq 11f /* If dst already two-word aligned. */
|
||||
SFI_BREG(r1) \
|
||||
ldr r3, [r1], #4
|
||||
SFI_BREG(r0) \
|
||||
str r3, [r0], #4
|
||||
subs r2, r2, #4
|
||||
blt copy_less_than_64
|
||||
11:
|
||||
/* TODO: Align to cacheline (useful for PLD optimization). */
|
||||
/* Every loop iteration copies 64 bytes. */
|
||||
1:
|
||||
.irp offset, #0, #8, #16, #24, #32, #40, #48, #56
|
||||
SFI_BREG(r1) \
|
||||
ldrd r4, r5, [r1, \offset]
|
||||
SFI_BREG(r0) \
|
||||
strd r4, r5, [r0, \offset]
|
||||
.endr
|
||||
add r0, r0, #64
|
||||
add r1, r1, #64
|
||||
subs r2, r2, #64
|
||||
bge 1b /* If there is more to copy. */
|
||||
copy_less_than_64:
|
||||
/* Get here if less than 64 bytes to copy, -64 <= r2 < 0.
|
||||
Restore the count if there is more than 7 bytes to copy. */
|
||||
adds r2, r2, #56
|
||||
blt copy_less_than_8
|
||||
/* Copy 8 bytes at a time. */
|
||||
2:
|
||||
SFI_BREG(r1) \
|
||||
ldrd r4, r5, [r1], #8
|
||||
SFI_BREG(r0) \
|
||||
strd r4, r5, [r0], #8
|
||||
subs r2, r2, #8
|
||||
bge 2b /* If there is more to copy. */
|
||||
copy_less_than_8:
|
||||
/* Get here if less than 8 bytes to copy, -8 <= r2 < 0.
|
||||
Check if there is more to copy. */
|
||||
cmn r2, #8
|
||||
beq return /* If r2 + 8 == 0. */
|
||||
/* Restore the count if there is more than 3 bytes to copy. */
|
||||
adds r2, r2, #4
|
||||
blt copy_less_than_4
|
||||
/* Copy 4 bytes. */
|
||||
SFI_BREG(r1) \
|
||||
ldr r3, [r1], #4
|
||||
SFI_BREG(r0) \
|
||||
str r3, [r0], #4
|
||||
copy_less_than_4:
|
||||
/* Get here if less than 4 bytes to copy, -4 <= r2 < 0. */
|
||||
/* Restore the count, check if there is more to copy. */
|
||||
adds r2, r2, #4
|
||||
beq return /* If r2 == 0. */
|
||||
/* Get here with r2 is in {1,2,3}={01,10,11}. */
|
||||
/* Logical shift left r2, insert 0s, update flags. */
|
||||
lsls r2, r2, #31
|
||||
/* Copy byte by byte.
|
||||
Condition ne means the last bit of r2 is 0.
|
||||
Condition cs means the second to last bit of r2 is set,
|
||||
i.e., r2 is 1 or 3. */
|
||||
IT(itt ne)
|
||||
SFI_BREG(r1) \
|
||||
ldrbne r3, [r1], #1
|
||||
SFI_BREG(r0) \
|
||||
strbne r3, [r0], #1
|
||||
IT(itttt cs)
|
||||
SFI_BREG(r1) \
|
||||
ldrbcs r4, [r1], #1
|
||||
SFI_BREG(r1) \
|
||||
ldrbcs r5, [r1]
|
||||
SFI_BREG(r0) \
|
||||
strbcs r4, [r0], #1
|
||||
SFI_BREG(r0) \
|
||||
strbcs r5, [r0]
|
||||
return:
|
||||
/* Restore registers: optimized pop {r0, r4, r5, pc} */
|
||||
ldrd r4, r5, [sp], #8
|
||||
#ifdef __native_client__
|
||||
pop {r0, lr}
|
||||
sfi_bx lr
|
||||
#else
|
||||
pop {r0, pc} /* This is the only return point of memcpy. */
|
||||
#endif
|
||||
#ifndef __ARM_FEATURE_UNALIGNED
|
||||
/* The following assembly macro implements misaligned copy in software.
|
||||
Assumes that dst is word aligned, src is at offset "pull" bits from
|
||||
word, push = 32 - pull, and the number of bytes that remain to copy
|
||||
is r2 + 4, r2 >= 0. */
|
||||
/* In the code below, r2 is the number of bytes that remain to be
|
||||
written. The number of bytes read is always larger, because we have
|
||||
partial words in the shift queue. */
|
||||
.macro miscopy pull push shiftleft shiftright
|
||||
/* Align src to the previous word boundary. */
|
||||
bic r1, r1, #3
|
||||
/* Initialize the shift queue. */
|
||||
SFI_BREG(r1) \
|
||||
ldr r5, [r1], #4 /* Load a word from source. */
|
||||
subs r2, r2, #4
|
||||
blt 6f /* Go to misaligned copy of less than 8 bytes. */
|
||||
/* Get here if there is more than 8 bytes to copy.
|
||||
The number of bytes to copy is r2+8, r2 >= 0. */
|
||||
/* Save registers: push { r6, r7 }.
|
||||
We need additional registers for LDRD and STRD, because in ARM state
|
||||
the first destination register must be even and the second
|
||||
consecutive. */
|
||||
strd r6, r7, [sp, #-8]!
|
||||
subs r2, r2, #56
|
||||
blt 4f /* Go to misaligned copy of less than 64 bytes. */
|
||||
3:
|
||||
/* Get here if there is more than 64 bytes to copy.
|
||||
The number of bytes to copy is r2+64, r2 >= 0. */
|
||||
/* Copy 64 bytes in every iteration.
|
||||
Use a partial word from the shift queue. */
|
||||
.irp offset, #0, #8, #16, #24, #32, #40, #48, #56
|
||||
mov r6, r5, \shiftleft #\pull
|
||||
SFI_BREG(r1) \
|
||||
ldrd r4, r5, [r1, \offset]
|
||||
orr r6, r6, r4, \shiftright #\push
|
||||
mov r7, r4, \shiftleft #\pull
|
||||
orr r7, r7, r5, \shiftright #\push
|
||||
SFI_BREG(r0) \
|
||||
strd r6, r7, [r0, \offset]
|
||||
.endr
|
||||
add r1, r1, #64
|
||||
add r0, r0, #64
|
||||
subs r2, r2, #64
|
||||
bge 3b
|
||||
4:
|
||||
/* Get here if there is less than 64 bytes to copy (-64 <= r2 < 0)
|
||||
and they are misaligned. */
|
||||
/* Restore the count if there is more than 7 bytes to copy. */
|
||||
adds r2, r2, #56
|
||||
/* If less than 8 bytes to copy,
|
||||
restore registers saved for this loop: optimized poplt { r6, r7 }. */
|
||||
itt lt
|
||||
ldrdlt r6, r7, [sp], #8
|
||||
blt 6f /* Go to misaligned copy of less than 8 bytes. */
|
||||
5:
|
||||
/* Copy 8 bytes at a time.
|
||||
Use a partial word from the shift queue. */
|
||||
mov r6, r5, \shiftleft #\pull
|
||||
SFI_BREG(r1) \
|
||||
ldrd r4, r5, [r1], #8
|
||||
orr r6, r6, r4, \shiftright #\push
|
||||
mov r7, r4, \shiftleft #\pull
|
||||
orr r7, r7, r5, \shiftright #\push
|
||||
SFI_BREG(r0) \
|
||||
strd r6, r7, [r0], #8
|
||||
subs r2, r2, #8
|
||||
bge 5b /* If there is more to copy. */
|
||||
/* Restore registers saved for this loop: optimized pop { r6, r7 }. */
|
||||
ldrd r6, r7, [sp], #8
|
||||
6:
|
||||
/* Get here if there less than 8 bytes to copy (-8 <= r2 < 0)
|
||||
and they are misaligned. */
|
||||
/* Check if there is more to copy. */
|
||||
cmn r2, #8
|
||||
beq return
|
||||
/* Check if there is less than 4 bytes to copy. */
|
||||
cmn r2, #4
|
||||
itt lt
|
||||
/* Restore src offset from word-align. */
|
||||
sublt r1, r1, #(\push / 8)
|
||||
blt copy_less_than_4
|
||||
/* Use a partial word from the shift queue. */
|
||||
mov r3, r5, \shiftleft #\pull
|
||||
/* Load a word from src, but without writeback
|
||||
(this word is not fully written to dst). */
|
||||
SFI_BREG(r1) \
|
||||
ldr r5, [r1]
|
||||
/* Restore src offset from word-align. */
|
||||
add r1, r1, #(\pull / 8)
|
||||
/* Shift bytes to create one dst word and store it. */
|
||||
orr r3, r3, r5, \shiftright #\push
|
||||
SFI_BREG(r0) \
|
||||
str r3, [r0], #4
|
||||
/* Use single byte copying of the remaining bytes. */
|
||||
b copy_less_than_4
|
||||
.endm
|
||||
#endif /* not __ARM_FEATURE_UNALIGNED */
|
||||
dst_not_word_aligned:
|
||||
/* Get here when dst is not aligned and ip has the last 2 bits of dst,
|
||||
i.e., ip is the offset of dst from word.
|
||||
The number of bytes that remains to copy is r2 + 4,
|
||||
i.e., there are at least 4 bytes to copy.
|
||||
Write a partial word (0 to 3 bytes), such that dst becomes
|
||||
word-aligned. */
|
||||
/* If dst is at ip bytes offset from a word (with 0 < ip < 4),
|
||||
then there are (4 - ip) bytes to fill up to align dst to the next
|
||||
word. */
|
||||
rsb ip, ip, #4 /* ip = #4 - ip. */
|
||||
cmp ip, #2
|
||||
/* Copy byte by byte with conditionals. */
|
||||
IT(itt gt)
|
||||
SFI_BREG(r1) \
|
||||
ldrbgt r3, [r1], #1
|
||||
SFI_BREG(r0) \
|
||||
strbgt r3, [r0], #1
|
||||
IT(itt ge)
|
||||
SFI_BREG(r1) \
|
||||
ldrbge r4, [r1], #1
|
||||
SFI_BREG(r0) \
|
||||
strbge r4, [r0], #1
|
||||
SFI_BREG(r1) \
|
||||
ldrb lr, [r1], #1
|
||||
SFI_BREG(r0) \
|
||||
strb lr, [r0], #1
|
||||
/* Update the count.
|
||||
ip holds the number of bytes we have just copied. */
|
||||
subs r2, r2, ip /* r2 = r2 - ip. */
|
||||
blt copy_less_than_4 /* If r2 < ip. */
|
||||
/* Get here if there are more than 4 bytes to copy.
|
||||
Check if src is aligned. If beforehand src and dst were not word
|
||||
aligned but congruent (same offset), then now they are both
|
||||
word-aligned, and we can copy the rest efficiently (without
|
||||
shifting). */
|
||||
ands ip, r1, #3 /* ip = last 2 bits of src. */
|
||||
beq word_aligned /* If r1 is word-aligned. */
|
||||
src_not_word_aligned:
|
||||
/* Get here when src is not word-aligned, but dst is word-aligned.
|
||||
The number of bytes that remains to copy is r2+4. */
|
||||
#ifdef __ARM_FEATURE_UNALIGNED
|
||||
/* Copy word by word using LDR when alignment can be done in hardware,
|
||||
i.e., SCTLR.A is set, supporting unaligned access in LDR and STR. */
|
||||
subs r2, r2, #60
|
||||
blt 8f
|
||||
7:
|
||||
/* Copy 64 bytes in every loop iteration. */
|
||||
.irp offset, #0, #4, #8, #12, #16, #20, #24, #28, #32, #36, #40, #44, #48, #52, #56, #60
|
||||
SFI_BREG(r1) \
|
||||
ldr r3, [r1, \offset]
|
||||
SFI_BREG(r0) \
|
||||
str r3, [r0, \offset]
|
||||
.endr
|
||||
add r0, r0, #64
|
||||
add r1, r1, #64
|
||||
subs r2, r2, #64
|
||||
bge 7b
|
||||
8:
|
||||
/* Get here if less than 64 bytes to copy, -64 <= r2 < 0.
|
||||
Check if there is more than 3 bytes to copy. */
|
||||
adds r2, r2, #60
|
||||
blt copy_less_than_4
|
||||
9:
|
||||
/* Get here if there is less than 64 but at least 4 bytes to copy,
|
||||
where the number of bytes to copy is r2+4. */
|
||||
SFI_BREG(r1) \
|
||||
ldr r3, [r1], #4
|
||||
SFI_BREG(r0) \
|
||||
str r3, [r0], #4
|
||||
subs r2, r2, #4
|
||||
bge 9b
|
||||
b copy_less_than_4
|
||||
#else /* not __ARM_FEATURE_UNALIGNED */
|
||||
/* ip has last 2 bits of src,
|
||||
i.e., ip is the offset of src from word, and ip > 0.
|
||||
Compute shifts needed to copy from src to dst. */
|
||||
cmp ip, #2
|
||||
beq miscopy_16_16 /* If ip == 2. */
|
||||
bge miscopy_24_8 /* If ip == 3. */
|
||||
/* Get here if ip == 1. */
|
||||
/* Endian independent macros for shifting bytes within registers. */
|
||||
#ifndef __ARMEB__
|
||||
miscopy_8_24: miscopy pull=8 push=24 shiftleft=lsr shiftright=lsl
|
||||
miscopy_16_16: miscopy pull=16 push=16 shiftleft=lsr shiftright=lsl
|
||||
miscopy_24_8: miscopy pull=24 push=8 shiftleft=lsr shiftright=lsl
|
||||
#else /* not __ARMEB__ */
|
||||
miscopy_8_24: miscopy pull=8 push=24 shiftleft=lsl shiftright=lsr
|
||||
miscopy_16_16: miscopy pull=16 push=16 shiftleft=lsl shiftright=lsr
|
||||
miscopy_24_8: miscopy pull=24 push=8 shiftleft=lsl shiftright=lsr
|
||||
#endif /* not __ARMEB__ */
|
||||
#endif /* not __ARM_FEATURE_UNALIGNED */
|
||||
#endif /* memcpy */
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
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);
|
||||
if (!__isFreeRTOS) {
|
||||
noInterrupts();
|
||||
}
|
||||
rp2040.idleOtherCore();
|
||||
flash_range_erase(PICO_FLASH_BANK_STORAGE_OFFSET + (PICO_FLASH_BANK_SIZE * bank), PICO_FLASH_BANK_SIZE);
|
||||
rp2040.resumeOtherCore();
|
||||
if (!__isFreeRTOS) {
|
||||
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
|
||||
if (!__isFreeRTOS) {
|
||||
noInterrupts();
|
||||
}
|
||||
rp2040.idleOtherCore();
|
||||
flash_range_program(bank_start_pos + (page * FLASH_PAGE_SIZE), page_data, FLASH_PAGE_SIZE);
|
||||
rp2040.resumeOtherCore();
|
||||
if (!__isFreeRTOS) {
|
||||
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
|
||||
|
||||
extern volatile bool __inLWIP;
|
||||
|
||||
// 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,121 @@
|
||||
// Taken from LWIP's inet_chksum.c just to set the -O2 flag
|
||||
|
||||
/*
|
||||
Copyright (c) 2001-2004 Swedish Institute of Computer Science.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
OF SUCH DAMAGE.
|
||||
|
||||
This file is part of the lwIP TCP/IP stack.
|
||||
|
||||
Author: Adam Dunkels <adam@sics.se>
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#include "lwip/inet_chksum.h"
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
|
||||
#pragma GCC optimize ("O2")
|
||||
/**
|
||||
An optimized checksum routine. Basically, it uses loop-unrolling on
|
||||
the checksum loop, treating the head and tail bytes specially, whereas
|
||||
the inner loop acts on 8 bytes at a time.
|
||||
|
||||
@arg start of buffer to be checksummed. May be an odd byte address.
|
||||
@len number of bytes in the buffer to be checksummed.
|
||||
@return host order (!) lwip checksum (non-inverted Internet sum)
|
||||
|
||||
by Curt McDowell, Broadcom Corp. December 8th, 2005
|
||||
*/
|
||||
extern "C" u16_t lwip_standard_chksum(const void *dataptr, int len) {
|
||||
const u8_t *pb = (const u8_t *)dataptr;
|
||||
const u16_t *ps;
|
||||
u16_t t = 0;
|
||||
const u32_t *pl;
|
||||
u32_t sum = 0, tmp;
|
||||
/* starts at odd byte address? */
|
||||
int odd = ((mem_ptr_t)pb & 1);
|
||||
|
||||
if (odd && len > 0) {
|
||||
((u8_t *)&t)[1] = *pb++;
|
||||
len--;
|
||||
}
|
||||
|
||||
ps = (const u16_t *)(const void *)pb;
|
||||
|
||||
if (((mem_ptr_t)ps & 3) && len > 1) {
|
||||
sum += *ps++;
|
||||
len -= 2;
|
||||
}
|
||||
|
||||
pl = (const u32_t *)(const void *)ps;
|
||||
|
||||
while (len > 7) {
|
||||
tmp = sum + *pl++; /* ping */
|
||||
if (tmp < sum) {
|
||||
tmp++; /* add back carry */
|
||||
}
|
||||
|
||||
sum = tmp + *pl++; /* pong */
|
||||
if (sum < tmp) {
|
||||
sum++; /* add back carry */
|
||||
}
|
||||
|
||||
len -= 8;
|
||||
}
|
||||
|
||||
/* make room in upper bits */
|
||||
sum = FOLD_U32T(sum);
|
||||
|
||||
ps = (const u16_t *)pl;
|
||||
|
||||
/* 16-bit aligned word remaining? */
|
||||
while (len > 1) {
|
||||
sum += *ps++;
|
||||
len -= 2;
|
||||
}
|
||||
|
||||
/* dangling tail byte remaining? */
|
||||
if (len > 0) { /* include odd byte */
|
||||
((u8_t *)&t)[0] = *(const u8_t *)ps;
|
||||
}
|
||||
|
||||
sum += t; /* add end bytes */
|
||||
|
||||
/* Fold 32-bit sum to 16 bits
|
||||
calling this twice is probably faster than if statements... */
|
||||
sum = FOLD_U32T(sum);
|
||||
sum = FOLD_U32T(sum);
|
||||
|
||||
if (odd) {
|
||||
sum = SWAP_BYTES_IN_WORD(sum);
|
||||
}
|
||||
|
||||
return (u16_t)sum;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/times.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#if PICO_ENTER_USB_BOOT_ON_EXIT
|
||||
#include "pico/bootrom.h"
|
||||
#endif
|
||||
#include "pico/time.h"
|
||||
#include "pico/runtime_init.h"
|
||||
|
||||
#if LIB_PICO_PRINTF_PICO
|
||||
#include "pico/printf.h"
|
||||
#else
|
||||
#define weak_raw_printf printf
|
||||
#define weak_raw_vprintf vprintf
|
||||
#endif
|
||||
#if LIB_PICO_STDIO
|
||||
#include "pico/stdio.h"
|
||||
#endif
|
||||
|
||||
#if PICO_ENTER_USB_BOOT_ON_EXIT
|
||||
#include "pico/bootrom.h"
|
||||
#endif
|
||||
|
||||
extern char __StackLimit; /* Set by linker. */
|
||||
|
||||
#define STDIO_HANDLE_STDIN 0
|
||||
#define STDIO_HANDLE_STDOUT 1
|
||||
#define STDIO_HANDLE_STDERR 2
|
||||
|
||||
void __attribute__((noreturn)) __weak _exit(__unused int status) {
|
||||
#if PICO_ENTER_USB_BOOT_ON_EXIT
|
||||
reset_usb_boot(0, 0);
|
||||
#else
|
||||
while (1) {
|
||||
__breakpoint();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
__weak void *_sbrk(int incr) {
|
||||
extern char end; /* Set by linker. */
|
||||
static char *heap_end;
|
||||
char *prev_heap_end;
|
||||
|
||||
if (heap_end == 0) {
|
||||
heap_end = &end;
|
||||
}
|
||||
|
||||
prev_heap_end = heap_end;
|
||||
char *next_heap_end = heap_end + incr;
|
||||
|
||||
if (__builtin_expect(next_heap_end > (&__StackLimit), false)) {
|
||||
#if PICO_USE_OPTIMISTIC_SBRK
|
||||
if (heap_end == &__StackLimit) {
|
||||
// errno = ENOMEM;
|
||||
return (char *) -1;
|
||||
}
|
||||
next_heap_end = &__StackLimit;
|
||||
#else
|
||||
return (char *) -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
heap_end = next_heap_end;
|
||||
return (void *) prev_heap_end;
|
||||
}
|
||||
#if 0
|
||||
static int64_t epoch_time_us_since_boot;
|
||||
|
||||
__weak int _gettimeofday(struct timeval *__restrict tv, __unused void *__restrict tz) {
|
||||
if (tv) {
|
||||
int64_t us_since_epoch = ((int64_t)to_us_since_boot(get_absolute_time())) - epoch_time_us_since_boot;
|
||||
tv->tv_sec = (time_t)(us_since_epoch / 1000000);
|
||||
tv->tv_usec = (suseconds_t)(us_since_epoch % 1000000);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
__weak int settimeofday(__unused const struct timeval *tv, __unused const struct timezone *tz) {
|
||||
if (tv) {
|
||||
int64_t us_since_epoch = tv->tv_sec * 1000000 + tv->tv_usec;
|
||||
epoch_time_us_since_boot = (int64_t)to_us_since_boot(get_absolute_time()) - us_since_epoch;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
__weak int _times(struct tms *tms) {
|
||||
#if CLOCKS_PER_SEC >= 1000000
|
||||
tms->tms_utime = (clock_t)(to_us_since_boot(get_absolute_time()) * (CLOCKS_PER_SEC / 1000000));
|
||||
#else
|
||||
tms->tms_utime = (clock_t)(to_us_since_boot(get_absolute_time()) / (1000000 / CLOCKS_PER_SEC));
|
||||
#endif
|
||||
tms->tms_stime = 0;
|
||||
tms->tms_cutime = 0;
|
||||
tms->tms_cstime = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__weak pid_t _getpid(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
__weak int _kill(__unused pid_t pid, __unused int sig) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int __attribute__((weak)) _read(int handle, char *buffer, int length) {
|
||||
#if LIB_PICO_STDIO
|
||||
if (handle == STDIO_HANDLE_STDIN) {
|
||||
return stdio_get_until(buffer, length, at_the_end_of_time);
|
||||
}
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
int __attribute__((weak)) _write(int handle, char *buffer, int length) {
|
||||
#if LIB_PICO_STDIO
|
||||
if (handle == STDIO_HANDLE_STDOUT || handle == STDIO_HANDLE_STDERR) {
|
||||
stdio_put_string(buffer, length, false, true);
|
||||
return length;
|
||||
}
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
int __attribute__((weak)) _open(__unused const char *fn, __unused int oflag, ...) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int __attribute__((weak)) _close(__unused int fd) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
off_t __attribute__((weak)) _lseek(__unused int fd, __unused off_t pos, __unused int whence) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int __attribute__((weak)) _fstat(__unused int fd, __unused struct stat *buf) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int __attribute__((weak)) _isatty(int fd) {
|
||||
return fd == STDIO_HANDLE_STDIN || fd == STDIO_HANDLE_STDOUT || fd == STDIO_HANDLE_STDERR;
|
||||
}
|
||||
|
||||
// exit is not useful... no desire to pull in __call_exitprocs
|
||||
void exit(int status) {
|
||||
_exit(status);
|
||||
}
|
||||
|
||||
// incorrect warning from GCC 6
|
||||
GCC_Pragma("GCC diagnostic push")
|
||||
GCC_Pragma("GCC diagnostic ignored \"-Wsuggest-attribute=format\"")
|
||||
void __weak __assert_func(const char *file, int line, const char *func, const char *failedexpr) {
|
||||
weak_raw_printf("assertion \"%s\" failed: file \"%s\", line %d%s%s\n",
|
||||
failedexpr, file, line, func ? ", function: " : "",
|
||||
func ? func : "");
|
||||
|
||||
_exit(1);
|
||||
}
|
||||
GCC_Pragma("GCC diagnostic pop")
|
||||
#endif
|
||||
void runtime_init(void) {
|
||||
#ifndef NDEBUG
|
||||
if (__get_current_exception()) {
|
||||
// crap; started in exception handler
|
||||
__breakpoint();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !PICO_RUNTIME_SKIP_INIT_PER_CORE_INSTALL_STACK_GUARD
|
||||
// install core0 stack guard
|
||||
extern char __StackBottom;
|
||||
runtime_init_per_core_install_stack_guard(&__StackBottom);
|
||||
#endif
|
||||
|
||||
// todo maybe we want to do this in the future, but it does stuff like register_tm_clones
|
||||
// which we didn't do in previous SDKs
|
||||
//extern void __libc_init_array(void);
|
||||
//__libc_init_array();
|
||||
|
||||
// ... so instead just do the __preinit_array
|
||||
runtime_run_initializers();
|
||||
// ... and the __init_array
|
||||
extern void (*__init_array_start)(void);
|
||||
extern void (*__init_array_end)(void);
|
||||
for (void (**p)(void) = &__init_array_start; p < &__init_array_end; ++p) {
|
||||
(*p)();
|
||||
}
|
||||
}
|
||||
@@ -6,10 +6,9 @@
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include "pico.h"
|
||||
#include "pico/time.h"
|
||||
#include "pico/bootrom.h"
|
||||
#include "pico/binary_info.h"
|
||||
#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
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
16-bit axis gamepad definition
|
||||
Copyright (c) 2024 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tusb.h"
|
||||
#include "class/hid/hid_device.h"
|
||||
|
||||
#define TUD_HID_REPORT_DESC_GAMEPAD16(...) \
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_GAMEPAD ) ,\
|
||||
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
|
||||
/* Report ID if any */\
|
||||
__VA_ARGS__ \
|
||||
/* 16 bit X, Y, Z, Rz, Rx, Ry (min -32767, max 32767 ) */ \
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_X ) ,\
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_Y ) ,\
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_Z ) ,\
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_RZ ) ,\
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_RX ) ,\
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_RY ) ,\
|
||||
HID_LOGICAL_MIN_N ( -32767, 2 ) ,\
|
||||
HID_LOGICAL_MAX_N ( 32767, 2 ) ,\
|
||||
HID_REPORT_COUNT ( 6 ) ,\
|
||||
HID_REPORT_SIZE ( 16 ) ,\
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||
/* 8 bit DPad/Hat Button Map */ \
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_HAT_SWITCH ) ,\
|
||||
HID_LOGICAL_MIN ( 1 ) ,\
|
||||
HID_LOGICAL_MAX ( 8 ) ,\
|
||||
HID_PHYSICAL_MIN ( 0 ) ,\
|
||||
HID_PHYSICAL_MAX_N ( 315, 2 ) ,\
|
||||
HID_REPORT_COUNT ( 1 ) ,\
|
||||
HID_REPORT_SIZE ( 8 ) ,\
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||
/* 32 bit Button Map */ \
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\
|
||||
HID_USAGE_MIN ( 1 ) ,\
|
||||
HID_USAGE_MAX ( 32 ) ,\
|
||||
HID_LOGICAL_MIN ( 0 ) ,\
|
||||
HID_LOGICAL_MAX ( 1 ) ,\
|
||||
HID_REPORT_COUNT ( 32 ) ,\
|
||||
HID_REPORT_SIZE ( 1 ) ,\
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||
HID_COLLECTION_END \
|
||||
|
||||
// HID Gamepad Protocol Report.
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
int16_t x; ///< Delta x movement of left analog-stick
|
||||
int16_t y; ///< Delta y movement of left analog-stick
|
||||
int16_t z; ///< Delta z movement of right analog-joystick
|
||||
int16_t rz; ///< Delta Rz movement of right analog-joystick
|
||||
int16_t rx; ///< Delta Rx movement of analog left trigger
|
||||
int16_t ry; ///< Delta Ry movement of analog right trigger
|
||||
uint8_t hat; ///< Buttons mask for currently pressed buttons in the DPad/hat
|
||||
uint32_t buttons; ///< Buttons mask for currently pressed buttons
|
||||
} hid_gamepad16_report_t;
|
||||
@@ -23,8 +23,8 @@
|
||||
.program tone2
|
||||
.side_set 1 opt
|
||||
|
||||
pull ; TXFIFO -> OSR, or X -> OSR if no new period
|
||||
mov x, osr ; OSR -> X
|
||||
; pull ; TXFIFO -> OSR, or X -> OSR if no new period
|
||||
; mov x, osr ; OSR -> X
|
||||
|
||||
high:
|
||||
pull noblock ; Potentially grab new HALFCYCLECOUNT, OTW copy from backup in X
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// This file is autogenerated by pioasm; do not edit! //
|
||||
// -------------------------------------------------- //
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
#include "hardware/pio.h"
|
||||
#endif
|
||||
@@ -11,27 +13,30 @@
|
||||
// ----- //
|
||||
|
||||
#define tone2_wrap_target 0
|
||||
#define tone2_wrap 8
|
||||
#define tone2_wrap 6
|
||||
#define tone2_pio_version 0
|
||||
|
||||
static const uint16_t tone2_program_instructions[] = {
|
||||
// .wrap_target
|
||||
0x80a0, // 0: pull block
|
||||
0x8080, // 0: pull noblock
|
||||
0xa027, // 1: mov x, osr
|
||||
0x8080, // 2: pull noblock
|
||||
0xa027, // 3: mov x, osr
|
||||
0xb847, // 4: mov y, osr side 1
|
||||
0xb847, // 2: mov y, osr side 1
|
||||
0x0083, // 3: jmp y--, 3
|
||||
0xb047, // 4: mov y, osr side 0
|
||||
0x0085, // 5: jmp y--, 5
|
||||
0xb047, // 6: mov y, osr side 0
|
||||
0x0087, // 7: jmp y--, 7
|
||||
0x0002, // 8: jmp 2
|
||||
0x0000, // 6: jmp 0
|
||||
// .wrap
|
||||
};
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
static const struct pio_program tone2_program = {
|
||||
.instructions = tone2_program_instructions,
|
||||
.length = 9,
|
||||
.length = 7,
|
||||
.origin = -1,
|
||||
.pio_version = 0,
|
||||
#if PICO_PIO_VERSION > 0
|
||||
.used_gpio_ranges = 0x0
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline pio_sm_config tone2_program_get_default_config(uint offset) {
|
||||
|
||||
@@ -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 uint32_t pwmInitted = 0;
|
||||
static uint64_t pwmInitted = 0;
|
||||
static bool scaleInitted = false;
|
||||
static bool adcInitted = false;
|
||||
static uint16_t analogWritePseudoScale = 1;
|
||||
static uint16_t analogWriteSlowScale = 1;
|
||||
@@ -40,15 +43,16 @@ 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 > 10'000'000) {
|
||||
DEBUGCORE("ERROR: analogWriteFreq too high (%d)\n", freq);
|
||||
DEBUGCORE("ERROR: analogWriteFreq too high (%lu)\n", freq);
|
||||
analogFreq = 10'000'000;
|
||||
} else {
|
||||
analogFreq = freq;
|
||||
}
|
||||
pwmInitted = 0;
|
||||
scaleInitted = false;
|
||||
}
|
||||
|
||||
extern "C" void analogWriteRange(uint32_t range) {
|
||||
@@ -58,8 +62,9 @@ extern "C" void analogWriteRange(uint32_t range) {
|
||||
if ((range >= 3) && (range <= 65535)) {
|
||||
analogScale = range;
|
||||
pwmInitted = 0;
|
||||
scaleInitted = false;
|
||||
} else {
|
||||
DEBUGCORE("ERROR: analogWriteRange out of range (%d)\n", range);
|
||||
DEBUGCORE("ERROR: analogWriteRange out of range (%lu)\n", range);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,30 +79,34 @@ extern "C" void analogWriteResolution(int res) {
|
||||
extern "C" void analogWrite(pin_size_t pin, int val) {
|
||||
CoreMutex m(&_dacMutex);
|
||||
|
||||
if ((pin > 29) || !m) {
|
||||
if ((pin >= __GPIOCNT) || !m) {
|
||||
DEBUGCORE("ERROR: Illegal analogWrite pin (%d)\n", pin);
|
||||
return;
|
||||
}
|
||||
if (!(pwmInitted & (1 << pin))) {
|
||||
__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)) {
|
||||
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)) < 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 & (1LL << 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 - 1);
|
||||
pwm_init(pwm_gpio_to_slice_num(pin), &c, true);
|
||||
pwmInitted |= 1 << pin;
|
||||
pwmInitted |= 1LL << pwm_gpio_to_slice_num(pin);
|
||||
}
|
||||
|
||||
val <<= analogWritePseudoScale;
|
||||
@@ -114,13 +123,19 @@ 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 uint64_t _adcGPIOInit = 0;
|
||||
|
||||
void __clearADCPin(pin_size_t p) {
|
||||
_adcGPIOInit &= ~(1LL << p);
|
||||
}
|
||||
|
||||
extern "C" int analogRead(pin_size_t pin) {
|
||||
CoreMutex m(&_adcMutex);
|
||||
|
||||
pin_size_t maxPin = max(A0, A3);
|
||||
pin_size_t minPin = min(A0, A3);
|
||||
pin_size_t maxPin = __GPIOCNT;
|
||||
pin_size_t minPin = __FIRSTANALOGGPIO;
|
||||
|
||||
if ((pin < minPin) || (pin > maxPin) || !m) {
|
||||
DEBUGCORE("ERROR: Illegal analogRead pin (%d)\n", pin);
|
||||
@@ -130,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 & (1LL << pin))) {
|
||||
adc_gpio_init(pin);
|
||||
_adcGPIOInit |= 1LL << 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) {
|
||||
@@ -145,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
|
||||
adc_select_input(__GPIOCNT - __FIRSTANALOGGPIO); // 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,10 +21,17 @@
|
||||
#include "Arduino.h"
|
||||
#include <hardware/gpio.h>
|
||||
|
||||
static PinMode _pm[30];
|
||||
extern void __clearADCPin(pin_size_t p);
|
||||
|
||||
static PinMode _pm[__GPIOCNT];
|
||||
|
||||
extern "C" void pinMode(pin_size_t ulPin, PinMode ulMode) __attribute__((weak, alias("__pinMode")));
|
||||
extern "C" void __pinMode(pin_size_t ulPin, PinMode ulMode) {
|
||||
if (ulPin >= __GPIOCNT) {
|
||||
DEBUGCORE("ERROR: Illegal pin in pinMode (%d)\n", ulPin);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (ulMode) {
|
||||
case INPUT:
|
||||
gpio_init(ulPin);
|
||||
@@ -70,16 +77,16 @@ extern "C" void __pinMode(pin_size_t ulPin, PinMode ulMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ulPin > 29) {
|
||||
DEBUGCORE("ERROR: Illegal pin in pinMode (%d)\n", ulPin);
|
||||
return;
|
||||
}
|
||||
_pm[ulPin] = ulMode;
|
||||
|
||||
if (ulPin >= __FIRSTANALOGGPIO) {
|
||||
__clearADCPin(ulPin);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void digitalWrite(pin_size_t ulPin, PinStatus ulVal) __attribute__((weak, alias("__digitalWrite")));
|
||||
extern "C" void __digitalWrite(pin_size_t ulPin, PinStatus ulVal) {
|
||||
if (ulPin > 29) {
|
||||
if (ulPin >= __GPIOCNT) {
|
||||
DEBUGCORE("ERROR: Illegal pin in pinMode (%d)\n", ulPin);
|
||||
return;
|
||||
}
|
||||
@@ -103,7 +110,7 @@ extern "C" void __digitalWrite(pin_size_t ulPin, PinStatus ulVal) {
|
||||
|
||||
extern "C" PinStatus digitalRead(pin_size_t ulPin) __attribute__((weak, alias("__digitalRead")));
|
||||
extern "C" PinStatus __digitalRead(pin_size_t ulPin) {
|
||||
if (ulPin > 29) {
|
||||
if (ulPin >= __GPIOCNT) {
|
||||
DEBUGCORE("ERROR: Illegal pin in digitalRead (%d)\n", ulPin);
|
||||
return LOW;
|
||||
}
|
||||
|
||||
@@ -23,29 +23,40 @@
|
||||
#include <hardware/gpio.h>
|
||||
#include <hardware/sync.h>
|
||||
#include <map>
|
||||
#include "_freertos.h"
|
||||
|
||||
|
||||
// Support nested IRQ disable/re-enable
|
||||
#ifndef maxIRQs
|
||||
#define maxIRQs 15
|
||||
#endif
|
||||
static uint32_t _irqStackTop[2] = { 0, 0 };
|
||||
static uint32_t _irqStack[2][maxIRQs];
|
||||
|
||||
extern "C" void interrupts() {
|
||||
auto core = get_core_num();
|
||||
if (!_irqStackTop[core]) {
|
||||
// ERROR
|
||||
return;
|
||||
if (__freeRTOSinitted) {
|
||||
__freertos_task_exit_critical();
|
||||
} else {
|
||||
auto core = get_core_num();
|
||||
if (!_irqStackTop[core]) {
|
||||
// ERROR
|
||||
return;
|
||||
}
|
||||
restore_interrupts(_irqStack[core][--_irqStackTop[core]]);
|
||||
}
|
||||
restore_interrupts(_irqStack[core][--_irqStackTop[core]]);
|
||||
}
|
||||
|
||||
extern "C" void noInterrupts() {
|
||||
auto core = get_core_num();
|
||||
if (_irqStackTop[core] == maxIRQs) {
|
||||
// ERROR
|
||||
panic("IRQ stack overflow");
|
||||
if (__freeRTOSinitted) {
|
||||
__freertos_task_enter_critical();
|
||||
} else {
|
||||
auto core = get_core_num();
|
||||
if (_irqStackTop[core] == maxIRQs) {
|
||||
// ERROR
|
||||
panic("IRQ stack overflow");
|
||||
}
|
||||
_irqStack[core][_irqStackTop[core]++] = save_and_disable_interrupts();
|
||||
}
|
||||
|
||||
_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
|
||||
@@ -76,15 +87,22 @@ static std::map<pin_size_t, CBInfo> _map;
|
||||
|
||||
void _gpioInterruptDispatcher(uint gpio, uint32_t events) {
|
||||
(void) events;
|
||||
|
||||
// Only need to lock around the std::map check, not the whole IRQ callback
|
||||
CoreMutex m(&_irqMutex);
|
||||
if (m) {
|
||||
auto irq = _map.find(gpio);
|
||||
if (irq != _map.end()) {
|
||||
auto cb = irq->second;
|
||||
cb.callback();
|
||||
CBInfo *cb;
|
||||
{
|
||||
CoreMutex m(&_irqMutex);
|
||||
if (m) {
|
||||
auto irq = _map.find(gpio);
|
||||
if (irq == _map.end()) {
|
||||
return;
|
||||
}
|
||||
cb = &irq->second;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
cb->callback();
|
||||
}
|
||||
|
||||
// To be called when appropriately protected w/IRQ and mutex protects
|
||||
@@ -135,7 +153,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);
|
||||
|
||||
@@ -26,7 +26,7 @@ extern "C" unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeo
|
||||
uint64_t start = time_us_64();
|
||||
uint64_t abort = start + timeout;
|
||||
|
||||
if (pin > 29) {
|
||||
if (pin >= __GPIOCNT) {
|
||||
DEBUGCORE("ERROR: Illegal pin in pulseIn (%d)\n", pin);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
extern "C" uint8_t shiftIn(pin_size_t dataPin, pin_size_t clockPin, BitOrder bitOrder) {
|
||||
uint8_t value = 0;
|
||||
uint8_t i;
|
||||
if (dataPin > 29) {
|
||||
if (dataPin >= __GPIOCNT) {
|
||||
DEBUGCORE("ERROR: Illegal dataPin in shiftIn (%d)\n", dataPin);
|
||||
return 0;
|
||||
}
|
||||
if (clockPin > 29) {
|
||||
if (clockPin >= __GPIOCNT) {
|
||||
DEBUGCORE("ERROR: Illegal clockPin in shiftIn (%d)\n", clockPin);
|
||||
return 0;
|
||||
}
|
||||
@@ -46,11 +46,11 @@ extern "C" uint8_t shiftIn(pin_size_t dataPin, pin_size_t clockPin, BitOrder bit
|
||||
|
||||
extern "C" void shiftOut(pin_size_t dataPin, pin_size_t clockPin, BitOrder bitOrder, uint8_t val) {
|
||||
uint8_t i;
|
||||
if (dataPin > 29) {
|
||||
if (dataPin >= __GPIOCNT) {
|
||||
DEBUGCORE("ERROR: Illegal dataPin in shiftOut (%d)\n", dataPin);
|
||||
return;
|
||||
}
|
||||
if (clockPin > 29) {
|
||||
if (clockPin >= __GPIOCNT) {
|
||||
DEBUGCORE("ERROR: Illegal clockPin in shiftOut (%d)\n", clockPin);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -66,10 +66,14 @@ typedef enum {
|
||||
} wl_status_t;
|
||||
|
||||
/* Encryption modes */
|
||||
enum wl_enc_type { /* Values map to 802.11 encryption suites... */
|
||||
enum wl_enc_type { /* Values map to 802.11 Cipher Algorithm Identifier */
|
||||
ENC_TYPE_WEP = 5,
|
||||
ENC_TYPE_TKIP = 2,
|
||||
ENC_TYPE_WPA = ENC_TYPE_TKIP,
|
||||
ENC_TYPE_CCMP = 4,
|
||||
ENC_TYPE_WPA2 = ENC_TYPE_CCMP,
|
||||
ENC_TYPE_GCMP = 6,
|
||||
ENC_TYPE_WPA3 = ENC_TYPE_GCMP,
|
||||
/* ... except these two, 7 and 8 are reserved in 802.11-2007 */
|
||||
ENC_TYPE_NONE = 7,
|
||||
ENC_TYPE_AUTO = 8,
|
||||
@@ -0,0 +1,39 @@
|
||||
Bluetooth Audio (A2DP Source and Sink)
|
||||
======================================
|
||||
|
||||
The PicoW can be used as a Bluetooth Audio sink or source with the ``BluetoothAudio`` class.
|
||||
Operation is generally handled "automatically" in the background so while the audio is
|
||||
playing or streaming the main application can perform other operations (like displaying
|
||||
playback info, polling buttons for controls, etc.)
|
||||
|
||||
.. code :: cpp
|
||||
|
||||
#include <BluetoothAudio.h>
|
||||
...
|
||||
|
||||
**Note about CPU usage:** Bluetooth SBC audio is a compressed format. That means
|
||||
that it takes non-trivial amounts of CPU to compress on send, or decompress on receive.
|
||||
Transmitting precompressed audio from, say, MP3 or AAC, requires first decompressing
|
||||
the source file into raw PCM and then re-compressing them in the SBC format. You may
|
||||
want to consider overclocking in this case to avoid underflow.
|
||||
|
||||
A2DPSink
|
||||
--------
|
||||
|
||||
This class implements slave sink-mode operation with player control (play, pause, etc.) and
|
||||
can play the received and decoded SBC audio to ``PWMAudio``, ``I2S``, or a user-created
|
||||
`BluetoothAudioConsumer`` class.
|
||||
|
||||
The ``A2DPSink.ino`` example demonstrates turning a PicoW into a Bluetooth headset with
|
||||
``PWMAudio``.
|
||||
|
||||
A2DPSource
|
||||
-----------
|
||||
|
||||
This class implements a master source-mode SBC Bluetooth A2DP audio connection which
|
||||
transmits audio using the standard ``Stream`` interface (like ``I2S`` or ``PWMAudio``.
|
||||
The main application connects to a Bluetooth speaker and then writes samples into a buffer
|
||||
that's automatically transmitted behind the scenes.
|
||||
|
||||
The ``A2DPSource.ino`` example shows how to connect to a Bluetooth speaker, transmit
|
||||
data, and respond to commands from the speaker.
|
||||
@@ -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().
|
||||
+8
-5
@@ -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.
|
||||
@@ -29,7 +30,7 @@ Analog Outputs
|
||||
--------------
|
||||
The RP2040 does not have any onboard DACs, so analog outputs are
|
||||
simulated using the standard method of using pulse width modulation
|
||||
(PWM) using the RP20400's hardware PWM units.
|
||||
(PWM) using the RP2040's hardware PWM units.
|
||||
|
||||
While up to 16 PWM channels can be generated, they are not independent
|
||||
and there are significant restrictions as to allowed pins in parallel.
|
||||
@@ -39,7 +40,9 @@ Analog Output Restrictions
|
||||
--------------------------
|
||||
|
||||
The PWM generator source clock restricts the legal combinations of
|
||||
frequency and ranges. For example, at 1MHz only about 6 bits of range
|
||||
frequency and ranges.
|
||||
At a CPU frequency of 133MHz, the 16 bit maximum range decreases by 1 bit for every doubling of the default PWM frequency of 1 kHz.
|
||||
For example, at 1MHz only about 6 bits of range
|
||||
are possible. When you define an ``analogWriteFreq`` and ``analogWriteRange``
|
||||
that can't be fulfilled by the hardware, the frequency will be preserved
|
||||
but the accuracy (range) will be reduced automatically. Your code will
|
||||
|
||||
@@ -120,7 +120,7 @@ Valid values for min and max are `BR_TLS10`, `BR_TLS11`, `BR_TLS12`. Min and ma
|
||||
|
||||
|
||||
ESP32 Compatibility
|
||||
===================
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
Simple ESP32 ``WiFiClientSecure`` compatibility is built-in, allow for some sketches to run without any modification.
|
||||
The following methods are implemented:
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ Similar to the `BearSSL::WiFiClientSecure` method, sets the receive and transmit
|
||||
Setting Server Certificates
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
TLS servers require a certificate identifying itself and containing its public key, and a private key they will use to encrypt information with. The application author is responsible for generating this certificate and key, either using a self-signed generator or using a commercial certification authority. **Do not re-use the certificates included in the examples provided.**
|
||||
TLS servers require a certificate identifying itself and containing its public key, and a private key they will use to encrypt information with. The application author is responsible for generating this certificate and key, either using a self-signed generator or using a commercial certification authority. **Do not reuse the certificates included in the examples provided.**
|
||||
|
||||
This example command will generate a RSA 2048-bit key and certificate:
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
Bluetooth on PicoW Support
|
||||
==========================
|
||||
|
||||
As of the Pico-SDK version 1.5.0, the PicoW has **BETA** Bluetooth support.
|
||||
|
||||
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.
|
||||
|
||||
Connect and use Bluetooth peripherals with the PicoW using the
|
||||
``BluetoothHIDMaster`` library.
|
||||
|
||||
``BluetoothAudio`` (A2DP) is also supported, both sink and source.
|
||||
|
||||
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. ``__lockBluetooth`` and ``unlockBluetooth`` are
|
||||
provided in the PicoW variant code.
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,21 @@
|
||||
BOOTSEL Button
|
||||
==============
|
||||
|
||||
The BOOTSEL button on the Pico is not connected to a standard GPIO, so
|
||||
it cannot be read using the usual ``digitalRead`` function. It **can**,
|
||||
however, be read using a special (relatively slow) method.
|
||||
|
||||
The ``BOOTSEL`` object implements a simple way of reading the BOOTSEL
|
||||
button. Simply use the object ``BOOTSEL`` as a boolean (as a conditional
|
||||
in an ``if`` or ``while``, or assigning to a ``bool``):
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
// Print "BEEP" if the BOOTSEL button is pressed
|
||||
if (BOOTSEL) {
|
||||
Serial.println("BEEP!");
|
||||
// Wait until BOOTSEL is released
|
||||
while (BOOTSEL) {
|
||||
delay(1);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -54,9 +54,9 @@ author = u'Earle F. Philhower, III'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = u'2.6.2'
|
||||
version = u'4.2.0'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = u'2.6.2'
|
||||
release = u'4.2.0'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
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
|
||||
|
||||
Identifying RP2040, RP2530A, or RP2350B
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To check if a board is an original RP2040
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
#if defined(PICO_RP2040)
|
||||
...OG Pico code...
|
||||
#endif
|
||||
|
||||
For RP2350(A or B):
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
#if defined(PICO_RP2350)
|
||||
...Pico 2 code...
|
||||
#endif
|
||||
|
||||
For only RP2350A variants (using the compile options, not the onboard ID register):
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
#if defined(PICO_RP2350) && !defined(PICO_RP2350B)
|
||||
...RP2350A only code...
|
||||
#endif
|
||||
|
||||
For only RP2350B variants (again, at compile time as identified by the selected board
|
||||
and not the chip ID register):
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
#if defined(PICO_RP2350B)
|
||||
...48-GPIO version code here
|
||||
#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.
|
||||
Note that even the RP2350 is identified as ``rp2040`` for this purpose.
|
||||
@@ -6,6 +6,10 @@ Board-Specific Pins
|
||||
The Raspberry Pi Pico RP2040 chip supports up to 30 digital I/O pins,
|
||||
however not all boards provide access to all pins.
|
||||
|
||||
Pin Notation
|
||||
------------
|
||||
When using Analog or Digital I/Os, if you supply an integer it specifies the RP2040 GPIO pin to use. Using Dx or Ax notation (for example, D4 or A3) may be necessary on boards without a direct PCB pin to GPIO mapping.
|
||||
|
||||
Input Modes
|
||||
-----------
|
||||
The Raspberry Pi Pico has 3 Input modes settings for use with `pinMode`: `INPUT`, `INPUT_PULLUP` and `INPUT_PULLDOWN`
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
EthernetLWIP (Wired Ethernet) Support
|
||||
=====================================
|
||||
|
||||
Wired Ethernet interfaces are supported for all the internal networking
|
||||
libraries (``WiFiClient``, ``WiFiClientSecure``, ``WiFiServer``,
|
||||
``WiFiServerSecure``, ``WiFiUDP``, ``WebServer``, ``Updater``,
|
||||
``HTTPClient``, etc.).
|
||||
|
||||
Using these wired interfaces is very similar to using the Pico-W WiFi
|
||||
so most examples in the core only require minor modifications to use
|
||||
a wired interface.
|
||||
|
||||
Supported Wired Ethernet Modules
|
||||
--------------------------------
|
||||
|
||||
* Wiznet W5100(s)
|
||||
|
||||
* Wiznet W5500
|
||||
|
||||
* ENC28J60
|
||||
|
||||
|
||||
Enabling Wired Ethernet
|
||||
-----------------------
|
||||
|
||||
Simply replace the WiFi include at the top with:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
#include <W5500lwIP.h> // Or W5100lwIP.h or ENC28J60.h
|
||||
|
||||
|
||||
And add a global Ethernet object of the same type:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
Wiznet5500lwIP eth(1); // Parameter is the Chip Select pin
|
||||
|
||||
In your ``setup()`` you may adjust the SPI pins you're using to
|
||||
match your hardware (be sure they are legal for the RP2040!), or
|
||||
skip this if you're using the default ones:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
void setup() {
|
||||
SPI.setRX(0);
|
||||
SPI.setCS(1);
|
||||
SPI.setSCK(2);
|
||||
SPI.setTX(3);
|
||||
....
|
||||
}
|
||||
|
||||
And finally replace the ``WiFi.begin()`` and ``WiFi.connected()``
|
||||
calls with ``eth.begin()`` and ``eth.connected()``:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
void setup() {
|
||||
....
|
||||
// WiFi.begin(SSID, PASS)
|
||||
eth.begin();
|
||||
|
||||
//while (!WiFi.connected()) {
|
||||
while (!eth.connected()) {
|
||||
Serial.print(".");
|
||||
}
|
||||
|
||||
Serial.print("IP address: ");
|
||||
//Serial.println(WiFi.localIP());
|
||||
Serial.println(eth.localIP());
|
||||
|
||||
....
|
||||
}
|
||||
|
||||
Adjusting LWIP Polling
|
||||
----------------------
|
||||
|
||||
LWIP operates in a polling mode for the wired Ethernet devices. By default it will run
|
||||
every 20ms, meaning that on average it will take half that time (10ms) before a packet
|
||||
received in the Ethernet module is received and operated upon by the Pico. This gives
|
||||
very low CPU utilization but in some cases this latency can affect performance.
|
||||
|
||||
Adding a call to ``lwipPollingPeriod(XXX)`` (where ``XXXX`` is the polling period in
|
||||
milliseconds) can adjust this setting on the fly. Note that if you set it too low, the
|
||||
Pico may not have enough time to service the Ethernet port before the timer fires again,
|
||||
leading to a lock up and hang.
|
||||
|
||||
|
||||
Using Interrupt-Driven Handling
|
||||
-------------------------------
|
||||
|
||||
The WizNet and ENC28J60 devices support generating an interrupt when a packet is received,
|
||||
removing the need for polling and decreasing latency. Simply specify the SPI object to use and the
|
||||
interrupt pin when instantiating the Ethernet object:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
#include <W5100lwIP.h>
|
||||
Wiznet5100lwIP eth(SS /* Chip Select*/, SPI /* SPI interface */, 17 /* Interrupt GPIO */ );
|
||||
|
||||
|
||||
Adjusting SPI Speed
|
||||
-------------------
|
||||
|
||||
By default a 4MHz clock will be used to clock data into and out of the Ethernet module.
|
||||
Depending on the module and your wiring, a higher SPI clock may increase performance (but
|
||||
too high of a clock will cause communications problems or hangs).
|
||||
|
||||
This value may be adjusted using the ``eth.setSPISpeed(hz)`` call **before** starting the
|
||||
device. (You may also use custom ``SPISettings`` instead via ``eth.setSPISettings(spis)```)
|
||||
|
||||
For example, to set the W5500 to use a 30MHZ clock:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
#include <W5500lwIP.h>
|
||||
Wiznet5500lwIP eth(1);
|
||||
|
||||
void setup() {
|
||||
eth.setSPISpeed(30000000);
|
||||
lwipPollingPeriod(3);
|
||||
...
|
||||
eth.begin();
|
||||
...
|
||||
}
|
||||
|
||||
Using the WIZnet W5100S-EVB-Pico
|
||||
--------------------------------
|
||||
|
||||
You can use the onboard Ethernet chip with these drivers, in interrupt mode, by utilizing the following options:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
#include <W5100lwIP.h>
|
||||
Wiznet5100lwIP eth(17, SPI, 21); // Note chip select is **17**
|
||||
|
||||
void setup() {
|
||||
// Set SPI to the onboard Wiznet chip
|
||||
SPI.setRX(16);
|
||||
SPI.setCS(17);
|
||||
SPI.setSCK(18);
|
||||
SPI.setTX(19);
|
||||
...
|
||||
eth.begin();
|
||||
...
|
||||
}
|
||||
|
||||
Example Code
|
||||
------------
|
||||
|
||||
The following examples allow switching between WiFi and Ethernet:
|
||||
|
||||
* ``WebServer/AdvancedWebServer``
|
||||
|
||||
* ``HTTPClient/BasicHTTPSClient``
|
||||
|
||||
Caveats
|
||||
-------
|
||||
|
||||
The same restrictions for ``WiFi`` apply to these Ethernet classes, namely:
|
||||
|
||||
* Only core 0 may run any networking related code.
|
||||
|
||||
* In FreeRTOS, only the ``setup`` and ``loop`` task can call networking libraries, not any tasks.
|
||||
|
||||
Special Thanks
|
||||
--------------
|
||||
|
||||
* LWIPEthernet classes come from the ESP8266 Arduino team
|
||||
|
||||
* Individual Ethernet drivers were written by Nicholas Humfrey
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
FatFSUSB
|
||||
========
|
||||
|
||||
When the onboard flash memory is used as a ``FatFS`` filesystem, the
|
||||
``FatFSUSB`` can be used to allow exporting it to a PC as a standard
|
||||
memory stick. The PC can then access, add, and remove files as if the
|
||||
Pico was a USB memory stick, and upon ejection the Pico can access
|
||||
any new files just as if it made them itself.
|
||||
|
||||
(Note, if you are using LittleFS then you need to use ``SingleFileDrive``
|
||||
to export a single file, not this class, because the PC does not
|
||||
understand the LittleFS disk format.)
|
||||
|
||||
Callbacks, Interrupt Safety, and File Operations
|
||||
------------------------------------------------
|
||||
|
||||
The ``FatFSUSB`` library allows your application to get a callback
|
||||
when a PC attempts to mount or unmount the Pico as a FAT drive.
|
||||
|
||||
When the drive is being used by the Pico (i.e. any ``File`` is open for
|
||||
read or write, the ``FatFS`` is not ``end()`` -ed and still mounted,
|
||||
etc.) the host may not access it. Conversely, while the host PC is
|
||||
connected to the drive no ``FatFS`` access by the Pico is allowed.
|
||||
|
||||
Your ``driveReady`` callback will be called when the PC attempts to mount
|
||||
the drive. If you have any files open, then this callback can report back
|
||||
that the drive is not yet ready. When you complete file processing, the PC
|
||||
can re-attempt to mount the drive and your callback can return ``true`` .
|
||||
|
||||
The ``onPlug`` callback will generally ``FatFS.end()`` and set a
|
||||
global flag letting your application know not to touch the filesystem until
|
||||
the flag is cleared by the ``onUnplug`` callback (which will also do a
|
||||
``FatFS.begin()`` call).
|
||||
|
||||
Failing to close all files **and** ``FatFS.end()`` before granting the
|
||||
PC access to flash memory will result in corruption. FAT does not allow multiple
|
||||
writers to access the same drive. Even mounting and only reading files from
|
||||
the PC may cause hidden writes (things like access time, etc.) which would
|
||||
also cause corruption.
|
||||
|
||||
See the included ``Listfiles-USB`` sketch for an example of working with
|
||||
these limitations.
|
||||
+131
-55
@@ -18,12 +18,12 @@ The following diagram shows the flash layout used in Arduino-Pico:
|
||||
|
||||
::
|
||||
|
||||
|---------------------|-------------|----|
|
||||
^ ^ ^
|
||||
Sketch File system EEPROM
|
||||
|----|---------------------|-------------|----|
|
||||
^ ^ ^ ^
|
||||
OTA Sketch File system EEPROM
|
||||
|
||||
The file system size is configurable via the IDE menus, rom 64k up to 15MB
|
||||
(assuming you have an RP2040 boad with that much flash)
|
||||
The file system size is configurable via the IDE menus, from 64k up to 15MB
|
||||
(assuming you have an RP2040 board with that much flash)
|
||||
|
||||
**Note:** to use any of file system functions in the sketch, add the
|
||||
following include to the sketch:
|
||||
@@ -33,31 +33,68 @@ following include to the sketch:
|
||||
#include "LittleFS.h" // LittleFS is declared
|
||||
// #include <SDFS.h>
|
||||
// #include <SD.h>
|
||||
// #include <FatFS.h>
|
||||
|
||||
|
||||
Compatible Filesystem APIs
|
||||
--------------------------
|
||||
|
||||
LittleFS is an onboard filesystem that sets asidesome program flash for
|
||||
LittleFS is an onboard filesystem that sets aside some program flash for
|
||||
use as a filesystem without requiring any external hardware.
|
||||
|
||||
SDFS is a filesystem for SD cards, based on [SdFat 2.0](https://github.com/earlephilhower/ESP8266SdFat).
|
||||
It supports FAT16 and FAT32 formatted cards, and requires an external
|
||||
SD card reader.
|
||||
|
||||
SD is the Arduino supported, somewhat old and limited SD card filesystem.
|
||||
SD is the Arduino-supported, somewhat old and limited SD card filesystem.
|
||||
It is recommended to use SDFS for new applications instead of SD.
|
||||
|
||||
All three of these filesystems can open and manipulate ``File`` and ``Dir``
|
||||
FatFS implements a wear-leveled, FTL-backed FAT filesystem in the onboard
|
||||
flash which can be easily accessed over USB as a standard memory stick
|
||||
via FatFSUSB.
|
||||
|
||||
All of these filesystems can open and manipulate ``File`` and ``Dir``
|
||||
objects with the same code because the implement a common end-user
|
||||
filesystem API.
|
||||
|
||||
FatFS File System Caveats and Warnings
|
||||
--------------------------------------
|
||||
|
||||
The FAT filesystem is ubiquitous, but it is also around 50 years old and ill
|
||||
suited to SPI flash memory due to having "hot spots" like the FAT copies that
|
||||
are rewritten many times over. SPI flash allows a high, but limited, number
|
||||
of writes before losing the ability to write safely. Applications like
|
||||
data loggers where many writes occur could end up wearing out the SPI flash
|
||||
sector that holds the FAT **years** before coming close to the write limits of
|
||||
the data sectors.
|
||||
|
||||
To circumvent this issue, the FatFS implementation here uses a flash translation
|
||||
layer (FTL) developed for SPI flash on embedded systems. This allows for the
|
||||
same LBA to be written over and over by the FAT filesystem, but use different
|
||||
flash locations. For more information see
|
||||
[SPIFTL](https://github.com/earlephilhower/SPIFTL). In this mode the Pico
|
||||
flash appears as a normal, 512-byte sector drive to the FAT.
|
||||
|
||||
What this means, practically, is that about 5KB of RAM per megabyte of flash
|
||||
is required for housekeeping. Writes can also become very slow if most of the
|
||||
flash LBA range is used (i.e. if the FAT drive is 99% full) due to the need
|
||||
for garbage collection processes to move data around and preserve the flash
|
||||
lifetime.
|
||||
|
||||
Alternatively, if an FTL is not desired or memory is tight, FatFS can use the
|
||||
raw flash directly. In this mode sectors are 4K in size and flash is mapped
|
||||
1:1 to sectors, so things like the FAT table updates will all use the same
|
||||
physical flash bits. For low-utilization operations this may be fine, but if
|
||||
significant writes are done (from the Pico or the PC host) this may wear out
|
||||
portions of flash very quickly , rendering it unusable.
|
||||
|
||||
LittleFS File System Limitations
|
||||
--------------------------------
|
||||
|
||||
The LittleFS implementation for the RP2040 supports filenames of up
|
||||
to 31 characters + terminating zero (i.e. ``char filename[32]``), and
|
||||
as many subdirectories as space permits.
|
||||
to 254 characters + terminating zero (i.e. ``char filename[255]`` or
|
||||
better ``char filename[LFS_NAME_MAX]`` ), and as many subdirectories
|
||||
as space permits.
|
||||
|
||||
Filenames are assumed to be in the root directory if no initial "/" is
|
||||
present.
|
||||
@@ -75,22 +112,28 @@ Uploading Files to the LittleFS File System
|
||||
menu item to **Tools** menu for uploading the contents of sketch data
|
||||
directory into a new LittleFS flash file system.
|
||||
|
||||
**IDE 1.x**
|
||||
|
||||
- Download the tool: https://github.com/earlephilhower/arduino-pico-littlefs-plugin/releases
|
||||
- In your Arduino sketchbook directory, create ``tools`` directory if
|
||||
it doesn't exist yet.
|
||||
- Unpack the tool into ``tools`` directory (the path will look like
|
||||
``<home_dir>/Arduino/tools/PicoLittleFS/tool/picolittlefs.jar``)
|
||||
- In your Arduino sketchbook directory, create ``tools`` directory if it doesn't exist yet.
|
||||
- Unpack the tool into ``tools`` directory (the path will look like ``<home_dir>/Arduino/tools/PicoLittleFS/tool/picolittlefs.jar``)
|
||||
If upgrading, overwrite the existing JAR file with the newer version.
|
||||
- Restart Arduino IDE.
|
||||
- Open a sketch (or create a new one and save it).
|
||||
- Go to sketch directory (choose Sketch > Show Sketch Folder).
|
||||
- Create a directory named ``data`` and any files you want in the file
|
||||
system there.
|
||||
- Create a directory named ``data`` and any files you want in the file system there.
|
||||
- Make sure you have selected a board, port, and closed Serial Monitor.
|
||||
- Double check theSerial Monitor is closed. Uploads will fail if the Serial
|
||||
Monitor has control of the serial port.
|
||||
- Select ``Tools > Pico LittleFS Data Upload``. This should start
|
||||
uploading the files into the flash file system.
|
||||
- Double check the Serial Monitor is closed. Uploads will fail if the Serial Monitor has control of the serial port.
|
||||
- Select ``Tools > Pico LittleFS Data Upload``. This should start uploading the files into the flash file system.
|
||||
|
||||
**IDE 2.x**
|
||||
|
||||
- Download the new tool: https://github.com/earlephilhower/arduino-littlefs-upload/releases
|
||||
- Exit the IDE, if running
|
||||
- Copy the VSIX file manually to (Linux/Mac) ``~/.arduinoIDE/plugins/`` (you may need to make this directory yourself beforehand) or to (Windows) ``C:\Users\<username>\.arduinoIDE\``
|
||||
- Restart the IDE
|
||||
- Double check the Serial Monitor is closed. Uploads will fail if the Serial Monitor has control of the serial port.
|
||||
- Enter ``[Ctrl]`` + ``[Shift]`` + ``[P]`` to bring up the command palette, then select/type ``Upload LittleFS to Pico/ESP8266``
|
||||
|
||||
SD Library Information
|
||||
----------------------
|
||||
@@ -109,8 +152,50 @@ second SPI port, ``SPI1``. Just use the following call in place of
|
||||
SD.begin(cspin, SPI1);
|
||||
|
||||
|
||||
File system object (LittleFS/SD/SDFS)
|
||||
-------------------------------------
|
||||
Using VFS (Virtual File System) for POSIX support
|
||||
-------------------------------------------------
|
||||
The ``VFS`` library enables sketches to use standard POSIX file I/O operations using
|
||||
standard ``FILE *`` operations. Include the ``VFS`` library in your application and
|
||||
add a call to map the ``VFS.root()`` to your filesystem. I.e.:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
#include <VFS.h>
|
||||
#include <LittleFS.h>
|
||||
|
||||
void setup() {
|
||||
LittleFS.begin();
|
||||
VFS.root(LittleFS);
|
||||
FILE *fp = fopen("/thisfilelivesonflash.txt", "w");
|
||||
fprintf(fp, "Hello!\n");
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
Multiple filesystems can be ``VFS.map()`` into the VFS namespace under different directory
|
||||
names. For example, the following will make files on ``/sd`` reside on an external\
|
||||
SD card and files on ``/lfs`` live in internal flash.
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
#include <VFS.h>
|
||||
#include <LittleFS.h>
|
||||
#include <SDFS.h>
|
||||
|
||||
void setup() {
|
||||
LittleFS.begin();
|
||||
SDFS.begin();
|
||||
VFS.map("/lfs", LittleFS);
|
||||
VFS.map("/sd", SDFS);
|
||||
FILE *onSD = fopen("/sd/thislivesonsd.txt", "wb");
|
||||
....
|
||||
}
|
||||
|
||||
See the examples in the ``VFS`` library for more information.
|
||||
|
||||
|
||||
|
||||
File system object (LittleFS/SD/SDFS/FatFS)
|
||||
-------------------------------------------
|
||||
|
||||
setConfig
|
||||
~~~~~~~~~
|
||||
@@ -125,14 +210,22 @@ setConfig
|
||||
c2.setCSPin(12);
|
||||
SDFS.setConfig(c2);
|
||||
|
||||
FatFSConfig c3;
|
||||
c3.setUseFTL(false); // Directly access flash memory
|
||||
c3.setDirEntries(256); // We need 256 root directory entries on a format()
|
||||
c3.setFATCopies(1); // Only 1 FAT to save 4K of space and extra writes
|
||||
FatFS.setConfig(c3);
|
||||
FatFS.format(); // Format using these settings, erasing everything
|
||||
|
||||
This method allows you to configure the parameters of a filesystem
|
||||
before mounting. All filesystems have their own ``*Config`` (i.e.
|
||||
``SDFSConfig`` or ``LittleFSConfig`` with their custom set of options.
|
||||
All filesystems allow explicitly enabling/disabling formatting when
|
||||
mounts fail. If you do not call this ``setConfig`` method before
|
||||
perforing ``begin()``, you will get the filesystem's default
|
||||
behavior and configuration. By default, SPIFFS will autoformat the
|
||||
filesystem if it cannot mount it, while SDFS will not.
|
||||
behavior and configuration. By default, LittleFS and FatFS will autoformat the
|
||||
filesystem if it cannot mount it, while SDFS will not. FatFS will also use
|
||||
the built-in FTL to support 512 byte sectors and higher write lifetime.
|
||||
|
||||
begin
|
||||
~~~~~
|
||||
@@ -144,11 +237,10 @@ begin
|
||||
|
||||
This method mounts file system. It must be called before any
|
||||
other FS APIs are used. Returns *true* if file system was mounted
|
||||
successfully, false otherwise. With no options it will format SPIFFS
|
||||
if it is unable to mount it on the first try.
|
||||
successfully, false otherwise.
|
||||
|
||||
Note that LittleFS will automatically format the filesystem
|
||||
if one is not detected. This is configurable via ``setConfig``
|
||||
if one is not detected. This is configurable via ``setConfig``.
|
||||
|
||||
end
|
||||
~~~
|
||||
@@ -181,8 +273,8 @@ open
|
||||
|
||||
Opens a file. ``path`` should be an absolute path starting with a slash
|
||||
(e.g. ``/dir/filename.txt``). ``mode`` is a string specifying access
|
||||
mode. It can be one of "r", "w", "a", "r+", "w+", "a+". Meaning of these
|
||||
modes is the same as for ``fopen`` C function.
|
||||
mode. It can be one of "r", "w", "a", "r+", "w+", "a+". The meaning of these
|
||||
modes is the same as for the ``fopen`` C function.
|
||||
|
||||
::
|
||||
|
||||
@@ -258,8 +350,6 @@ openDir
|
||||
or LittleFS.openDir(path)
|
||||
|
||||
Opens a directory given its absolute path. Returns a *Dir* object.
|
||||
Please note the previous discussion on the difference in behavior between
|
||||
LittleFS and SPIFFS for this call.
|
||||
|
||||
remove
|
||||
~~~~~~
|
||||
@@ -283,8 +373,8 @@ rename
|
||||
Renames file from ``pathFrom`` to ``pathTo``. Paths must be absolute.
|
||||
Returns *true* if file was renamed successfully.
|
||||
|
||||
info **DEPRECATED**
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
info
|
||||
~~~~
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
@@ -293,9 +383,8 @@ info **DEPRECATED**
|
||||
|
||||
Fills `FSInfo structure <#filesystem-information-structure>`__ with
|
||||
information about the file system. Returns ``true`` if successful,
|
||||
``false`` otherwise. Because this cannot report information about
|
||||
filesystemd greater than 4MB, don't use it in new code. Use ``info64``
|
||||
instead which uses 64-bit fields for filesystem sizes.
|
||||
``false`` otherwise. ``ìnfo()`` has been updated to support filesystems
|
||||
greater than 4GB and ``FSInfo64`` and ``info64()`` have been discarded.
|
||||
|
||||
Filesystem information structure
|
||||
--------------------------------
|
||||
@@ -303,8 +392,8 @@ Filesystem information structure
|
||||
.. code:: cpp
|
||||
|
||||
struct FSInfo {
|
||||
size_t totalBytes;
|
||||
size_t usedBytes;
|
||||
uint64_t totalBytes;
|
||||
uint64_t usedBytes;
|
||||
size_t blockSize;
|
||||
size_t pageSize;
|
||||
size_t maxOpenFiles;
|
||||
@@ -319,19 +408,6 @@ block size - ``pageSize`` — filesystem logical page size - ``maxOpenFiles``
|
||||
``maxPathLength`` — max file name length (including one byte for zero
|
||||
termination)
|
||||
|
||||
info64
|
||||
~~~~~~
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
FSInfo64 fsinfo;
|
||||
SDFS.info(fsinfo);
|
||||
or LittleFS(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
|
||||
filesystems since most SD cards today are greater than 4GB in size.
|
||||
|
||||
setTimeCallback(time_t (\*cb)(void))
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -550,8 +626,8 @@ close
|
||||
Close the file. No other operations should be performed on *File* object
|
||||
after ``close`` function was called.
|
||||
|
||||
openNextFile (compatibiity method, not recommended for new code)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
openNextFile (compatibility method, not recommended for new code)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
@@ -562,8 +638,8 @@ openNextFile (compatibiity method, not recommended for new code)
|
||||
Opens the next file in the directory pointed to by the File. Only valid
|
||||
when ``File.isDirectory() == true``.
|
||||
|
||||
rewindDirectory (compatibiity method, not recommended for new code)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
rewindDirectory (compatibility method, not recommended for new code)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
|
||||
+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
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
Bluetooth HID Master
|
||||
====================
|
||||
|
||||
The PicoW can connect to a Bluetooth Classic or Bluetooth BLE keyboard,
|
||||
mouse, or joystick and receive input events from it. As opposed to
|
||||
the ``Keyboard``, ``Mouse``, and ``Joystick`` libraries, which make
|
||||
the PicoW into a peripheral others can use, this lets the PicoW use the
|
||||
same kinds of peripherals in a master rols.
|
||||
|
||||
BTDeviceInfo Class
|
||||
------------------
|
||||
|
||||
The ``BluetoothHCI`` class implements a scanning function for classic
|
||||
and BLE devices and can return a ``std::vector`` of discovered devices to an application.
|
||||
Iterate over the list using any of the STL iteration methods (i.e. ``for (auto e : list)``).
|
||||
The elements of this list are ``BTDeviceInfo`` objects which have the following
|
||||
member functions:
|
||||
|
||||
``BTDeviceInfo::deviceClass()`` returns the Bluetooth BLE UUID or the Blustooth device
|
||||
class for the device. This specifies the general class of the device (keyboard, mouse,
|
||||
etc.).
|
||||
|
||||
``BTDeviceInfo::address()`` and ``BTDeviceInfo::addressString()`` return the
|
||||
Bluetooth address as a binary array or a string that can be used to ``print``.
|
||||
|
||||
``BTDeviceInfo::addressType()`` returns whether the BLE address is random or not, and
|
||||
is not generally needed by a user application.
|
||||
|
||||
``BTDeviceInfo::rssi()`` returns an approximate dB level for the device. Less
|
||||
negative is stronger signal.
|
||||
|
||||
``BTDeviceInfo::name()`` returns the advertised name of the device, if present. Some
|
||||
devices or scans do not return names for valid devices.
|
||||
|
||||
|
||||
BluetoothHCI Class
|
||||
------------------
|
||||
|
||||
The ``BluetoothHCI`` class is responsible for scanning for devices and the lower-level
|
||||
housekeeping for a master-mode Bluetooth application. Most applications will not need
|
||||
to access it directly, but it can be used to discover nearby BT devices. As
|
||||
part of the application Bluetooth setup, call ``BluetoothHCI::install()`` and then
|
||||
``BluetoothHCI::begin()`` to start BT processing. Your application is still responsible
|
||||
for all the non-default HCI initialization and customization. See the ``BluetoothScanner.ino``
|
||||
example for more info.
|
||||
|
||||
|
||||
BluetoothHIDMaster Operation
|
||||
----------------------------
|
||||
|
||||
Most applications will use the ``BluetoothHIDMaster`` class and, after connecting, receive
|
||||
callbacks from the Bluetooth stack when input events (key presses, mouse moves, button
|
||||
mashes) occur.
|
||||
|
||||
Application flow will generally be:
|
||||
1. Install the appropriate callbacks using the ``BluetoothHIDMaster::onXXX`` methods
|
||||
2. Start the Bluetooth processing with ``BluetoothHIDMaster::begin()`` or ``BluetoothHIDMaster::beginBLE()``
|
||||
3. Connect to the first device found with ``BluetoothHIDMaster::connectXXX()`` and start receiving callbacks.
|
||||
4. Callbacks will come at interrupt time and set global state variables, which the main ``loop()`` will process
|
||||
|
||||
Callback Event Handlers
|
||||
-----------------------
|
||||
The main application is informed of new inputs via a standard callback mechanism. These callbacks run at
|
||||
interrupt time and should not do significant work, ``delay()``, or allocate or free memory. The most common
|
||||
way of handling this is setting global ``volatile`` flags and variables that the main ``loop()`` will poll
|
||||
and process.
|
||||
|
||||
Mouse Callbacks
|
||||
~~~~~~~~~~~~~~~
|
||||
The ``BluetoothHIDMaster::onMouseMove`` callback gets the delta X, Y, and wheel reported by the device.
|
||||
The ``BluetoothHIDMaster::onMouseButton`` gets a button number and state (up or down) and will be called
|
||||
each time an individual button is reported changed by the mouse.
|
||||
|
||||
.. code :: cpp
|
||||
|
||||
void mouseMoveCB(void *cbdata, int dx, int dy, int dw) {
|
||||
// Process the deltas, adjust global mouse state
|
||||
}
|
||||
|
||||
void mouseButtonCB(void *cbdata, int butt, bool down) {
|
||||
// Set the global button array with this new info
|
||||
}
|
||||
|
||||
|
||||
Meyboard Callbacks
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
The `BluetoothHIDMaster::onKeyDown`` callback receives the raw HID key (**NOT ASCII**) sent by the device on a key press
|
||||
while `BluetoothHIDMaster::onKeyUp`` gets the same when a key is released. Note that up to 6 keys can be pressed at any
|
||||
one time. For media keys ("consumer keys" in the USB HID documentation) the ``BluetoothHIDMaster::onConsumerKeyDown`` and
|
||||
``BluetoothHIDMaster::onConsumerKeyUp`` perform the same function (and receive the consumer key IDs defined by the
|
||||
USB HID spec and not ASCII).
|
||||
|
||||
To convert the key press and release (including SHIFT handling), use a ``HIDKeyStream`` object. Simply write the raw
|
||||
HID key and the up/down state to the stream and read back the ASCII for use in an application.
|
||||
|
||||
.. code :: cpp
|
||||
|
||||
HIDKeyStream keystream;
|
||||
|
||||
void keyDownCB(void *cbdata, int key) {
|
||||
keystream.write((uint8_t )key);
|
||||
keystream.write((uint8_t) true); // Keystream now has 1 ASCII character to read out and use
|
||||
char ascii = keystream.read();
|
||||
// ....
|
||||
}
|
||||
|
||||
void keyUpCB(void *cbdata, int key) {
|
||||
// Normally don't do anything on this, the character was read in the keyDownCB
|
||||
}
|
||||
|
||||
void consumerKeyDownCB(void *cbdata, int key) {
|
||||
// switch(key) and use cases from the USB Consumer Key page
|
||||
}
|
||||
|
||||
void consumerKeyUpCB(void *cbdata, int key) {
|
||||
// switch(key) and use cases from the USB Consumer Key page
|
||||
}
|
||||
|
||||
|
||||
Joystick Callbacks
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
A single ``BluetoothHIDMaster::onJoystick`` callback gets activated every time a report from a joystick is processed.
|
||||
It receives (potentially, if supported by the device) 4 analog axes, one 8-way digital hat switch position, and up
|
||||
to 32 button states at a time.
|
||||
|
||||
.. code :: cpp
|
||||
|
||||
void joystickCB(void *cbdata, int x, int y, int z, int rz, uint8_t hat, uint32_t buttons) {
|
||||
// HAT 0 = UP and continues clockwise. If no hat direction it is set to 0x0f.
|
||||
// Use "buttons & (1 << buttonNumber)" to look at the individual button states
|
||||
// ...
|
||||
}
|
||||
|
||||
PianoKeyboard Example
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
See the ``PianoKeyboard.ino`` and ``PianoKeyboardBLE.ino`` examples for more information on callback operation.
|
||||
|
||||
|
||||
BluetoothHIDMaster::onXXX Callback Installers
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code :: cpp
|
||||
|
||||
void BluetoothHIDMaster::onMouseMove(void (*)(void *, int, int, int), void *cbData = nullptr);
|
||||
void BluetoothHIDMaster::onMouseButton(void (*)(void *, int, bool), void *cbData = nullptr);
|
||||
void BluetoothHIDMaster::onKeyDown(void (*)(void *, int), void *cbData = nullptr);
|
||||
void BluetoothHIDMaster::onKeyUp(void (*)(void *, int), void *cbData = nullptr);
|
||||
void BluetoothHIDMaster::onConsumerKeyDown(void (*)(void *, int), void *cbData = nullptr);
|
||||
void BluetoothHIDMaster::onConsumerKeyUp(void (*)(void *, int), void *cbData = nullptr);
|
||||
void BluetoothHIDMaster::onJoystick(void (*)(void *, int, int, int, int, uint8_t, uint32_t), void *cbData = nullptr);
|
||||
|
||||
BluetoothHIDMaster Class
|
||||
------------------------
|
||||
|
||||
bool BluetoothHIDMaster::begin()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Installs and configures the Bluetooth Classic stack and starts processing events. No connections are made at this point.
|
||||
When running in Classic mode, no BLE devices can be detected or used.
|
||||
|
||||
|
||||
bool BluetoothHIDMaster::begin(const char *BLEName)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Installs and configures the Bluetooth BLE stack and starts processing events. No connections are made at this point.
|
||||
When running in BLE mode, no Classic devices can be detected or used.
|
||||
|
||||
bool BluetoothHIDMaster::connected()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns if the Bluetooth stack is up and running and a connection to a device is currently active.
|
||||
|
||||
void BluetoothHIDMaster::end()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Disables the Bluetooth stack. Note that with the current Bluetooth implementation restarting the stack (i.e. calling ``begin()`` after ``end()``) is not stable and will not work. Consider storing state and rebooting completely if this is necessary.
|
||||
|
||||
bool BluetoothHIDMaster::running()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns if the Bluetooth stack is running at all. Does not indicate if there is an active connection or not.
|
||||
|
||||
bool BluetoothHIDMaster::hciRunning()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns if the Bluetooth stack has passed the initial HCI start up phase. Until this returns ``true`` no Bluetooth operations can be performed.
|
||||
|
||||
std::vector<BTDeviceInfo> BluetoothHIDMaster::scan(uint32_t mask, int scanTimeSec, bool async)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Passes through the ``BluetoothHCI::scan()`` function to manually scan for a list of nearby devices. If you want to connect to the first found device, this is not needed.
|
||||
|
||||
bool BluetoothHIDMaster::connect(const uint8_t *addr)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Start the connection process to the Bluetooth Classic device with the given MAC. Note that this returns immediately, but it may take several seconds until ``connected()`` reports that the connection has been established.
|
||||
|
||||
bool BluetoothHIDMaster::connectKeyboard(), connectMouse(), connectJoystick(), connectAny()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Connect to the first found specified Bluetooth Classic device type (or any HID device) in pairing mode. No need to call ``scan()`` or have an address.
|
||||
|
||||
bool BluetoothHIDMaster::connectBLE(const uint8_t *addr, int addrType)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Start the connection process to the Bluetooth BLE device with the given MAC. Note that this returns immediately, but it may take several seconds until ``connected()`` reports that the connection has been established.
|
||||
|
||||
bool BluetoothHIDMaster::connectBLE()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Connect to the first found BLE device that has a HID service UUID (keyboard, mouse, or joystick)
|
||||
|
||||
bool BluetoothHIDMaster::disconnect()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Shuts down the connection to the currently connected device.
|
||||
|
||||
void BluetoothHIDMaster::clearPairing()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Erases all Bluetooth keys from memory. This effectively "forgets" all pairing between devices and can help avoid issues with the beta Bluetooth stack in the SDK.
|
||||
+1
-1
@@ -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,
|
||||
|
||||
+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()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user