Compare commits
@@ -14,21 +14,21 @@ jobs:
|
||||
name: Check spelling
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- name: Run codespell
|
||||
uses: codespell-project/actions-codespell@master
|
||||
with:
|
||||
skip: ./pico-extras,./ArduinoCore-API,./libraries/SdFat,./libraries/Adafruit_TinyUSB_Arduino,./libraries/LittleFS/lib,./tools/pyserial,./pico-sdk,./.github,./docs/i2s.rst,./cores/rp2040/api
|
||||
ignore_words_list: ser,DOUT
|
||||
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
|
||||
ignore_words_list: ser,dout
|
||||
|
||||
# Consistent style
|
||||
astyle:
|
||||
name: Style, Boards, Package
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: false
|
||||
- name: Check package references
|
||||
@@ -36,7 +36,9 @@ jobs:
|
||||
./tests/ci/pkgrefs_test.sh
|
||||
- name: Check boards.txt was not edited after makeboards.py
|
||||
run: |
|
||||
./tools/makeboards.py > boards.txt
|
||||
./tools/makeboards.py
|
||||
# If anything changed, GIT should return an error and fail the test
|
||||
git diff --exit-code
|
||||
- name: Run astyle on all code/examples
|
||||
run: |
|
||||
sudo apt update
|
||||
@@ -44,10 +46,10 @@ 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
|
||||
# - 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:
|
||||
@@ -57,7 +59,7 @@ jobs:
|
||||
matrix:
|
||||
chunk: [0, 1, 2, 3]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v2
|
||||
@@ -79,8 +81,6 @@ jobs:
|
||||
run: |
|
||||
cd pico-sdk
|
||||
git submodule update --init
|
||||
cd ../pico-extras
|
||||
git submodule update --init
|
||||
cd ..
|
||||
bash ./tests/build.sh
|
||||
|
||||
@@ -89,7 +89,7 @@ jobs:
|
||||
name: Build TinyUSB Examples
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v2
|
||||
@@ -109,8 +109,6 @@ jobs:
|
||||
run: |
|
||||
cd pico-sdk
|
||||
git submodule update --init
|
||||
cd ../pico-extras
|
||||
git submodule update --init
|
||||
cd ..
|
||||
bash ./tests/build-tinyusb.sh
|
||||
|
||||
@@ -119,7 +117,7 @@ jobs:
|
||||
name: Windows
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v2
|
||||
@@ -145,8 +143,6 @@ jobs:
|
||||
try { Get-Command python3 } catch { copy (get-command python).source (get-command python).source.Replace("python.exe", "python3.exe") }
|
||||
cd pico-sdk
|
||||
git submodule update --init
|
||||
cd ../pico-extras
|
||||
git submodule update --init
|
||||
cd ..
|
||||
bash ./tests/build.sh
|
||||
|
||||
@@ -156,7 +152,7 @@ jobs:
|
||||
name: Mac
|
||||
runs-on: macOS-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v2
|
||||
@@ -179,8 +175,40 @@ jobs:
|
||||
run: |
|
||||
cd pico-sdk
|
||||
git submodule update --init
|
||||
cd ../pico-extras
|
||||
git submodule update --init
|
||||
cd ..
|
||||
bash ./tests/build.sh
|
||||
|
||||
# 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
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- name: Cache pip
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
- name: Cache PlatformIO
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
- 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 install --global --tool symlink://.
|
||||
- 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
|
||||
- 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
|
||||
run: pio ci --board=rpipico --board=adafruit_feather -O "platform_packages=framework-arduinopico@symlink:///home/runner/work/arduino-pico/arduino-pico" -O "build_flags=-DUSE_TINYUSB" libraries/Adafruit_TinyUSB_Arduino/examples/CDC/cdc_multi/cdc_multi.ino
|
||||
@@ -12,9 +12,25 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- name: Cache pip
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
- name: Cache PlatformIO
|
||||
uses: actions/cache@v2
|
||||
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
|
||||
- name: Deploy updated JSON
|
||||
env:
|
||||
TRAVIS_BUILD_DIR: ${{ github.workspace }}
|
||||
@@ -25,3 +41,6 @@ jobs:
|
||||
TAG=$(git describe --exact-match --tags)
|
||||
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
|
||||
|
||||
@@ -28,6 +28,7 @@ jobs:
|
||||
BUILD_TYPE: package
|
||||
CI_GITHUB_API_KEY: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
(cd pico-sdk && git submodule update --init)
|
||||
(cd ./package && bash ./build_boards_manager_package.sh)
|
||||
pip3 install PyGithub
|
||||
# Create a draft release and upload the ZIP and JSON files.
|
||||
|
||||
+4
-4
@@ -11,11 +11,8 @@
|
||||
path = libraries/LittleFS/lib/littlefs
|
||||
url = https://github.com/littlefs-project/littlefs.git
|
||||
[submodule "libraries/SdFat"]
|
||||
path = libraries/SdFat
|
||||
path = libraries/ESP8266SdFat
|
||||
url = https://github.com/earlephilhower/ESP8266SdFat.git
|
||||
[submodule "pico-extras"]
|
||||
path = pico-extras
|
||||
url = https://github.com/raspberrypi/pico-extras.git
|
||||
[submodule "libraries/Keyboard"]
|
||||
path = libraries/Keyboard
|
||||
url = https://github.com/earlephilhower/Keyboard
|
||||
@@ -25,3 +22,6 @@
|
||||
[submodule "libraries/Adafruit_TinyUSB_Arduino"]
|
||||
path = libraries/Adafruit_TinyUSB_Arduino
|
||||
url = https://github.com/adafruit/Adafruit_TinyUSB_Arduino.git
|
||||
[submodule "libraries/FreeRTOS/lib/FreeRTOS-Kernel"]
|
||||
path = libraries/FreeRTOS/lib/FreeRTOS-Kernel
|
||||
url = https://github.com/earlephilhower/FreeRTOS-Kernel.git
|
||||
|
||||
+1
-1
Submodule ArduinoCore-API updated: 9da5373517...ff01bb620e
@@ -1,8 +1,10 @@
|
||||
# Arduino-Pico [](https://gitter.im/arduino-pico/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
# Arduino-Pico
|
||||
[](https://github.com/earlephilhower/arduino-pico/releases)
|
||||
[](https://gitter.im/arduino-pico/community)
|
||||
|
||||
Raspberry Pi Pico Arduino core, for all RP2040 boards
|
||||
|
||||
This is a port of the RP2040 (Raspberry Pi Pico processor) to the Arduino ecosystem. It uses the bare Raspberry Pi Pico SDK and a custom GCC 10.2/Newlib 4.0 toolchain.
|
||||
This is a port of the RP2040 (Raspberry Pi Pico processor) to the Arduino ecosystem. It uses the bare Raspberry Pi Pico SDK and a custom GCC 10.3/Newlib 4.0 toolchain.
|
||||
|
||||
# Documentation
|
||||
See https://arduino-pico.readthedocs.io/en/latest/ along with the examples for more detailed usage information.
|
||||
@@ -11,9 +13,31 @@ See https://arduino-pico.readthedocs.io/en/latest/ along with the examples for m
|
||||
* Raspberry Pi Pico
|
||||
* Adafruit Feather RP2040
|
||||
* Adafruit ItsyBitsy RP2040
|
||||
* Adafruit KB2040
|
||||
* Adafruit Macropad RP2040
|
||||
* Adafruit QTPy RP2040
|
||||
* Adafruit STEMMA Friend RP2040
|
||||
* Adafruit Trinkey RP2040 QT
|
||||
* Arduino Nano RP2040 Connect
|
||||
* Cytron Maker Pi RP2040
|
||||
* Cytron Maker Nano RP2040
|
||||
* DeRuiLab FlyBoard2040 Core
|
||||
* DFRobot Beetle RP2040
|
||||
* Invector Labs Challenger RP2040 WiFi
|
||||
* Invector Labs Challenger RP2040 WiFi/BLE
|
||||
* Invector Labs Challenger NB RP2040 WiFi
|
||||
* Invector Labs Challenger RP2040 LTE
|
||||
* Invector Labs Challenger RP2040 LoRa
|
||||
* Invector Labs RPICO32
|
||||
* Melopero Shake RP2040
|
||||
* Seeed XIAO RP2040
|
||||
* Solder Party RP2040 Stamp
|
||||
* SparkFun ProMicro RP2040
|
||||
* SparkFun Thing Plus RP2040
|
||||
* uPesy RP2040 DevKit
|
||||
* WIZnet W5100S-EVB-Pico
|
||||
* WIZnet W5500-EVB-Pico
|
||||
* WIZnet WizFi360-EVB-Pico
|
||||
* Generic (configurable flash, I/O pins)
|
||||
|
||||
# Installing via Arduino Boards Manager
|
||||
@@ -49,8 +73,6 @@ cd ~/Arduino/hardware/pico/rp2040
|
||||
git submodule update --init
|
||||
cd pico-sdk
|
||||
git submodule update --init
|
||||
cd ../pico-extras
|
||||
git submodule update --init
|
||||
cd ../tools
|
||||
python3 ./get.py
|
||||
`````
|
||||
@@ -64,7 +86,7 @@ If you follow Les' step-by-step you will also have a fully functional `CMake`-ba
|
||||
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.
|
||||
|
||||
After the first upload, this should not be necessary as the `arduino-pico` core has auto-reset support.
|
||||
After the first upload, this should not be necessary as the `arduino-pico` core has auto-reset support.
|
||||
Select the appropriate serial port shown in the Arduino Tools->Port->Serial Port menu once (this setting will stick and does not need to be
|
||||
touched for multiple uploads). This selection allows the auto-reset tool to identify the proper device to reset.
|
||||
Them hit the upload button and your sketch should upload and run.
|
||||
@@ -76,8 +98,8 @@ follow the initial procedure of holding the BOOTSEL button down while plugging i
|
||||
The onboard flash filesystem for the Pico, LittleFS, lets you upload a filesystem image from the sketch directory for your sketch to use. Download the needed plugin from
|
||||
* https://github.com/earlephilhower/arduino-pico-littlefs-plugin/releases
|
||||
|
||||
To install, follow the directions in
|
||||
* https://github.com/earlephilhower/arduino-pico-littlefs-plugin/blob/master/README.md
|
||||
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
|
||||
@@ -97,8 +119,25 @@ The first line creates a file with the USB vendor and ID of the Picoprobe and te
|
||||
|
||||
Once Picoprobe permissions are set up properly, then select the board "Raspberry Pi Pico (Picoprobe)" in the Tools menu and upload as normal.
|
||||
|
||||
# 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.
|
||||
# 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)
|
||||
@@ -107,13 +146,15 @@ The installed tools include a version of OpenOCD (in the pqt-openocd directory)
|
||||
* Multicore support (setup1() and loop1())
|
||||
* 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 output, Servo
|
||||
* printf (i.e. debug) output over USB serial
|
||||
* 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`
|
||||
@@ -129,11 +170,12 @@ If you want to contribute or have bugfixes, drop me a note at <earlephilhower@ya
|
||||
# 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.
|
||||
* The [RP2040 GCC-based toolchain](https://github.com/earlephilhower/pico-quick-toolchain) is licensed under under the GPL.
|
||||
* The [Pico-SDK](https://github.com/raspberrypi/pico-sdk) and [Pico-Extras](https://github.com/raspberrypi/pico-extras) are by Raspberry Pi (Trading) Ltd and licensed under the BSD 3-Clause license.
|
||||
* The [Pico-SDK](https://github.com/raspberrypi/pico-sdk) is by Raspberry Pi (Trading) Ltd and licensed under the BSD 3-Clause license.
|
||||
* [Arduino-Pico](https://github.com/earlephilhower/arduino-pico) core files are licensed under the LGPL.
|
||||
* [LittleFS](https://github.com/ARMmbed/littlefs) library written by ARM Limited and released under the [BSD 3-clause license](https://github.com/ARMmbed/littlefs/blob/master/LICENSE.md).
|
||||
* [UF2CONV.PY](https://github.com/microsoft/uf2) is by Microsoft Corporation and licensed under the MIT license.
|
||||
* Some filesystem code taken from the [ESP8266 Arduino Core](https://github.com/esp8266/Arduino) and licensed under the LGPL.
|
||||
* [FreeRTOS](https://freertos.org) is Copyright Amazon.com, Inc. or its affiliates, and distributed under the MIT license.
|
||||
|
||||
-Earle F. Philhower, III
|
||||
earlephilhower@yahoo.com
|
||||
|
||||
-315
@@ -1,315 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include "hardware/regs/m0plus.h"
|
||||
#include "hardware/platform_defs.h"
|
||||
#include "hardware/regs/addressmap.h"
|
||||
#include "hardware/regs/sio.h"
|
||||
#include "pico/binary_info/defs.h"
|
||||
|
||||
#ifdef NDEBUG
|
||||
#ifndef COLLAPSE_IRQS
|
||||
#define COLLAPSE_IRQS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
.syntax unified
|
||||
.cpu cortex-m0plus
|
||||
.thumb
|
||||
|
||||
.section .vectors, "ax"
|
||||
.align 2
|
||||
|
||||
.global __vectors
|
||||
__vectors:
|
||||
.word __StackTop
|
||||
.word _reset_handler
|
||||
.word isr_nmi
|
||||
.word isr_hardfault
|
||||
.word isr_invalid // Reserved, should never fire
|
||||
.word isr_invalid // Reserved, should never fire
|
||||
.word isr_invalid // Reserved, should never fire
|
||||
.word isr_invalid // Reserved, should never fire
|
||||
.word isr_invalid // Reserved, should never fire
|
||||
.word isr_invalid // Reserved, should never fire
|
||||
.word isr_invalid // Reserved, should never fire
|
||||
.word isr_svcall
|
||||
.word isr_invalid // Reserved, should never fire
|
||||
.word isr_invalid // Reserved, should never fire
|
||||
.word isr_pendsv
|
||||
.word isr_systick
|
||||
.word isr_irq0
|
||||
.word isr_irq1
|
||||
.word isr_irq2
|
||||
.word isr_irq3
|
||||
.word isr_irq4
|
||||
.word isr_irq5
|
||||
.word isr_irq6
|
||||
.word isr_irq7
|
||||
.word isr_irq8
|
||||
.word isr_irq9
|
||||
.word isr_irq10
|
||||
.word isr_irq11
|
||||
.word isr_irq12
|
||||
.word isr_irq13
|
||||
.word isr_irq14
|
||||
.word isr_irq15
|
||||
.word isr_irq16
|
||||
.word isr_irq17
|
||||
.word isr_irq18
|
||||
.word isr_irq19
|
||||
.word isr_irq20
|
||||
.word isr_irq21
|
||||
.word isr_irq22
|
||||
.word isr_irq23
|
||||
.word isr_irq24
|
||||
.word isr_irq25
|
||||
.word isr_irq26
|
||||
.word isr_irq27
|
||||
.word isr_irq28
|
||||
.word isr_irq29
|
||||
.word isr_irq30
|
||||
.word isr_irq31
|
||||
|
||||
// Declare a weak symbol for each ISR.
|
||||
// By default, they will fall through to the undefined IRQ handler below (breakpoint),
|
||||
// but can be overridden by C functions with correct name.
|
||||
|
||||
.macro decl_isr_bkpt name
|
||||
.weak \name
|
||||
.type \name,%function
|
||||
.thumb_func
|
||||
\name:
|
||||
bkpt #0
|
||||
.endm
|
||||
|
||||
// these are separated out for clarity
|
||||
decl_isr_bkpt isr_invalid
|
||||
decl_isr_bkpt isr_nmi
|
||||
decl_isr_bkpt isr_hardfault
|
||||
decl_isr_bkpt isr_svcall
|
||||
decl_isr_bkpt isr_pendsv
|
||||
decl_isr_bkpt isr_systick
|
||||
|
||||
.macro decl_isr name
|
||||
.weak \name
|
||||
.type \name,%function
|
||||
.thumb_func
|
||||
\name:
|
||||
.endm
|
||||
|
||||
decl_isr isr_irq0
|
||||
decl_isr isr_irq1
|
||||
decl_isr isr_irq2
|
||||
decl_isr isr_irq3
|
||||
decl_isr isr_irq4
|
||||
decl_isr isr_irq5
|
||||
decl_isr isr_irq6
|
||||
decl_isr isr_irq7
|
||||
decl_isr isr_irq8
|
||||
decl_isr isr_irq9
|
||||
decl_isr isr_irq10
|
||||
decl_isr isr_irq11
|
||||
decl_isr isr_irq12
|
||||
decl_isr isr_irq13
|
||||
decl_isr isr_irq14
|
||||
decl_isr isr_irq15
|
||||
decl_isr isr_irq16
|
||||
decl_isr isr_irq17
|
||||
decl_isr isr_irq18
|
||||
decl_isr isr_irq19
|
||||
decl_isr isr_irq20
|
||||
decl_isr isr_irq21
|
||||
decl_isr isr_irq22
|
||||
decl_isr isr_irq23
|
||||
decl_isr isr_irq24
|
||||
decl_isr isr_irq25
|
||||
decl_isr isr_irq26
|
||||
decl_isr isr_irq27
|
||||
decl_isr isr_irq28
|
||||
decl_isr isr_irq29
|
||||
decl_isr isr_irq30
|
||||
decl_isr isr_irq31
|
||||
|
||||
// All unhandled USER IRQs fall through to here
|
||||
.global __unhandled_user_irq
|
||||
.thumb_func
|
||||
__unhandled_user_irq:
|
||||
bl __get_current_exception
|
||||
subs r0, #16
|
||||
.global unhandled_user_irq_num_in_r0
|
||||
unhandled_user_irq_num_in_r0:
|
||||
bkpt #0
|
||||
|
||||
.section .reset, "ax"
|
||||
|
||||
// This is the beginning of the image, which is entered from stage2 or bootrom USB MSD watchdog reboot
|
||||
|
||||
// note if we are NO_FLASH then start: below is currently identical anyway, so save 4 bytes
|
||||
#if !PICO_NO_FLASH
|
||||
// We simply install our own vector table and redirect through it
|
||||
ldr r0, =__vectors
|
||||
b __vector_entry
|
||||
#endif
|
||||
|
||||
// ELF entry point generally called when we load an ELF via debugger
|
||||
.type _entry_point,%function
|
||||
.thumb_func
|
||||
.global _entry_point
|
||||
_entry_point:
|
||||
|
||||
#if PICO_NO_FLASH
|
||||
// non flash
|
||||
ldr r0, =__vectors
|
||||
#else
|
||||
// todo clear watchdog?
|
||||
// When using flash, we install and use the ROM vector table to go thru regular bootrom/stage2 flash sequence
|
||||
movs r0, #0
|
||||
#endif
|
||||
|
||||
__vector_entry:
|
||||
ldr r1, =(PPB_BASE + M0PLUS_CPUID_OFFSET)
|
||||
str r0, [r1, #8]
|
||||
ldmia r0!, {r1, r2}
|
||||
msr msp, r1
|
||||
bx r2
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Reset handler:
|
||||
// - initialises .data
|
||||
// - clears .bss
|
||||
// - calls runtime_init
|
||||
// - calls main
|
||||
// - calls exit (which should eventually hang the processor via _exit)
|
||||
|
||||
.type _reset_handler,%function
|
||||
.thumb_func
|
||||
_reset_handler:
|
||||
// Hang all cores except core 0
|
||||
ldr r0, =(SIO_BASE + SIO_CPUID_OFFSET)
|
||||
ldr r0, [r0]
|
||||
cmp r0, #0
|
||||
bne wait_for_vector
|
||||
|
||||
adr r4, data_cpy_table
|
||||
|
||||
// assume there is at least one entry
|
||||
1:
|
||||
ldmia r4!, {r1-r3}
|
||||
cmp r1, #0
|
||||
beq 2f
|
||||
bl data_cpy
|
||||
b 1b
|
||||
2:
|
||||
|
||||
// Zero out the BSS
|
||||
ldr r1, =__bss_start__
|
||||
ldr r2, =__bss_end__
|
||||
movs r0, #0
|
||||
b bss_fill_test
|
||||
bss_fill_loop:
|
||||
stm r1!, {r0}
|
||||
bss_fill_test:
|
||||
cmp r1, r2
|
||||
bne bss_fill_loop
|
||||
|
||||
platform_entry: // symbol for stack traces
|
||||
// Use 32-bit jumps, in case these symbols are moved out of branch range
|
||||
// (e.g. if main is in SRAM and crt0 in flash)
|
||||
ldr r1, =runtime_init
|
||||
blx r1
|
||||
ldr r1, =main
|
||||
blx r1
|
||||
ldr r1, =exit
|
||||
blx r1
|
||||
// exit should not return. If it does, hang the core.
|
||||
// (fall thru into our hang _exit impl
|
||||
.weak _exit
|
||||
.type _exit,%function
|
||||
.thumb_func
|
||||
_exit:
|
||||
1: // separate label because _exit can be moved out of branch range
|
||||
bkpt #0
|
||||
b 1b
|
||||
|
||||
data_cpy_loop:
|
||||
ldm r1!, {r0}
|
||||
stm r2!, {r0}
|
||||
data_cpy:
|
||||
cmp r2, r3
|
||||
blo data_cpy_loop
|
||||
bx lr
|
||||
|
||||
#if !PICO_NO_BINARY_INFO
|
||||
binary_info_header:
|
||||
.word BINARY_INFO_MARKER_START
|
||||
.word __binary_info_start
|
||||
.word __binary_info_end
|
||||
.word data_cpy_table // we may need to decode pointers that are in RAM at runtime.
|
||||
.word BINARY_INFO_MARKER_END
|
||||
#endif
|
||||
|
||||
.align 2
|
||||
data_cpy_table:
|
||||
#if PICO_COPY_TO_RAM
|
||||
.word __ram_text_source__
|
||||
.word __ram_text_start__
|
||||
.word __ram_text_end__
|
||||
#endif
|
||||
.word __etext
|
||||
.word __data_start__
|
||||
.word __data_end__
|
||||
|
||||
.word __scratch_x_source__
|
||||
.word __scratch_x_start__
|
||||
.word __scratch_x_end__
|
||||
|
||||
.word __scratch_y_source__
|
||||
.word __scratch_y_start__
|
||||
.word __scratch_y_end__
|
||||
|
||||
.word 0 // null terminator
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Provide safe defaults for _exit and runtime_init
|
||||
// Full implementations usually provided by platform.c
|
||||
|
||||
.weak runtime_init
|
||||
.type runtime_init,%function
|
||||
.thumb_func
|
||||
runtime_init:
|
||||
bx lr
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// In case core 1's VTOR has already been moved into flash, we need to handle
|
||||
// core 1 reset. However, we do so by just jumping back into bootrom version of
|
||||
// wait_for_vector
|
||||
|
||||
wait_for_vector:
|
||||
ldr r0, = 'W' | ('V' << 8)
|
||||
bl rom_func_lookup
|
||||
bx r0
|
||||
|
||||
.global __get_current_exception
|
||||
.thumb_func
|
||||
__get_current_exception:
|
||||
mrs r0, ipsr
|
||||
uxtb r0, r0
|
||||
bx lr
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Stack/heap dummies to set size
|
||||
|
||||
.section .stack
|
||||
// align to allow for memory protection (although this alignment is pretty much ignored by linker script)
|
||||
.align 5
|
||||
.equ StackSize, PICO_STACK_SIZE
|
||||
.space StackSize
|
||||
|
||||
.section .heap
|
||||
.align 2
|
||||
.equ HeapSize, PICO_HEAP_SIZE
|
||||
.space HeapSize
|
||||
+13001
-100
File diff suppressed because it is too large
Load Diff
+15
-33
@@ -24,17 +24,14 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
// Wacky deprecated AVR compatibility functions
|
||||
#include "stdlib_noniso.h"
|
||||
|
||||
#include "stdlib_noniso.h" // Wacky deprecated AVR compatibility functions
|
||||
#include "RP2040Version.h"
|
||||
#include "api/ArduinoAPI.h"
|
||||
#include "api/itoa.h" // ARM toolchain doesn't provide itoa etc, provide them
|
||||
#include <pins_arduino.h>
|
||||
|
||||
|
||||
// Required for the port*Register macros
|
||||
#include "hardware/gpio.h"
|
||||
|
||||
#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()
|
||||
@@ -44,8 +41,10 @@
|
||||
#endif // abs
|
||||
#ifdef __cplusplus
|
||||
using std::abs;
|
||||
using std::round;
|
||||
#else
|
||||
#define abs(x) ((x)>0?(x):-(x))
|
||||
#define abs(x) ({ __typeof__(x) _x = (x); _x >= 0 ? _x : -_x; })
|
||||
#define round(x) ({ __typeof__(x) _x = (x); _x >= 0 ? (long)(_x + 0.5) : (long)(_x - 0.5); })
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -61,10 +60,6 @@ extern "C" {
|
||||
void interrupts();
|
||||
void noInterrupts();
|
||||
|
||||
// GPIO change/value interrupts
|
||||
void attachInterrupt(pin_size_t pin, voidFuncPtr callback, PinStatus mode);
|
||||
void detachInterrupt(pin_size_t pin);
|
||||
|
||||
// AVR compatibility macros...naughty and accesses the HW directly
|
||||
#define digitalPinToPort(pin) (0)
|
||||
#define digitalPinToBitMask(pin) (1UL << (pin))
|
||||
@@ -75,27 +70,17 @@ void detachInterrupt(pin_size_t pin);
|
||||
#define portInputRegister(port) ((volatile uint32_t*) sio_hw->gpio_in)
|
||||
#define portModeRegister(port) ((volatile uint32_t*) sio_hw->gpio_oe)
|
||||
|
||||
// IO config
|
||||
void pinMode(pin_size_t pinNumber, PinMode pinMode);
|
||||
|
||||
// SIO (GPIO)
|
||||
void digitalWrite(pin_size_t pinNumber, PinStatus status);
|
||||
PinStatus digitalRead(pin_size_t pinNumber);
|
||||
|
||||
// ADC
|
||||
int analogRead(pin_size_t pinNumber);
|
||||
// ADC RP2040-specific calls
|
||||
void analogReadResolution(int bits);
|
||||
float analogReadTemp(); // Returns core temp in Centigrade
|
||||
|
||||
// PWM
|
||||
void analogWrite(pin_size_t pinNumber, int value);
|
||||
// PWM RP2040-specific calls
|
||||
void analogWriteFreq(uint32_t freq);
|
||||
void analogWriteRange(uint32_t range);
|
||||
void analogWriteResolution(int res);
|
||||
|
||||
// Timing
|
||||
void delay(unsigned long);
|
||||
void delayMicroseconds(unsigned int us);
|
||||
unsigned long millis();
|
||||
// FreeRTOS potential calls
|
||||
extern bool __isFreeRTOS;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
@@ -116,7 +101,8 @@ unsigned long millis();
|
||||
#endif
|
||||
|
||||
#include "SerialUART.h"
|
||||
#include "RP2040.h"
|
||||
#include "RP2040Support.h"
|
||||
#include "SerialPIO.h"
|
||||
#include "Bootsel.h"
|
||||
|
||||
// Template which will evaluate at *compile time* to a single 32b number
|
||||
@@ -127,8 +113,4 @@ constexpr uint32_t __bitset(const int (&a)[N], size_t i = 0U) {
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// ARM toolchain doesn't provide itoa etc, provide them
|
||||
#include "api/itoa.h"
|
||||
|
||||
#endif // Arduino_h
|
||||
|
||||
@@ -47,8 +47,8 @@ static bool __no_inline_not_in_flash_func(get_bootsel_button)() {
|
||||
GPIO_OVERRIDE_NORMAL << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB,
|
||||
IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS);
|
||||
|
||||
interrupts();
|
||||
rp2040.resumeOtherCore();
|
||||
interrupts();
|
||||
|
||||
return button_state;
|
||||
}
|
||||
|
||||
@@ -188,15 +188,6 @@ public:
|
||||
bool _autoFormat;
|
||||
};
|
||||
|
||||
class SPIFFSConfig : public FSConfig {
|
||||
public:
|
||||
static constexpr uint32_t FSId = 0x53504946;
|
||||
SPIFFSConfig(bool autoFormat = true) : FSConfig(FSId, autoFormat) { }
|
||||
|
||||
// Inherit _type and _autoFormat
|
||||
// nothing yet, enableTime TBD when SPIFFS has metadate
|
||||
};
|
||||
|
||||
class FS {
|
||||
public:
|
||||
FS(FSImplPtr impl) : _impl(impl) {
|
||||
@@ -272,11 +263,6 @@ using fs::SeekCur;
|
||||
using fs::SeekEnd;
|
||||
using fs::FSInfo;
|
||||
using fs::FSConfig;
|
||||
using fs::SPIFFSConfig;
|
||||
#endif //FS_NO_GLOBALS
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SPIFFS)
|
||||
extern fs::FS SPIFFS __attribute__((deprecated("SPIFFS has been deprecated. Please consider moving to LittleFS or other filesystems.")));
|
||||
#endif
|
||||
|
||||
#endif //FS_H
|
||||
|
||||
@@ -21,9 +21,15 @@
|
||||
#include <hardware/clocks.h>
|
||||
#include <hardware/irq.h>
|
||||
#include <hardware/pio.h>
|
||||
#include <hardware/exception.h>
|
||||
#include <hardware/structs/systick.h>
|
||||
#include <pico/multicore.h>
|
||||
#include <pico/util/queue.h>
|
||||
#include <CoreMutex.h>
|
||||
#include "CoreMutex.h"
|
||||
#include "ccount.pio.h"
|
||||
|
||||
|
||||
extern "C" volatile bool __otherCoreIdled;
|
||||
|
||||
class _MFIFO {
|
||||
public:
|
||||
@@ -43,9 +49,12 @@ public:
|
||||
}
|
||||
|
||||
void registerCore() {
|
||||
multicore_fifo_clear_irq();
|
||||
irq_set_exclusive_handler(SIO_IRQ_PROC0 + get_core_num(), _irq);
|
||||
irq_set_enabled(SIO_IRQ_PROC0 + get_core_num(), true);
|
||||
if (!__isFreeRTOS) {
|
||||
multicore_fifo_clear_irq();
|
||||
irq_set_exclusive_handler(SIO_IRQ_PROC0 + get_core_num(), _irq);
|
||||
irq_set_enabled(SIO_IRQ_PROC0 + get_core_num(), true);
|
||||
}
|
||||
// FreeRTOS port.c will handle the IRQ hooking
|
||||
}
|
||||
|
||||
void push(uint32_t val) {
|
||||
@@ -77,9 +86,9 @@ public:
|
||||
return;
|
||||
}
|
||||
mutex_enter_blocking(&_idleMutex);
|
||||
_otherIdled = false;
|
||||
__otherCoreIdled = false;
|
||||
multicore_fifo_push_blocking(_GOTOSLEEP);
|
||||
while (!_otherIdled) { /* noop */ }
|
||||
while (!__otherCoreIdled) { /* noop */ }
|
||||
}
|
||||
|
||||
void resumeOtherCore() {
|
||||
@@ -87,58 +96,50 @@ public:
|
||||
return;
|
||||
}
|
||||
mutex_exit(&_idleMutex);
|
||||
_otherIdled = false;
|
||||
__otherCoreIdled = false;
|
||||
// Other core will exit busy-loop and return to operation
|
||||
// once otherIdled == false.
|
||||
// once __otherCoreIdled == false.
|
||||
}
|
||||
|
||||
void clear() {
|
||||
uint32_t val;
|
||||
|
||||
while (queue_try_remove(&_queue[0], &val)) {
|
||||
tight_loop_contents();
|
||||
}
|
||||
|
||||
while (queue_try_remove(&_queue[1], &val)) {
|
||||
tight_loop_contents();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static void __no_inline_not_in_flash_func(_irq)() {
|
||||
multicore_fifo_clear_irq();
|
||||
noInterrupts(); // We need total control, can't run anything
|
||||
while (multicore_fifo_rvalid()) {
|
||||
if (_GOTOSLEEP == multicore_fifo_pop_blocking()) {
|
||||
_otherIdled = true;
|
||||
while (_otherIdled) { /* noop */ }
|
||||
break;
|
||||
if (!__isFreeRTOS) {
|
||||
multicore_fifo_clear_irq();
|
||||
noInterrupts(); // We need total control, can't run anything
|
||||
while (multicore_fifo_rvalid()) {
|
||||
if (_GOTOSLEEP == multicore_fifo_pop_blocking()) {
|
||||
__otherCoreIdled = true;
|
||||
while (__otherCoreIdled) { /* noop */ }
|
||||
break;
|
||||
}
|
||||
}
|
||||
interrupts();
|
||||
}
|
||||
interrupts();
|
||||
}
|
||||
|
||||
bool _multicore = false;
|
||||
|
||||
mutex_t _idleMutex;
|
||||
static volatile bool _otherIdled;
|
||||
queue_t _queue[2];
|
||||
|
||||
static constexpr int _GOTOSLEEP = 0x66666666;
|
||||
static constexpr uint32_t _GOTOSLEEP = 0xC0DED02E;
|
||||
};
|
||||
|
||||
class RP2040 {
|
||||
public:
|
||||
// Convert from microseconds to PIO clock cycles
|
||||
static int usToPIOCycles(int us) {
|
||||
// Parenthesis needed to guarantee order of operations to avoid 32bit overflow
|
||||
return (us * (clock_get_hz(clk_sys) / 1000000));
|
||||
}
|
||||
|
||||
// Get current clock frequency
|
||||
static int f_cpu() {
|
||||
return clock_get_hz(clk_sys);
|
||||
}
|
||||
|
||||
void idleOtherCore() {
|
||||
fifo.idleOtherCore();
|
||||
}
|
||||
void resumeOtherCore() {
|
||||
fifo.resumeOtherCore();
|
||||
}
|
||||
|
||||
// Multicore comms FIFO
|
||||
_MFIFO fifo;
|
||||
};
|
||||
|
||||
class RP2040;
|
||||
extern RP2040 rp2040;
|
||||
extern "C" void main1();
|
||||
class PIOProgram;
|
||||
|
||||
// Wrapper class for PIO programs, abstracting common operations out
|
||||
// TODO - Add unload/destructor
|
||||
@@ -194,3 +195,90 @@ private:
|
||||
const pio_program_t *_pgm;
|
||||
};
|
||||
|
||||
class RP2040 {
|
||||
public:
|
||||
RP2040() { /* noop */ }
|
||||
~RP2040() { /* noop */ }
|
||||
|
||||
void begin() {
|
||||
_epoch = 0;
|
||||
if (!__isFreeRTOS) {
|
||||
// Enable SYSTICK exception
|
||||
exception_set_exclusive_handler(SYSTICK_EXCEPTION, _SystickHandler);
|
||||
systick_hw->csr = 0x7;
|
||||
systick_hw->rvr = 0x00FFFFFF;
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert from microseconds to PIO clock cycles
|
||||
static int usToPIOCycles(int us) {
|
||||
// Parenthesis needed to guarantee order of operations to avoid 32bit overflow
|
||||
return (us * (clock_get_hz(clk_sys) / 1000000));
|
||||
}
|
||||
|
||||
// Get current clock frequency
|
||||
static int f_cpu() {
|
||||
return clock_get_hz(clk_sys);
|
||||
}
|
||||
|
||||
// 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 (!__isFreeRTOS) {
|
||||
uint32_t epoch;
|
||||
uint32_t ctr;
|
||||
do {
|
||||
epoch = (uint32_t)_epoch;
|
||||
ctr = systick_hw->cvr;
|
||||
} while (epoch != (uint32_t)_epoch);
|
||||
return epoch + (1 << 24) - ctr; /* CTR counts down from 1<<24-1 */
|
||||
} else {
|
||||
return ccount_read(_pio, _sm);
|
||||
}
|
||||
}
|
||||
|
||||
inline uint64_t getCycleCount64() {
|
||||
if (!__isFreeRTOS) {
|
||||
uint64_t epoch;
|
||||
uint64_t ctr;
|
||||
do {
|
||||
epoch = _epoch;
|
||||
ctr = systick_hw->cvr;
|
||||
} while (epoch != _epoch);
|
||||
return epoch + (1LL << 24) - ctr;
|
||||
} else {
|
||||
return ccount_read(_pio, _sm);
|
||||
}
|
||||
}
|
||||
|
||||
void idleOtherCore() {
|
||||
fifo.idleOtherCore();
|
||||
}
|
||||
|
||||
void resumeOtherCore() {
|
||||
fifo.resumeOtherCore();
|
||||
}
|
||||
|
||||
void restartCore1() {
|
||||
multicore_reset_core1();
|
||||
fifo.clear();
|
||||
multicore_launch_core1(main1);
|
||||
}
|
||||
|
||||
// Multicore comms FIFO
|
||||
_MFIFO fifo;
|
||||
|
||||
private:
|
||||
static void _SystickHandler() {
|
||||
rp2040._epoch += 1LL << 24;
|
||||
}
|
||||
PIO _pio;
|
||||
int _sm;
|
||||
PIOProgram *_ccountPgm;
|
||||
};
|
||||
+69
-51
@@ -19,7 +19,7 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef USE_TINYUSB
|
||||
#if !defined(USE_TINYUSB) && !defined(NO_USB)
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "CoreMutex.h"
|
||||
@@ -52,7 +52,6 @@ mutex_t __usb_mutex;
|
||||
#endif
|
||||
|
||||
#define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN)
|
||||
#define USBD_MAX_POWER_MA (250)
|
||||
|
||||
#define USBD_ITF_CDC (0) // needs 2 interfaces
|
||||
#define USBD_ITF_MAX (2)
|
||||
@@ -121,53 +120,72 @@ int __USBGetMouseReportID() {
|
||||
return __USBInstallKeyboard ? 2 : 1;
|
||||
}
|
||||
|
||||
static int __hid_report_len = 0;
|
||||
static uint8_t *__hid_report = nullptr;
|
||||
|
||||
static uint8_t *GetDescHIDReport(int *len) {
|
||||
if (len) {
|
||||
*len = __hid_report_len;
|
||||
}
|
||||
return __hid_report;
|
||||
}
|
||||
|
||||
void __SetupDescHIDReport() {
|
||||
if (__USBInstallKeyboard && __USBInstallMouse) {
|
||||
static uint8_t desc_hid_report[] = {
|
||||
uint8_t desc_hid_report[] = {
|
||||
TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(1)),
|
||||
TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(2))
|
||||
};
|
||||
if (len) {
|
||||
*len = sizeof(desc_hid_report);
|
||||
__hid_report = (uint8_t *)malloc(sizeof(desc_hid_report));
|
||||
if (__hid_report) {
|
||||
__hid_report_len = sizeof(desc_hid_report);
|
||||
memcpy(__hid_report, desc_hid_report, __hid_report_len);
|
||||
}
|
||||
return desc_hid_report;
|
||||
} else if (__USBInstallKeyboard && ! __USBInstallMouse) {
|
||||
static uint8_t desc_hid_report[] = {
|
||||
uint8_t desc_hid_report[] = {
|
||||
TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(1))
|
||||
};
|
||||
if (len) {
|
||||
*len = sizeof(desc_hid_report);
|
||||
__hid_report = (uint8_t *)malloc(sizeof(desc_hid_report));
|
||||
if (__hid_report) {
|
||||
__hid_report_len = sizeof(desc_hid_report);
|
||||
memcpy(__hid_report, desc_hid_report, __hid_report_len);
|
||||
}
|
||||
return desc_hid_report;
|
||||
} else { // if (!__USBInstallKeyboard && __USBInstallMouse) {
|
||||
static uint8_t desc_hid_report[] = {
|
||||
} else if (! __USBInstallKeyboard && __USBInstallMouse) {
|
||||
uint8_t desc_hid_report[] = {
|
||||
TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(1))
|
||||
};
|
||||
if (len) {
|
||||
*len = sizeof(desc_hid_report);
|
||||
__hid_report = (uint8_t *)malloc(sizeof(desc_hid_report));
|
||||
if (__hid_report) {
|
||||
__hid_report_len = sizeof(desc_hid_report);
|
||||
memcpy(__hid_report, desc_hid_report, __hid_report_len);
|
||||
}
|
||||
return desc_hid_report;
|
||||
} else {
|
||||
__hid_report = nullptr;
|
||||
__hid_report_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Invoked when received GET HID REPORT DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
// Descriptor contents must exist long enough for transfer to complete
|
||||
uint8_t const * tud_hid_descriptor_report_cb(void) {
|
||||
uint8_t const * tud_hid_descriptor_report_cb(uint8_t instance) {
|
||||
(void) instance;
|
||||
return GetDescHIDReport(nullptr);
|
||||
}
|
||||
|
||||
|
||||
static uint8_t *usbd_desc_cfg = nullptr;
|
||||
const uint8_t *tud_descriptor_configuration_cb(uint8_t index) {
|
||||
(void)index;
|
||||
static uint8_t *usbd_desc_cfg;
|
||||
return usbd_desc_cfg;
|
||||
}
|
||||
|
||||
void __SetupUSBDescriptor() {
|
||||
if (!usbd_desc_cfg) {
|
||||
bool hasHID = __USBInstallKeyboard || __USBInstallMouse;
|
||||
|
||||
uint8_t interface_count = (__USBInstallSerial ? 2 : 0) + (hasHID ? 1 : 0) + (__USBInstallMIDI ? 2 : 0);
|
||||
|
||||
static uint8_t cdc_desc[TUD_CDC_DESC_LEN] = {
|
||||
uint8_t cdc_desc[TUD_CDC_DESC_LEN] = {
|
||||
// Interface number, string index, protocol, report descriptor len, EP In & Out address, size & polling interval
|
||||
TUD_CDC_DESCRIPTOR(USBD_ITF_CDC, USBD_STR_CDC, USBD_CDC_EP_CMD, USBD_CDC_CMD_MAX_SIZE, USBD_CDC_EP_OUT, USBD_CDC_EP_IN, USBD_CDC_IN_OUT_MAX_SIZE)
|
||||
};
|
||||
@@ -175,43 +193,44 @@ const uint8_t *tud_descriptor_configuration_cb(uint8_t index) {
|
||||
int hid_report_len;
|
||||
GetDescHIDReport(&hid_report_len);
|
||||
uint8_t hid_itf = __USBInstallSerial ? 2 : 0;
|
||||
static uint8_t hid_desc[TUD_HID_DESC_LEN] = {
|
||||
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_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, 10)
|
||||
};
|
||||
|
||||
uint8_t midi_itf = hid_itf + (hasHID ? 1 : 0);
|
||||
static uint8_t midi_desc[TUD_MIDI_DESC_LEN] = {
|
||||
uint8_t midi_desc[TUD_MIDI_DESC_LEN] = {
|
||||
// Interface number, string index, EP Out & EP In address, EP size
|
||||
TUD_MIDI_DESCRIPTOR(midi_itf, 0, EPNUM_MIDI, 0x80 | EPNUM_MIDI, 64)
|
||||
};
|
||||
|
||||
int usbd_desc_len = TUD_CONFIG_DESC_LEN + (__USBInstallSerial ? sizeof(cdc_desc) : 0) + (hasHID ? sizeof(hid_desc) : 0) + (__USBInstallMIDI ? sizeof(midi_desc) : 0);
|
||||
|
||||
static uint8_t tud_cfg_desc[TUD_CONFIG_DESC_LEN] = {
|
||||
uint8_t tud_cfg_desc[TUD_CONFIG_DESC_LEN] = {
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, interface_count, USBD_STR_0, usbd_desc_len, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, USBD_MAX_POWER_MA)
|
||||
};
|
||||
|
||||
// Combine to one descriptor
|
||||
usbd_desc_cfg = (uint8_t *)malloc(usbd_desc_len);
|
||||
bzero(usbd_desc_cfg, usbd_desc_len);
|
||||
uint8_t *ptr = usbd_desc_cfg;
|
||||
memcpy(ptr, tud_cfg_desc, sizeof(tud_cfg_desc));
|
||||
ptr += sizeof(tud_cfg_desc);
|
||||
if (__USBInstallSerial) {
|
||||
memcpy(ptr, cdc_desc, sizeof(cdc_desc));
|
||||
ptr += sizeof(cdc_desc);
|
||||
}
|
||||
if (hasHID) {
|
||||
memcpy(ptr, hid_desc, sizeof(hid_desc));
|
||||
ptr += sizeof(hid_desc);
|
||||
}
|
||||
if (__USBInstallMIDI) {
|
||||
memcpy(ptr, midi_desc, sizeof(midi_desc));
|
||||
if (usbd_desc_cfg) {
|
||||
bzero(usbd_desc_cfg, usbd_desc_len);
|
||||
uint8_t *ptr = usbd_desc_cfg;
|
||||
memcpy(ptr, tud_cfg_desc, sizeof(tud_cfg_desc));
|
||||
ptr += sizeof(tud_cfg_desc);
|
||||
if (__USBInstallSerial) {
|
||||
memcpy(ptr, cdc_desc, sizeof(cdc_desc));
|
||||
ptr += sizeof(cdc_desc);
|
||||
}
|
||||
if (hasHID) {
|
||||
memcpy(ptr, hid_desc, sizeof(hid_desc));
|
||||
ptr += sizeof(hid_desc);
|
||||
}
|
||||
if (__USBInstallMIDI) {
|
||||
memcpy(ptr, midi_desc, sizeof(midi_desc));
|
||||
}
|
||||
}
|
||||
}
|
||||
return usbd_desc_cfg;
|
||||
}
|
||||
|
||||
const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||
@@ -230,15 +249,7 @@ const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||
};
|
||||
|
||||
if (!idString[0]) {
|
||||
// Get ID string into human readable serial number on the first pass
|
||||
pico_unique_board_id_t id;
|
||||
pico_get_unique_board_id(&id);
|
||||
idString[0] = 0;
|
||||
for (auto i = 0; i < PICO_UNIQUE_BOARD_ID_SIZE_BYTES; i++) {
|
||||
char hx[3];
|
||||
sprintf(hx, "%02X", id.id[i]);
|
||||
strcat(idString, hx);
|
||||
}
|
||||
pico_get_unique_board_id_string(idString, sizeof(idString));
|
||||
}
|
||||
|
||||
uint8_t len;
|
||||
@@ -277,13 +288,19 @@ static int64_t timer_task(__unused alarm_id_t id, __unused void *user_data) {
|
||||
return USB_TASK_INTERVAL;
|
||||
}
|
||||
|
||||
void __USBStart() __attribute__((weak));
|
||||
|
||||
void __USBStart() {
|
||||
if (tusb_inited()) {
|
||||
// Already called
|
||||
return;
|
||||
}
|
||||
|
||||
__SetupDescHIDReport();
|
||||
__SetupUSBDescriptor();
|
||||
|
||||
mutex_init(&__usb_mutex);
|
||||
|
||||
tusb_init();
|
||||
|
||||
irq_set_exclusive_handler(USB_TASK_IRQ, usb_irq);
|
||||
@@ -296,8 +313,9 @@ void __USBStart() {
|
||||
// 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
|
||||
uint16_t tud_hid_get_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) {
|
||||
// TODO not Implemented
|
||||
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;
|
||||
(void) report_id;
|
||||
(void) report_type;
|
||||
(void) buffer;
|
||||
@@ -308,8 +326,9 @@ uint16_t tud_hid_get_report_cb(uint8_t report_id, hid_report_type_t report_type,
|
||||
|
||||
// Invoked when received SET_REPORT control request or
|
||||
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
|
||||
void tud_hid_set_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) {
|
||||
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;
|
||||
(void) report_id;
|
||||
(void) report_type;
|
||||
(void) buffer;
|
||||
@@ -317,4 +336,3 @@ void tud_hid_set_report_cb(uint8_t report_id, hid_report_type_t report_type, uin
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
#define ARDUINO_PICO_MAJOR 2
|
||||
#define ARDUINO_PICO_MINOR 2
|
||||
#define ARDUINO_PICO_REVISION 0
|
||||
#define ARDUINO_PICO_VERSION_STR "2.2.0"
|
||||
@@ -0,0 +1,372 @@
|
||||
/*
|
||||
Serial-over-PIO for the Raspberry Pi Pico RP2040
|
||||
|
||||
Copyright (c) 2021 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "SerialPIO.h"
|
||||
#include "CoreMutex.h"
|
||||
#include <hardware/gpio.h>
|
||||
#include <map>
|
||||
#include "pio_uart.pio.h"
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// -- Generates a unique program for differing bit lengths
|
||||
static std::map<int, PIOProgram*> _txMap;
|
||||
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;
|
||||
p->length = pg->length;
|
||||
p->origin = pg->origin;
|
||||
uint16_t *insn = (uint16_t *)malloc(p->length * 2);
|
||||
if (!insn) {
|
||||
delete p;
|
||||
return nullptr;
|
||||
}
|
||||
memcpy(insn, pg->instructions, p->length * 2);
|
||||
insn[0] = pio_encode_set(pio_x, repl);
|
||||
p->instructions = insn;
|
||||
return p;
|
||||
}
|
||||
|
||||
static PIOProgram *_getTxProgram(int bits) {
|
||||
auto f = _txMap.find(bits);
|
||||
if (f == _txMap.end()) {
|
||||
pio_program_t * p = pio_make_uart_prog(bits, &pio_tx_program);
|
||||
_txMap.insert({bits, new PIOProgram(p)});
|
||||
f = _txMap.find(bits);
|
||||
}
|
||||
return f->second;
|
||||
}
|
||||
|
||||
static PIOProgram *_getRxProgram(int bits) {
|
||||
auto f = _rxMap.find(bits);
|
||||
if (f == _rxMap.end()) {
|
||||
pio_program_t * p = pio_make_uart_prog(bits, &pio_rx_program);
|
||||
_rxMap.insert({bits, new PIOProgram(p)});
|
||||
f = _rxMap.find(bits);
|
||||
}
|
||||
return f->second;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// TODO - this works, but there must be a faster/better way...
|
||||
static int _parity(int bits, int data) {
|
||||
int p = 0;
|
||||
for (int b = 0; b < bits; b++) {
|
||||
p ^= (data & (1 << b)) ? 1 : 0;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
// We need to cache generated SerialPIOs so we can add data to them from
|
||||
// the shared handler
|
||||
static SerialPIO *_pioSP[2][4];
|
||||
static void __not_in_flash_func(_fifoIRQ)() {
|
||||
for (int p = 0; p < 2; p++) {
|
||||
for (int sm = 0; sm < 4; sm++) {
|
||||
SerialPIO *s = _pioSP[p][sm];
|
||||
if (s) {
|
||||
s->_handleIRQ();
|
||||
pio_interrupt_clear((p == 0) ? pio0 : pio1, sm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void __not_in_flash_func(SerialPIO::_handleIRQ)() {
|
||||
if (_rx == NOPIN) {
|
||||
return;
|
||||
}
|
||||
while (!pio_sm_is_rx_fifo_empty(_rxPIO, _rxSM)) {
|
||||
uint32_t decode = _rxPIO->rxf[_rxSM];
|
||||
decode >>= 33 - _rxBits;
|
||||
uint32_t val = 0;
|
||||
for (int b = 0; b < _bits + 1; b++) {
|
||||
val |= (decode & (1 << (b * 2))) ? 1 << b : 0;
|
||||
}
|
||||
if (_parity == UART_PARITY_EVEN) {
|
||||
int p = ::_parity(_bits, val);
|
||||
int r = (val & (1 << _bits)) ? 1 : 0;
|
||||
if (p != r) {
|
||||
// TODO - parity error
|
||||
continue;
|
||||
}
|
||||
} else if (_parity == UART_PARITY_ODD) {
|
||||
int p = ::_parity(_bits, val);
|
||||
int r = (val & (1 << _bits)) ? 1 : 0;
|
||||
if (p == r) {
|
||||
// TODO - parity error
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
auto next_writer = _writer + 1;
|
||||
if (next_writer == _fifoSize) {
|
||||
next_writer = 0;
|
||||
}
|
||||
if (next_writer != _reader) {
|
||||
_queue[_writer] = val & ((1 << _bits) - 1);
|
||||
asm volatile("" ::: "memory"); // Ensure the queue is written before the written count advances
|
||||
_writer = next_writer;
|
||||
} else {
|
||||
_overflow = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SerialPIO::SerialPIO(pin_size_t tx, pin_size_t rx, size_t fifoSize) {
|
||||
_tx = tx;
|
||||
_rx = rx;
|
||||
_fifoSize = fifoSize + 1; // Always one unused entry
|
||||
_queue = new uint8_t[_fifoSize];
|
||||
mutex_init(&_mutex);
|
||||
}
|
||||
|
||||
SerialPIO::~SerialPIO() {
|
||||
end();
|
||||
delete[] _queue;
|
||||
}
|
||||
|
||||
void SerialPIO::begin(unsigned long baud, uint16_t config) {
|
||||
_overflow = false;
|
||||
_baud = baud;
|
||||
switch (config & SERIAL_PARITY_MASK) {
|
||||
case SERIAL_PARITY_EVEN:
|
||||
_parity = UART_PARITY_EVEN;
|
||||
break;
|
||||
case SERIAL_PARITY_ODD:
|
||||
_parity = UART_PARITY_ODD;
|
||||
break;
|
||||
default:
|
||||
_parity = UART_PARITY_NONE;
|
||||
break;
|
||||
}
|
||||
switch (config & SERIAL_STOP_BIT_MASK) {
|
||||
case SERIAL_STOP_BIT_1:
|
||||
_stop = 1;
|
||||
break;
|
||||
default:
|
||||
_stop = 2;
|
||||
break;
|
||||
}
|
||||
switch (config & SERIAL_DATA_MASK) {
|
||||
case SERIAL_DATA_5:
|
||||
_bits = 5;
|
||||
break;
|
||||
case SERIAL_DATA_6:
|
||||
_bits = 6;
|
||||
break;
|
||||
case SERIAL_DATA_7:
|
||||
_bits = 7;
|
||||
break;
|
||||
default:
|
||||
_bits = 8;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((_tx == NOPIN) && (_rx == NOPIN)) {
|
||||
DEBUGCORE("ERROR: No pins specified for SerialPIO\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_tx != NOPIN) {
|
||||
_txBits = _bits + _stop + (_parity != UART_PARITY_NONE ? 1 : 0) + 1/*start bit*/;
|
||||
_txPgm = _getTxProgram(_txBits);
|
||||
int off;
|
||||
if (!_txPgm->prepare(&_txPIO, &_txSM, &off)) {
|
||||
DEBUGCORE("ERROR: Unable to allocate PIO TX UART, out of PIO resources\n");
|
||||
// ERROR, no free slots
|
||||
return;
|
||||
}
|
||||
|
||||
digitalWrite(_tx, HIGH);
|
||||
pinMode(_tx, OUTPUT);
|
||||
|
||||
pio_tx_program_init(_txPIO, _txSM, off, _tx);
|
||||
pio_sm_clear_fifos(_txPIO, _txSM); // Remove any existing data
|
||||
|
||||
// Put the divider into ISR w/o using up program space
|
||||
pio_sm_put_blocking(_txPIO, _txSM, clock_get_hz(clk_sys) / _baud - 2);
|
||||
pio_sm_exec(_txPIO, _txSM, pio_encode_pull(false, false));
|
||||
pio_sm_exec(_txPIO, _txSM, pio_encode_mov(pio_isr, pio_osr));
|
||||
|
||||
// Start running!
|
||||
pio_sm_set_enabled(_txPIO, _txSM, true);
|
||||
}
|
||||
if (_rx != NOPIN) {
|
||||
_writer = 0;
|
||||
_reader = 0;
|
||||
|
||||
_rxBits = 2 * (_bits + _stop + (_parity != UART_PARITY_NONE ? 1 : 0) + 1) - 1;
|
||||
_rxPgm = _getRxProgram(_rxBits);
|
||||
int off;
|
||||
if (!_rxPgm->prepare(&_rxPIO, &_rxSM, &off)) {
|
||||
DEBUGCORE("ERROR: Unable to allocate PIO RX UART, out of PIO resources\n");
|
||||
return;
|
||||
}
|
||||
// Stash away the created RX port for the IRQ handler
|
||||
_pioSP[pio_get_index(_rxPIO)][_rxSM] = this;
|
||||
|
||||
pinMode(_rx, INPUT);
|
||||
pio_rx_program_init(_rxPIO, _rxSM, off, _rx);
|
||||
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_exec(_rxPIO, _rxSM, pio_encode_pull(false, false));
|
||||
|
||||
// Join the TX FIFO to the RX one now that we don't need it
|
||||
_rxPIO->sm[_rxSM].shiftctrl |= 0x80000000;
|
||||
|
||||
// Enable interrupts on rxfifo
|
||||
switch (_rxSM) {
|
||||
case 0: pio_set_irq0_source_enabled(_rxPIO, pis_sm0_rx_fifo_not_empty, true); break;
|
||||
case 1: pio_set_irq0_source_enabled(_rxPIO, pis_sm1_rx_fifo_not_empty, true); break;
|
||||
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;
|
||||
irq_set_exclusive_handler(irqno, _fifoIRQ);
|
||||
irq_set_enabled(irqno, true);
|
||||
|
||||
pio_sm_set_enabled(_rxPIO, _rxSM, true);
|
||||
}
|
||||
|
||||
_running = true;
|
||||
}
|
||||
|
||||
void SerialPIO::end() {
|
||||
if (!_running) {
|
||||
return;
|
||||
}
|
||||
if (_tx != NOPIN) {
|
||||
pio_sm_set_enabled(_txPIO, _txSM, false);
|
||||
}
|
||||
if (_rx != NOPIN) {
|
||||
pio_sm_set_enabled(_rxPIO, _rxSM, false);
|
||||
_pioSP[pio_get_index(_rxPIO)][_rxSM] = nullptr;
|
||||
// If no more active, disable the IRQ
|
||||
auto pioNum = pio_get_index(_rxPIO);
|
||||
bool used = false;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
used = used || !!_pioSP[pioNum][i];
|
||||
}
|
||||
if (!used) {
|
||||
auto irqno = pioNum == 0 ? PIO0_IRQ_0 : PIO1_IRQ_0;
|
||||
irq_set_enabled(irqno, false);
|
||||
}
|
||||
}
|
||||
_running = false;
|
||||
}
|
||||
|
||||
int SerialPIO::peek() {
|
||||
CoreMutex m(&_mutex);
|
||||
if (!_running || !m || (_rx == NOPIN)) {
|
||||
return -1;
|
||||
}
|
||||
// If there's something in the FIFO now, just peek at it
|
||||
if (_writer != _reader) {
|
||||
return _queue[_reader];
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int SerialPIO::read() {
|
||||
CoreMutex m(&_mutex);
|
||||
if (!_running || !m || (_rx == NOPIN)) {
|
||||
return -1;
|
||||
}
|
||||
if (_writer != _reader) {
|
||||
auto ret = _queue[_reader];
|
||||
asm volatile("" ::: "memory"); // Ensure the value is read before advancing
|
||||
auto next_reader = (_reader + 1) % _fifoSize;
|
||||
asm volatile("" ::: "memory"); // Ensure the reader value is only written once, correctly
|
||||
_reader = next_reader;
|
||||
return ret;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool SerialPIO::overflow() {
|
||||
CoreMutex m(&_mutex);
|
||||
if (!_running || !m || (_rx == NOPIN)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hold = _overflow;
|
||||
_overflow = false;
|
||||
return hold;
|
||||
}
|
||||
|
||||
int SerialPIO::available() {
|
||||
CoreMutex m(&_mutex);
|
||||
if (!_running || !m || (_rx == NOPIN)) {
|
||||
return 0;
|
||||
}
|
||||
return (_writer - _reader) % _fifoSize;
|
||||
}
|
||||
|
||||
int SerialPIO::availableForWrite() {
|
||||
CoreMutex m(&_mutex);
|
||||
if (!_running || !m || (_tx == NOPIN)) {
|
||||
return 0;
|
||||
}
|
||||
return 8 - pio_sm_get_tx_fifo_level(_txPIO, _txSM);
|
||||
}
|
||||
|
||||
void SerialPIO::flush() {
|
||||
CoreMutex m(&_mutex);
|
||||
if (!_running || !m || (_tx == NOPIN)) {
|
||||
return;
|
||||
}
|
||||
while (!pio_sm_is_tx_fifo_empty(_txPIO, _txSM)) {
|
||||
delay(1); // Wait for all FIFO to be read
|
||||
}
|
||||
// Could have 1 byte being transmitted, so wait for bit times
|
||||
delay((1000 * (_txBits + 1)) / _baud);
|
||||
}
|
||||
|
||||
size_t SerialPIO::write(uint8_t c) {
|
||||
CoreMutex m(&_mutex);
|
||||
if (!_running || !m || (_tx == NOPIN)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t val = c;
|
||||
if (_parity == UART_PARITY_NONE) {
|
||||
val |= 7 << _bits; // Set 2 stop bits, the HW will only transmit the required number
|
||||
} else if (_parity == UART_PARITY_EVEN) {
|
||||
val |= ::_parity(_bits, c) << _bits;
|
||||
val |= 7 << (_bits + 1);
|
||||
} else {
|
||||
val |= (1 ^ ::_parity(_bits, c)) << _bits;
|
||||
val |= 7 << (_bits + 1);
|
||||
}
|
||||
val <<= 1; // Start bit = low
|
||||
|
||||
pio_sm_put_blocking(_txPIO, _txSM, val);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
SerialPIO::operator bool() {
|
||||
return _running;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
Serial-over-PIO for the Raspberry Pi Pico RP2040
|
||||
|
||||
Copyright (c) 2021 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "api/HardwareSerial.h"
|
||||
#include <stdarg.h>
|
||||
#include <queue>
|
||||
#include <hardware/uart.h>
|
||||
#include "CoreMutex.h"
|
||||
|
||||
extern "C" typedef struct uart_inst uart_inst_t;
|
||||
|
||||
class SerialPIO : public HardwareSerial {
|
||||
public:
|
||||
static const pin_size_t NOPIN = 0xff; // Use in constructor to disable RX or TX unit
|
||||
SerialPIO(pin_size_t tx, pin_size_t rx, size_t fifoSize = 32);
|
||||
~SerialPIO();
|
||||
|
||||
void begin(unsigned long baud = 115200) override {
|
||||
begin(baud, SERIAL_8N1);
|
||||
};
|
||||
void begin(unsigned long baud, uint16_t config) override;
|
||||
void end() override;
|
||||
|
||||
virtual int peek() override;
|
||||
virtual int read() override;
|
||||
virtual int available() override;
|
||||
virtual int availableForWrite() override;
|
||||
virtual void flush() override;
|
||||
virtual size_t write(uint8_t c) override;
|
||||
bool overflow();
|
||||
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
|
||||
void _handleIRQ();
|
||||
|
||||
protected:
|
||||
bool _running = false;
|
||||
pin_size_t _tx, _rx;
|
||||
int _baud;
|
||||
int _bits;
|
||||
uart_parity_t _parity;
|
||||
int _stop;
|
||||
bool _overflow;
|
||||
mutex_t _mutex;
|
||||
|
||||
PIOProgram *_txPgm;
|
||||
PIO _txPIO;
|
||||
int _txSM;
|
||||
int _txBits;
|
||||
|
||||
PIOProgram *_rxPgm;
|
||||
PIO _rxPIO;
|
||||
int _rxSM;
|
||||
int _rxBits;
|
||||
|
||||
// Lockless, IRQ-handled circular queue
|
||||
size_t _fifoSize;
|
||||
uint32_t _writer;
|
||||
uint32_t _reader;
|
||||
uint8_t *_queue;
|
||||
};
|
||||
+277
-43
@@ -31,78 +31,208 @@
|
||||
extern void serialEvent1() __attribute__((weak));
|
||||
extern void serialEvent2() __attribute__((weak));
|
||||
|
||||
bool SerialUART::setRX(pin_size_t rx) {
|
||||
bool SerialUART::setRX(pin_size_t pin) {
|
||||
constexpr uint32_t valid[2] = { __bitset({1, 13, 17, 29}) /* UART0 */,
|
||||
__bitset({5, 9, 21, 25}) /* UART1 */
|
||||
};
|
||||
if (_running) {
|
||||
DEBUGCORE("ERROR: SerialUART setRX while running\n");
|
||||
return false;
|
||||
} else if ((1 << rx) & valid[uart_get_index(_uart)]) {
|
||||
_rx = rx;
|
||||
if ((!_running) && ((1 << pin) & valid[uart_get_index(_uart)])) {
|
||||
_rx = pin;
|
||||
return true;
|
||||
} else {
|
||||
DEBUGCORE("ERROR: SerialUART setRX illegal pin (%d)\n", rx);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set Serial%d.RX while running", uart_get_index(_uart) + 1);
|
||||
} else {
|
||||
panic("FATAL: Attempting to set Serial%d.RX to illegal pin %d", uart_get_index(_uart) + 1, pin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SerialUART::setTX(pin_size_t tx) {
|
||||
bool SerialUART::setTX(pin_size_t pin) {
|
||||
constexpr uint32_t valid[2] = { __bitset({0, 12, 16, 28}) /* UART0 */,
|
||||
__bitset({4, 8, 20, 24}) /* UART1 */
|
||||
};
|
||||
if (_running) {
|
||||
DEBUGCORE("ERROR: SerialUART setTX while running\n");
|
||||
return false;
|
||||
} else if ((1 << tx) & valid[uart_get_index(_uart)]) {
|
||||
_tx = tx;
|
||||
if ((!_running) && ((1 << pin) & valid[uart_get_index(_uart)])) {
|
||||
_tx = pin;
|
||||
return true;
|
||||
} else {
|
||||
DEBUGCORE("ERROR: SerialUART setTX illegal pin (%d)\n", tx);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set Serial%d.TX while running", uart_get_index(_uart) + 1);
|
||||
} else {
|
||||
panic("FATAL: Attempting to set Serial%d.TX to illegal pin %d", uart_get_index(_uart) + 1, pin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
SerialUART::SerialUART(uart_inst_t *uart, pin_size_t tx, pin_size_t rx) {
|
||||
bool SerialUART::setRTS(pin_size_t pin) {
|
||||
constexpr uint32_t valid[2] = { __bitset({3, 15, 19}) /* UART0 */,
|
||||
__bitset({7, 11, 23, 27}) /* UART1 */
|
||||
};
|
||||
if ((!_running) && ((1 << pin) & valid[uart_get_index(_uart)])) {
|
||||
_rts = pin;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set Serial%d.RTS while running", uart_get_index(_uart) + 1);
|
||||
} else {
|
||||
panic("FATAL: Attempting to set Serial%d.RTS to illegal pin %d", uart_get_index(_uart) + 1, pin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SerialUART::setCTS(pin_size_t pin) {
|
||||
constexpr uint32_t valid[2] = { __bitset({2, 14, 18}) /* UART0 */,
|
||||
__bitset({6, 10, 22, 26}) /* UART1 */
|
||||
};
|
||||
if ((!_running) && ((1 << pin) & valid[uart_get_index(_uart)])) {
|
||||
_cts = pin;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set Serial%d.CTS while running", uart_get_index(_uart) + 1);
|
||||
} else {
|
||||
panic("FATAL: Attempting to set Serial%d.CTS to illegal pin %d", uart_get_index(_uart) + 1, pin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool SerialUART::setPollingMode(bool mode) {
|
||||
if (_running) {
|
||||
return false;
|
||||
}
|
||||
_polling = mode;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SerialUART::setFIFOSize(size_t size) {
|
||||
if (!size || _running) {
|
||||
return false;
|
||||
}
|
||||
_fifoSize = size + 1; // Always 1 unused entry
|
||||
return true;
|
||||
}
|
||||
|
||||
SerialUART::SerialUART(uart_inst_t *uart, pin_size_t tx, pin_size_t rx, pin_size_t rts, pin_size_t cts) {
|
||||
_uart = uart;
|
||||
_tx = tx;
|
||||
_rx = rx;
|
||||
_rts = rts;
|
||||
_cts = cts;
|
||||
mutex_init(&_mutex);
|
||||
mutex_init(&_fifoMutex);
|
||||
}
|
||||
|
||||
static void _uart0IRQ();
|
||||
static void _uart1IRQ();
|
||||
|
||||
void SerialUART::begin(unsigned long baud, uint16_t config) {
|
||||
if (_running) {
|
||||
end();
|
||||
}
|
||||
_overflow = false;
|
||||
_queue = new uint8_t[_fifoSize];
|
||||
_baud = baud;
|
||||
uart_init(_uart, baud);
|
||||
int bits, stop;
|
||||
uart_parity_t parity;
|
||||
switch (config & SERIAL_PARITY_MASK) {
|
||||
case SERIAL_PARITY_EVEN: parity = UART_PARITY_EVEN; break;
|
||||
case SERIAL_PARITY_ODD: parity = UART_PARITY_ODD; break;
|
||||
default: parity = UART_PARITY_NONE; break;
|
||||
case SERIAL_PARITY_EVEN:
|
||||
parity = UART_PARITY_EVEN;
|
||||
break;
|
||||
case SERIAL_PARITY_ODD:
|
||||
parity = UART_PARITY_ODD;
|
||||
break;
|
||||
default:
|
||||
parity = UART_PARITY_NONE;
|
||||
break;
|
||||
}
|
||||
switch (config & SERIAL_STOP_BIT_MASK) {
|
||||
case SERIAL_STOP_BIT_1: stop = 1; break;
|
||||
default: stop = 2; break;
|
||||
case SERIAL_STOP_BIT_1:
|
||||
stop = 1;
|
||||
break;
|
||||
default:
|
||||
stop = 2;
|
||||
break;
|
||||
}
|
||||
switch (config & SERIAL_DATA_MASK) {
|
||||
case SERIAL_DATA_5: bits = 5; break;
|
||||
case SERIAL_DATA_6: bits = 6; break;
|
||||
case SERIAL_DATA_7: bits = 7; break;
|
||||
default: bits = 8; break;
|
||||
case SERIAL_DATA_5:
|
||||
bits = 5;
|
||||
break;
|
||||
case SERIAL_DATA_6:
|
||||
bits = 6;
|
||||
break;
|
||||
case SERIAL_DATA_7:
|
||||
bits = 7;
|
||||
break;
|
||||
default:
|
||||
bits = 8;
|
||||
break;
|
||||
}
|
||||
uart_set_format(_uart, bits, stop, parity);
|
||||
gpio_set_function(_tx, GPIO_FUNC_UART);
|
||||
gpio_set_function(_rx, GPIO_FUNC_UART);
|
||||
if (_rts != UART_PIN_NOT_DEFINED) {
|
||||
gpio_set_function(_rts, GPIO_FUNC_UART);
|
||||
}
|
||||
if (_cts != UART_PIN_NOT_DEFINED) {
|
||||
gpio_set_function(_cts, GPIO_FUNC_UART);
|
||||
}
|
||||
uart_set_hw_flow(_uart, _rts != UART_PIN_NOT_DEFINED, _cts != UART_PIN_NOT_DEFINED);
|
||||
_writer = 0;
|
||||
_reader = 0;
|
||||
|
||||
if (!_polling) {
|
||||
if (_uart == uart0) {
|
||||
irq_set_exclusive_handler(UART0_IRQ, _uart0IRQ);
|
||||
irq_set_enabled(UART0_IRQ, true);
|
||||
} else {
|
||||
irq_set_exclusive_handler(UART1_IRQ, _uart1IRQ);
|
||||
irq_set_enabled(UART1_IRQ, true);
|
||||
}
|
||||
// Set the IRQ enables and FIFO level to minimum
|
||||
uart_set_irq_enables(_uart, true, false);
|
||||
} else {
|
||||
// Polling mode has no IRQs used
|
||||
}
|
||||
_running = true;
|
||||
_peek = -1;
|
||||
}
|
||||
|
||||
void SerialUART::end() {
|
||||
if (!_running) {
|
||||
return;
|
||||
}
|
||||
uart_deinit(_uart);
|
||||
_running = false;
|
||||
if (!_polling) {
|
||||
if (_uart == uart0) {
|
||||
irq_set_enabled(UART0_IRQ, false);
|
||||
} else {
|
||||
irq_set_enabled(UART1_IRQ, false);
|
||||
}
|
||||
}
|
||||
// Paranoia - ensure nobody else is using anything here at the same time
|
||||
mutex_enter_blocking(&_mutex);
|
||||
mutex_enter_blocking(&_fifoMutex);
|
||||
uart_deinit(_uart);
|
||||
delete[] _queue;
|
||||
// Reset the mutexes once all is off/cleaned up
|
||||
mutex_exit(&_fifoMutex);
|
||||
mutex_exit(&_mutex);
|
||||
}
|
||||
|
||||
void SerialUART::_pumpFIFO() {
|
||||
// Use the _fifoMutex to guard against the other core potentially
|
||||
// running the IRQ (since we can't disable their IRQ handler).
|
||||
// We guard against this core by disabling the IRQ handler and
|
||||
// re-enabling if it was previously enabled at the end.
|
||||
auto irqno = (_uart == uart0) ? UART0_IRQ : UART1_IRQ;
|
||||
bool enabled = irq_is_enabled(irqno);
|
||||
irq_set_enabled(irqno, false);
|
||||
mutex_enter_blocking(&_fifoMutex);
|
||||
_handleIRQ(false);
|
||||
mutex_exit(&_fifoMutex);
|
||||
irq_set_enabled(irqno, enabled);
|
||||
}
|
||||
|
||||
int SerialUART::peek() {
|
||||
@@ -110,11 +240,15 @@ int SerialUART::peek() {
|
||||
if (!_running || !m) {
|
||||
return -1;
|
||||
}
|
||||
if (_peek >= 0) {
|
||||
return _peek;
|
||||
if (_polling) {
|
||||
_handleIRQ(false);
|
||||
} else {
|
||||
_pumpFIFO();
|
||||
}
|
||||
_peek = uart_getc(_uart);
|
||||
return _peek;
|
||||
if (_writer != _reader) {
|
||||
return _queue[_reader];
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int SerialUART::read() {
|
||||
@@ -122,12 +256,30 @@ int SerialUART::read() {
|
||||
if (!_running || !m) {
|
||||
return -1;
|
||||
}
|
||||
if (_peek >= 0) {
|
||||
int ret = _peek;
|
||||
_peek = -1;
|
||||
if (_polling) {
|
||||
_handleIRQ(false);
|
||||
} else {
|
||||
_pumpFIFO();
|
||||
}
|
||||
if (_writer != _reader) {
|
||||
auto ret = _queue[_reader];
|
||||
asm volatile("" ::: "memory"); // Ensure the value is read before advancing
|
||||
auto next_reader = (_reader + 1) % _fifoSize;
|
||||
asm volatile("" ::: "memory"); // Ensure the reader value is only written once, correctly
|
||||
_reader = next_reader;
|
||||
return ret;
|
||||
}
|
||||
return uart_getc(_uart);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool SerialUART::overflow() {
|
||||
CoreMutex m(&_mutex);
|
||||
if (!_running || !m) {
|
||||
return false;
|
||||
}
|
||||
bool hold = _overflow;
|
||||
_overflow = false;
|
||||
return hold;
|
||||
}
|
||||
|
||||
int SerialUART::available() {
|
||||
@@ -135,7 +287,12 @@ int SerialUART::available() {
|
||||
if (!_running || !m) {
|
||||
return 0;
|
||||
}
|
||||
return (uart_is_readable(_uart)) ? 1 : 0;
|
||||
if (_polling) {
|
||||
_handleIRQ(false);
|
||||
} else {
|
||||
_pumpFIFO();
|
||||
}
|
||||
return (_writer - _reader) % _fifoSize;
|
||||
}
|
||||
|
||||
int SerialUART::availableForWrite() {
|
||||
@@ -143,6 +300,9 @@ int SerialUART::availableForWrite() {
|
||||
if (!_running || !m) {
|
||||
return 0;
|
||||
}
|
||||
if (_polling) {
|
||||
_handleIRQ(false);
|
||||
}
|
||||
return (uart_is_writable(_uart)) ? 1 : 0;
|
||||
}
|
||||
|
||||
@@ -151,7 +311,10 @@ void SerialUART::flush() {
|
||||
if (!_running || !m) {
|
||||
return;
|
||||
}
|
||||
uart_default_tx_wait_blocking();
|
||||
if (_polling) {
|
||||
_handleIRQ(false);
|
||||
}
|
||||
uart_tx_wait_blocking(_uart);
|
||||
}
|
||||
|
||||
size_t SerialUART::write(uint8_t c) {
|
||||
@@ -159,6 +322,9 @@ size_t SerialUART::write(uint8_t c) {
|
||||
if (!_running || !m) {
|
||||
return 0;
|
||||
}
|
||||
if (_polling) {
|
||||
_handleIRQ(false);
|
||||
}
|
||||
uart_putc_raw(_uart, c);
|
||||
return 1;
|
||||
}
|
||||
@@ -168,6 +334,9 @@ size_t SerialUART::write(const uint8_t *p, size_t len) {
|
||||
if (!_running || !m) {
|
||||
return 0;
|
||||
}
|
||||
if (_polling) {
|
||||
_handleIRQ(false);
|
||||
}
|
||||
size_t cnt = len;
|
||||
while (cnt) {
|
||||
uart_putc_raw(_uart, *p);
|
||||
@@ -181,9 +350,6 @@ SerialUART::operator bool() {
|
||||
return _running;
|
||||
}
|
||||
|
||||
SerialUART Serial1(uart0, PIN_SERIAL1_TX, PIN_SERIAL1_RX);
|
||||
SerialUART Serial2(uart1, PIN_SERIAL2_TX, PIN_SERIAL2_RX);
|
||||
|
||||
void arduino::serialEvent1Run(void) {
|
||||
if (serialEvent1 && Serial1.available()) {
|
||||
serialEvent1();
|
||||
@@ -195,3 +361,71 @@ void arduino::serialEvent2Run(void) {
|
||||
serialEvent2();
|
||||
}
|
||||
}
|
||||
|
||||
// IRQ handler, called when FIFO > 1/8 full or when it had held unread data for >32 bit times
|
||||
void __not_in_flash_func(SerialUART::_handleIRQ)(bool inIRQ) {
|
||||
if (inIRQ) {
|
||||
uint32_t owner;
|
||||
if (!mutex_try_enter(&_fifoMutex, &owner)) {
|
||||
// Main app on the other core has the mutex so it is
|
||||
// in the process of pulling data out of the HW FIFO
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
auto next_writer = _writer + 1;
|
||||
if (next_writer == _fifoSize) {
|
||||
next_writer = 0;
|
||||
}
|
||||
if (next_writer != _reader) {
|
||||
_queue[_writer] = val;
|
||||
asm volatile("" ::: "memory"); // Ensure the queue is written before the written count advances
|
||||
// Avoid using division or mod because the HW divider could be in use
|
||||
_writer = next_writer;
|
||||
} else {
|
||||
_overflow = true;
|
||||
}
|
||||
}
|
||||
if (inIRQ) {
|
||||
mutex_exit(&_fifoMutex);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef __SERIAL1_DEVICE
|
||||
#define __SERIAL1_DEVICE uart0
|
||||
#endif
|
||||
#ifndef __SERIAL2_DEVICE
|
||||
#define __SERIAL2_DEVICE uart1
|
||||
#endif
|
||||
|
||||
#if defined(PIN_SERIAL1_RTS)
|
||||
SerialUART Serial1(__SERIAL1_DEVICE, PIN_SERIAL1_TX, PIN_SERIAL1_RX, PIN_SERIAL1_RTS, PIN_SERIAL1_CTS);
|
||||
#else
|
||||
SerialUART Serial1(__SERIAL1_DEVICE, PIN_SERIAL1_TX, PIN_SERIAL1_RX);
|
||||
#endif
|
||||
|
||||
#if defined(PIN_SERIAL2_RTS)
|
||||
SerialUART Serial2(__SERIAL2_DEVICE, PIN_SERIAL2_TX, PIN_SERIAL2_RX, PIN_SERIAL2_RTS, PIN_SERIAL2_CTS);
|
||||
#else
|
||||
SerialUART Serial2(__SERIAL2_DEVICE, PIN_SERIAL2_TX, PIN_SERIAL2_RX);
|
||||
#endif
|
||||
|
||||
|
||||
static void __not_in_flash_func(_uart0IRQ)() {
|
||||
if (__SERIAL1_DEVICE == uart0) {
|
||||
Serial1._handleIRQ();
|
||||
} else {
|
||||
Serial2._handleIRQ();
|
||||
}
|
||||
}
|
||||
|
||||
static void __not_in_flash_func(_uart1IRQ)() {
|
||||
if (__SERIAL2_DEVICE == uart1) {
|
||||
Serial2._handleIRQ();
|
||||
} else {
|
||||
Serial1._handleIRQ();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,22 +23,28 @@
|
||||
#include <Arduino.h>
|
||||
#include "api/HardwareSerial.h"
|
||||
#include <stdarg.h>
|
||||
#include <queue>
|
||||
#include "CoreMutex.h"
|
||||
|
||||
extern "C" typedef struct uart_inst uart_inst_t;
|
||||
|
||||
#define UART_PIN_NOT_DEFINED (255u)
|
||||
class SerialUART : public HardwareSerial {
|
||||
public:
|
||||
SerialUART(uart_inst_t *uart, pin_size_t tx, pin_size_t rx);
|
||||
SerialUART(uart_inst_t *uart, pin_size_t tx, pin_size_t rx, pin_size_t rts = UART_PIN_NOT_DEFINED, pin_size_t cts = UART_PIN_NOT_DEFINED);
|
||||
|
||||
// Select the pinout. Call before .begin()
|
||||
bool setRX(pin_size_t pin);
|
||||
bool setTX(pin_size_t pin);
|
||||
bool setRTS(pin_size_t pin);
|
||||
bool setCTS(pin_size_t pin);
|
||||
bool setPinout(pin_size_t tx, pin_size_t rx) {
|
||||
bool ret = setRX(rx);
|
||||
ret &= setTX(tx);
|
||||
return ret;
|
||||
}
|
||||
bool setFIFOSize(size_t size);
|
||||
bool setPollingMode(bool mode = true);
|
||||
|
||||
void begin(unsigned long baud = 115200) override {
|
||||
begin(baud, SERIAL_8N1);
|
||||
@@ -54,15 +60,29 @@ public:
|
||||
virtual size_t write(uint8_t c) override;
|
||||
virtual size_t write(const uint8_t *p, size_t len) override;
|
||||
using Print::write;
|
||||
bool overflow();
|
||||
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
|
||||
void _handleIRQ(bool inIRQ = true);
|
||||
|
||||
private:
|
||||
bool _running = false;
|
||||
uart_inst_t *_uart;
|
||||
pin_size_t _tx, _rx;
|
||||
pin_size_t _rts, _cts;
|
||||
int _baud;
|
||||
int _peek;
|
||||
mutex_t _mutex;
|
||||
bool _polling = false;
|
||||
bool _overflow;
|
||||
|
||||
// Lockless, IRQ-handled circular queue
|
||||
uint32_t _writer;
|
||||
uint32_t _reader;
|
||||
size_t _fifoSize = 32;
|
||||
uint8_t *_queue;
|
||||
mutex_t _fifoMutex; // Only needed when non-IRQ updates _writer
|
||||
void _pumpFIFO(); // User space FIFO transfer
|
||||
};
|
||||
|
||||
extern SerialUART Serial1; // HW UART 0
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef USE_TINYUSB
|
||||
#if !defined(USE_TINYUSB) && !defined(NO_USB)
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "CoreMutex.h"
|
||||
@@ -70,7 +70,7 @@ int SerialUSB::peek() {
|
||||
}
|
||||
|
||||
uint8_t c;
|
||||
return tud_cdc_peek(0, &c) ? (int) c : -1;
|
||||
return tud_cdc_peek(&c) ? (int) c : -1;
|
||||
}
|
||||
|
||||
int SerialUSB::read() {
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
SoftwareSerial wrapper for SerialPIO
|
||||
|
||||
Copyright (c) 2022 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SerialPIO.h"
|
||||
|
||||
class SoftwareSerial : public SerialPIO {
|
||||
public:
|
||||
// Note the rx/tx pins are swapped in PIO vs SWSerial
|
||||
SoftwareSerial(pin_size_t rx, pin_size_t tx, bool invert = false) : SerialPIO(tx, rx) {
|
||||
_invert = invert;
|
||||
}
|
||||
|
||||
~SoftwareSerial() {
|
||||
if (_invert) {
|
||||
gpio_set_outover(_tx, 0);
|
||||
gpio_set_outover(_rx, 0);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void begin(unsigned long baud = 115200) override {
|
||||
begin(baud, SERIAL_8N1);
|
||||
};
|
||||
|
||||
void begin(unsigned long baud, uint16_t config) override {
|
||||
SerialPIO::begin(baud, config);
|
||||
if (_invert) {
|
||||
gpio_set_outover(_tx, GPIO_OVERRIDE_INVERT);
|
||||
gpio_set_inover(_rx, GPIO_OVERRIDE_INVERT);
|
||||
}
|
||||
}
|
||||
|
||||
void listen() { /* noop */ }
|
||||
|
||||
bool isListening() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool _invert;
|
||||
};
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018, hathach for Adafruit
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_CONFIG_H_
|
||||
#define _TUSB_CONFIG_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// COMMON CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
#ifdef USE_TINYUSB
|
||||
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE
|
||||
#else
|
||||
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_NONE
|
||||
#endif
|
||||
|
||||
//#ifndef CFG_TUSB_MCU
|
||||
// #define CFG_TUSB_MCU OPT_MCU_RP2040
|
||||
//#endif
|
||||
#define CFG_TUSB_OS OPT_OS_PICO
|
||||
|
||||
#define CFG_TUSB_DEBUG 0
|
||||
//#if CFG_TUSB_DEBUG
|
||||
// #define tu_printf serial1_printf
|
||||
// extern int serial1_printf(const char *__restrict __format, ...);
|
||||
//#endif
|
||||
|
||||
#define CFG_TUSB_MEM_SECTION
|
||||
#define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4)
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// DEVICE CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
#define CFG_TUD_ENDOINT0_SIZE 64
|
||||
|
||||
//------------- CLASS -------------//
|
||||
#define CFG_TUD_CDC 1
|
||||
#define CFG_TUD_MSC 1
|
||||
#define CFG_TUD_HID 1
|
||||
#define CFG_TUD_MIDI 1
|
||||
#define CFG_TUD_VENDOR 1
|
||||
|
||||
// CDC FIFO size of TX and RX
|
||||
#define CFG_TUD_CDC_RX_BUFSIZE 256
|
||||
#define CFG_TUD_CDC_TX_BUFSIZE 256
|
||||
|
||||
// MSC Buffer size of Device Mass storage
|
||||
#define CFG_TUD_MSC_BUFSIZE 512
|
||||
|
||||
// HID buffer size Should be sufficient to hold ID (if any) + Data
|
||||
#define CFG_TUD_HID_BUFSIZE 64
|
||||
|
||||
// MIDI FIFO size of TX and RX
|
||||
#define CFG_TUD_MIDI_RX_BUFSIZE 128
|
||||
#define CFG_TUD_MIDI_TX_BUFSIZE 128
|
||||
|
||||
// Vendor FIFO size of TX and RX
|
||||
#define CFG_TUD_VENDOR_RX_BUFSIZE 64
|
||||
#define CFG_TUD_VENDOR_TX_BUFSIZE 64
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_CONFIG_H_ */
|
||||
+46
-23
@@ -28,16 +28,24 @@ typedef struct {
|
||||
pin_size_t pin;
|
||||
PIO pio;
|
||||
int sm;
|
||||
alarm_id_t alarm;
|
||||
} Tone;
|
||||
|
||||
// Keep std::map safe for multicore use
|
||||
auto_init_mutex(_toneMutex);
|
||||
|
||||
|
||||
#include "tone.pio.h"
|
||||
static PIOProgram _tonePgm(&tone_program);
|
||||
#include "tone2.pio.h"
|
||||
static PIOProgram _tone2Pgm(&tone2_program);
|
||||
static std::map<pin_size_t, Tone *> _toneMap;
|
||||
|
||||
int64_t _stopTonePIO(alarm_id_t id, void *user_data) {
|
||||
(void) id;
|
||||
Tone *tone = (Tone *)user_data;
|
||||
tone->alarm = 0;
|
||||
pio_sm_set_enabled(tone->pio, tone->sm, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tone(uint8_t pin, unsigned int frequency, unsigned long duration) {
|
||||
if (pin > 29) {
|
||||
DEBUGCORE("ERROR: Illegal pin in tone (%d)\n", pin);
|
||||
@@ -58,32 +66,43 @@ void tone(uint8_t pin, unsigned int frequency, unsigned long duration) {
|
||||
if (us < 5) {
|
||||
us = 5;
|
||||
}
|
||||
// Even phases run forever, odd phases end after count...so ensure its odd
|
||||
int phases = duration ? (duration * 1000 / us) | 1 : 2;
|
||||
auto entry = _toneMap.find(pin);
|
||||
if (entry != _toneMap.end()) {
|
||||
noTone(pin);
|
||||
Tone *newTone;
|
||||
if (entry == _toneMap.end()) {
|
||||
newTone = new Tone();
|
||||
newTone->pin = pin;
|
||||
pinMode(pin, OUTPUT);
|
||||
int off;
|
||||
if (!_tone2Pgm.prepare(&newTone->pio, &newTone->sm, &off)) {
|
||||
DEBUGCORE("ERROR: tone unable to start, out of PIO resources\n");
|
||||
// ERROR, no free slots
|
||||
delete newTone;
|
||||
return;
|
||||
}
|
||||
tone2_program_init(newTone->pio, newTone->sm, off, pin);
|
||||
newTone->alarm = 0;
|
||||
} else {
|
||||
newTone = entry->second;
|
||||
if (newTone->alarm) {
|
||||
cancel_alarm(newTone->alarm);
|
||||
newTone->alarm = 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto newTone = new Tone();
|
||||
newTone->pin = pin;
|
||||
pinMode(pin, OUTPUT);
|
||||
int off;
|
||||
if (!_tonePgm.prepare(&newTone->pio, &newTone->sm, &off)) {
|
||||
DEBUGCORE("ERROR: tone unable to start, out of PIO resources\n");
|
||||
// ERROR, no free slots
|
||||
delete newTone;
|
||||
return;
|
||||
}
|
||||
tone_program_init(newTone->pio, newTone->sm, off, pin);
|
||||
pio_sm_set_enabled(newTone->pio, newTone->sm, false);
|
||||
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_out(pio_isr, 32));
|
||||
pio_sm_set_enabled(newTone->pio, newTone->sm, true);
|
||||
pio_sm_put_blocking(newTone->pio, newTone->sm, phases);
|
||||
|
||||
_toneMap.insert({pin, newTone});
|
||||
|
||||
if (duration) {
|
||||
auto ret = add_alarm_in_ms(duration, _stopTonePIO, (void *)newTone, true);
|
||||
if (ret > 0) {
|
||||
newTone->alarm = ret;
|
||||
} else {
|
||||
DEBUGCORE("ERROR: Unable to allocate timer for tone(%d, %d, %d)\n",
|
||||
pin, frequency, duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void noTone(uint8_t pin) {
|
||||
@@ -95,6 +114,10 @@ void noTone(uint8_t pin) {
|
||||
}
|
||||
auto entry = _toneMap.find(pin);
|
||||
if (entry != _toneMap.end()) {
|
||||
if (entry->second->alarm) {
|
||||
cancel_alarm(entry->second->alarm);
|
||||
entry->second->alarm = 0;
|
||||
}
|
||||
pio_sm_set_enabled(entry->second->pio, entry->second->sm, false);
|
||||
pio_sm_unclaim(entry->second->pio, entry->second->sm);
|
||||
delete entry->second;
|
||||
|
||||
@@ -20,6 +20,10 @@ typedef enum {
|
||||
OUTPUT = 0x1,
|
||||
INPUT_PULLUP = 0x2,
|
||||
INPUT_PULLDOWN = 0x3,
|
||||
OUTPUT_2MA = 0x4,
|
||||
OUTPUT_4MA = 0x5,
|
||||
OUTPUT_8MA = 0x6,
|
||||
OUTPUT_12MA = 0x7,
|
||||
} PinMode;
|
||||
|
||||
typedef enum {
|
||||
|
||||
@@ -110,11 +110,23 @@ typedef const void* uint_farptr_t;
|
||||
#define pgm_read_ptr(addr) (*(void *const *)(addr))
|
||||
#else
|
||||
// Supports misaligned addresses
|
||||
#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
|
||||
#define pgm_read_word(addr) ((unsigned short)(pgm_read_byte((intptr_t)addr) | (pgm_read_byte((intptr_t)addr + 1) << 8)))
|
||||
#define pgm_read_dword(addr) ((unsigned long)(pgm_read_byte((intptr_t)addr) | (pgm_read_byte((intptr_t)addr + 1) << 8)) | \
|
||||
(pgm_read_byte((intptr_t)addr + 2) << 16) | (pgm_read_byte((intptr_t)addr + 3) << 24))
|
||||
#define pgm_read_ptr(addr) ((void *)pgm_read_dword(addr))
|
||||
#ifdef __cplusplus
|
||||
extern "C"{
|
||||
#endif
|
||||
static inline unsigned char pgm_read_byte(const void *addr) {
|
||||
return *(const unsigned char *)(addr);
|
||||
}
|
||||
static inline unsigned short pgm_read_word(const void *addr) {
|
||||
const unsigned char *a = (const unsigned char *)addr;
|
||||
return pgm_read_byte(a) | ( pgm_read_byte(a + 1) << 8 );
|
||||
}
|
||||
static inline unsigned long pgm_read_dword(const void *addr) {
|
||||
const unsigned char *a = (const unsigned char *)addr;
|
||||
return pgm_read_byte(a) | ( pgm_read_byte(a + 1) << 8 ) | ( pgm_read_byte(a + 2) << 16 ) | ( pgm_read_byte(a + 3) << 24 );
|
||||
}
|
||||
static inline void *pgm_read_ptr(const void *addr) {
|
||||
return (void*) pgm_read_dword(addr);
|
||||
}
|
||||
static inline float pgm_read_float(const void *addr) {
|
||||
union {
|
||||
void *p;
|
||||
@@ -123,6 +135,10 @@ static inline float pgm_read_float(const void *addr) {
|
||||
x.p = pgm_read_ptr(addr);
|
||||
return x.f;
|
||||
}
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#define pgm_read_byte_near(addr) pgm_read_byte(addr)
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
; Cycle Count for the Raspberry Pi Pico RP2040
|
||||
;
|
||||
; Copyright (c) 2022 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
;
|
||||
; This library is free software; you can redistribute it and/or
|
||||
; modify it under the terms of the GNU Lesser General Public
|
||||
; License as published by the Free Software Foundation; either
|
||||
; version 2.1 of the License, or (at your option) any later version.
|
||||
;
|
||||
; This library is distributed in the hope that it will be useful,
|
||||
; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
; Lesser General Public License for more details.
|
||||
;
|
||||
; You should have received a copy of the GNU Lesser General Public
|
||||
; License along with this library; if not, write to the Free Software
|
||||
; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
; Cycle count is stored in {-y, -x}
|
||||
|
||||
.program ccount
|
||||
cnt:
|
||||
jmp x-- cnt
|
||||
epoch:
|
||||
jmp y-- cnt
|
||||
|
||||
% c-sdk {
|
||||
static inline void ccount_program_init(PIO pio, uint sm, uint offset) {
|
||||
pio_sm_config c = ccount_program_get_default_config(offset);
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
pio_sm_exec(pio, sm, pio_encode_set(pio_x, 0));
|
||||
pio_sm_exec(pio, sm, pio_encode_set(pio_y, 0));
|
||||
}
|
||||
|
||||
static inline uint64_t ccount_read(PIO pio, uint sm) {
|
||||
static uint64_t extra = 0;
|
||||
// Guard against having the epoch rollover while we're reading the time.
|
||||
// If epoch2 != epoch1, we looped in middle and get new LSW
|
||||
pio_sm_exec(pio, sm, pio_encode_mov(pio_isr, pio_y));
|
||||
pio_sm_exec(pio, sm, pio_encode_push(false, false));
|
||||
pio_sm_exec(pio, sm, pio_encode_mov(pio_isr, pio_x));
|
||||
pio_sm_exec(pio, sm, pio_encode_push(false, false));
|
||||
pio_sm_exec(pio, sm, pio_encode_mov(pio_isr, pio_y));
|
||||
pio_sm_exec(pio, sm, pio_encode_push(false, false));
|
||||
pio_sm_exec(pio, sm, pio_encode_mov(pio_isr, pio_x));
|
||||
pio_sm_exec(pio, sm, pio_encode_push(false, false));
|
||||
extra += 8;
|
||||
uint32_t y1 = -((int)pio_sm_get_blocking(pio, sm));
|
||||
uint32_t x1 = -((int)pio_sm_get_blocking(pio, sm));
|
||||
uint32_t y2 = -((int)pio_sm_get_blocking(pio, sm));
|
||||
uint32_t x2 = -((int)pio_sm_get_blocking(pio, sm));
|
||||
uint64_t val;
|
||||
if (y2 != y1) {
|
||||
val = ((((uint64_t)y2) << 32LL) | x2) + y2 /* adjust for missed cycle every epoch increment */;
|
||||
} else {
|
||||
val = ((((uint64_t)y1) << 32LL) | x1) + y1 /* adjust for missed cycle every epoch increment */;
|
||||
}
|
||||
return val + extra;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
// -------------------------------------------------- //
|
||||
// This file is autogenerated by pioasm; do not edit! //
|
||||
// -------------------------------------------------- //
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
#include "hardware/pio.h"
|
||||
#endif
|
||||
|
||||
// ------ //
|
||||
// ccount //
|
||||
// ------ //
|
||||
|
||||
#define ccount_wrap_target 0
|
||||
#define ccount_wrap 1
|
||||
|
||||
static const uint16_t ccount_program_instructions[] = {
|
||||
// .wrap_target
|
||||
0x0040, // 0: jmp x--, 0
|
||||
0x0080, // 1: jmp y--, 0
|
||||
// .wrap
|
||||
};
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
static const struct pio_program ccount_program = {
|
||||
.instructions = ccount_program_instructions,
|
||||
.length = 2,
|
||||
.origin = -1,
|
||||
};
|
||||
|
||||
static inline pio_sm_config ccount_program_get_default_config(uint offset) {
|
||||
pio_sm_config c = pio_get_default_sm_config();
|
||||
sm_config_set_wrap(&c, offset + ccount_wrap_target, offset + ccount_wrap);
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline void ccount_program_init(PIO pio, uint sm, uint offset) {
|
||||
pio_sm_config c = ccount_program_get_default_config(offset);
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
pio_sm_exec(pio, sm, pio_encode_set(pio_x, 0));
|
||||
pio_sm_exec(pio, sm, pio_encode_set(pio_y, 0));
|
||||
}
|
||||
static inline uint64_t ccount_read(PIO pio, uint sm) {
|
||||
static uint64_t extra = 0;
|
||||
// Guard against having the epoch rollover while we're reading the time.
|
||||
// If epoch2 != epoch1, we looped in middle and get new LSW
|
||||
pio_sm_exec(pio, sm, pio_encode_mov(pio_isr, pio_y));
|
||||
pio_sm_exec(pio, sm, pio_encode_push(false, false));
|
||||
pio_sm_exec(pio, sm, pio_encode_mov(pio_isr, pio_x));
|
||||
pio_sm_exec(pio, sm, pio_encode_push(false, false));
|
||||
pio_sm_exec(pio, sm, pio_encode_mov(pio_isr, pio_y));
|
||||
pio_sm_exec(pio, sm, pio_encode_push(false, false));
|
||||
pio_sm_exec(pio, sm, pio_encode_mov(pio_isr, pio_x));
|
||||
pio_sm_exec(pio, sm, pio_encode_push(false, false));
|
||||
extra += 8;
|
||||
uint32_t y1 = -((int)pio_sm_get_blocking(pio, sm));
|
||||
uint32_t x1 = -((int)pio_sm_get_blocking(pio, sm));
|
||||
uint32_t y2 = -((int)pio_sm_get_blocking(pio, sm));
|
||||
uint32_t x2 = -((int)pio_sm_get_blocking(pio, sm));
|
||||
uint64_t val;
|
||||
if (y2 != y1) {
|
||||
val = ((((uint64_t)y2) << 32LL) | x2) + y2 /* adjust for missed cycle every epoch increment */;
|
||||
} else {
|
||||
val = ((((uint64_t)y1) << 32LL) | x1) + y1 /* adjust for missed cycle every epoch increment */;
|
||||
}
|
||||
return val + extra;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
#include "Adafruit_TinyUSB_API.h"
|
||||
#endif
|
||||
|
||||
extern "C" void delay(unsigned long ms) __attribute__((weak));
|
||||
extern "C" void yield() __attribute__((weak));
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
Newlib retargetable lock class using RP2040 SDK
|
||||
|
||||
Overrides weak functions in Newlib for locking to safely support
|
||||
multi-core operation. Does not need any --wrap for memory allocators.
|
||||
|
||||
Copyright (c) 2022 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
|
||||
#include <pico/mutex.h>
|
||||
#include <sys/lock.h>
|
||||
|
||||
// HACK ALERT
|
||||
// Pico-SDK defines mutex which can be at global scope, but when the auto_init_
|
||||
// macros are used they are defined as static. Newlib needs global access to
|
||||
// these mutextes, so instead of hacking pico-sdk just make "static" a no-op.
|
||||
|
||||
#define static
|
||||
auto_init_recursive_mutex(__lock___sinit_recursive_mutex);
|
||||
auto_init_recursive_mutex(__lock___sfp_recursive_mutex);
|
||||
auto_init_recursive_mutex(__lock___atexit_recursive_mutex);
|
||||
auto_init_mutex(__lock___at_quick_exit_mutex);
|
||||
auto_init_recursive_mutex(__lock___malloc_recursive_mutex);
|
||||
auto_init_recursive_mutex(__lock___env_recursive_mutex);
|
||||
auto_init_mutex(__lock___tz_mutex);
|
||||
auto_init_mutex(__lock___dd_hash_mutex);
|
||||
auto_init_mutex(__lock___arc4random_mutex);
|
||||
#undef static
|
||||
|
||||
void __retarget_lock_init(_LOCK_T *lock) {
|
||||
mutex_init((mutex_t*) lock);
|
||||
}
|
||||
|
||||
void __retarget_lock_init_recursive(_LOCK_T *lock) {
|
||||
recursive_mutex_init((recursive_mutex_t*) lock);
|
||||
}
|
||||
|
||||
void __retarget_lock_close(_LOCK_T lock) {
|
||||
(void) lock;
|
||||
}
|
||||
|
||||
void __retarget_lock_close_recursive(_LOCK_T lock) {
|
||||
(void) lock;
|
||||
}
|
||||
|
||||
void __retarget_lock_acquire(_LOCK_T lock) {
|
||||
mutex_enter_blocking((mutex_t*)lock);
|
||||
}
|
||||
|
||||
void __retarget_lock_acquire_recursive(_LOCK_T lock) {
|
||||
recursive_mutex_enter_blocking((recursive_mutex_t*)lock);
|
||||
}
|
||||
|
||||
int __retarget_lock_try_acquire(_LOCK_T lock) {
|
||||
return mutex_try_enter((mutex_t *)lock, NULL);
|
||||
}
|
||||
|
||||
int __retarget_lock_try_acquire_recursive(_LOCK_T lock) {
|
||||
return recursive_mutex_try_enter((recursive_mutex_t*)lock, NULL);
|
||||
}
|
||||
|
||||
void __retarget_lock_release(_LOCK_T lock) {
|
||||
mutex_exit((mutex_t*)lock);
|
||||
}
|
||||
|
||||
void __retarget_lock_release_recursive(_LOCK_T lock) {
|
||||
recursive_mutex_exit((recursive_mutex_t*)lock);
|
||||
}
|
||||
+92
-29
@@ -22,26 +22,31 @@
|
||||
#include "RP2040USB.h"
|
||||
#include <pico/stdlib.h>
|
||||
#include <pico/multicore.h>
|
||||
#include <reent.h>
|
||||
|
||||
RP2040 rp2040;
|
||||
extern "C" {
|
||||
volatile bool __otherCoreIdled = false;
|
||||
};
|
||||
|
||||
volatile bool _MFIFO::_otherIdled = false;
|
||||
mutex_t _pioMutex;
|
||||
|
||||
|
||||
extern void setup();
|
||||
extern void loop();
|
||||
|
||||
// FreeRTOS potential includes
|
||||
extern void initFreeRTOS() __attribute__((weak));
|
||||
extern void startFreeRTOS() __attribute__((weak));
|
||||
bool __isFreeRTOS;
|
||||
|
||||
// Weak empty variant initialization. May be redefined by variant files.
|
||||
void initVariant() __attribute__((weak));
|
||||
void initVariant() { }
|
||||
|
||||
|
||||
// Optional 2nd core setup and loop
|
||||
extern void setup1() __attribute__((weak));
|
||||
extern void loop1() __attribute__((weak));
|
||||
static void main1() {
|
||||
extern "C" void main1() {
|
||||
rp2040.fifo.registerCore();
|
||||
if (setup1) {
|
||||
setup1();
|
||||
@@ -53,14 +58,48 @@ static void main1() {
|
||||
}
|
||||
}
|
||||
|
||||
extern void __loop() {
|
||||
#ifdef USE_TINYUSB
|
||||
yield();
|
||||
#endif
|
||||
|
||||
if (arduino::serialEventRun) {
|
||||
arduino::serialEventRun();
|
||||
}
|
||||
if (arduino::serialEvent1Run) {
|
||||
arduino::serialEvent1Run();
|
||||
}
|
||||
if (arduino::serialEvent2Run) {
|
||||
arduino::serialEvent2Run();
|
||||
}
|
||||
}
|
||||
static struct _reent *_impure_ptr1 = nullptr;
|
||||
|
||||
extern "C" int main() {
|
||||
#if F_CPU != 125000000
|
||||
set_sys_clock_khz(F_CPU / 1000, true);
|
||||
#endif
|
||||
|
||||
// Let rest of core know if we're using FreeRTOS
|
||||
__isFreeRTOS = initFreeRTOS ? true : false;
|
||||
|
||||
// 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);
|
||||
_REENT_INIT_PTR(_impure_ptr1);
|
||||
}
|
||||
|
||||
mutex_init(&_pioMutex);
|
||||
|
||||
rp2040.begin();
|
||||
|
||||
initVariant();
|
||||
|
||||
if (__isFreeRTOS) {
|
||||
initFreeRTOS();
|
||||
}
|
||||
|
||||
#ifndef NO_USB
|
||||
#ifdef USE_TINYUSB
|
||||
TinyUSB_Device_Init(0);
|
||||
|
||||
@@ -68,40 +107,64 @@ extern "C" int main() {
|
||||
__USBStart();
|
||||
|
||||
#ifndef DISABLE_USB_SERIAL
|
||||
// Enable serial port for reset/upload always
|
||||
Serial.begin(115200);
|
||||
|
||||
if (!__isFreeRTOS) {
|
||||
// Enable serial port for reset/upload always
|
||||
Serial.begin(115200);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined DEBUG_RP2040_PORT
|
||||
DEBUG_RP2040_PORT.begin();
|
||||
#endif
|
||||
|
||||
if (setup1 || loop1) {
|
||||
rp2040.fifo.begin(2);
|
||||
multicore_launch_core1(main1);
|
||||
} else {
|
||||
rp2040.fifo.begin(1);
|
||||
if (!__isFreeRTOS) {
|
||||
DEBUG_RP2040_PORT.begin(115200);
|
||||
}
|
||||
rp2040.fifo.registerCore();
|
||||
|
||||
setup();
|
||||
while (true) {
|
||||
loop();
|
||||
|
||||
#ifdef USE_TINYUSB
|
||||
yield();
|
||||
#endif
|
||||
|
||||
if (arduino::serialEventRun) {
|
||||
arduino::serialEventRun();
|
||||
#ifndef NO_USB
|
||||
if (!__isFreeRTOS) {
|
||||
if (setup1 || loop1) {
|
||||
rp2040.fifo.begin(2);
|
||||
multicore_launch_core1(main1);
|
||||
} else {
|
||||
rp2040.fifo.begin(1);
|
||||
}
|
||||
if (arduino::serialEvent1Run) {
|
||||
arduino::serialEvent1Run();
|
||||
}
|
||||
if (arduino::serialEvent2Run) {
|
||||
arduino::serialEvent2Run();
|
||||
rp2040.fifo.registerCore();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!__isFreeRTOS) {
|
||||
setup();
|
||||
while (true) {
|
||||
loop();
|
||||
__loop();
|
||||
}
|
||||
} else {
|
||||
rp2040.fifo.begin(2);
|
||||
startFreeRTOS();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" unsigned long ulMainGetRunTimeCounterValue() {
|
||||
return rp2040.getCycleCount64();
|
||||
}
|
||||
|
||||
extern "C" void __register_impure_ptr(struct _reent *p) {
|
||||
if (get_core_num() == 0) {
|
||||
_impure_ptr = p;
|
||||
} else {
|
||||
_impure_ptr1 = p;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: FreeRTOS should implement this based on thread ID (and each thread should have its own struct _reent
|
||||
extern "C" struct _reent *__wrap___getreent() {
|
||||
if (get_core_num() == 0) {
|
||||
return _impure_ptr;
|
||||
} else {
|
||||
return _impure_ptr1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
; pio_uart for the Raspberry Pi Pico RP2040
|
||||
;
|
||||
; Based loosely off of the uart_rx/_tx examples in the pico-examples repo,
|
||||
; but heavily modified and optimized to not require changing the PIO
|
||||
; clocks so that Tone and Servo won't be affected, and multiple different
|
||||
; PIO ports can be run at different baud at the same time.
|
||||
;
|
||||
; Copyright (c) 2021 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
;
|
||||
; This library is free software; you can redistribute it and/or
|
||||
; modify it under the terms of the GNU Lesser General Public
|
||||
; License as published by the Free Software Foundation; either
|
||||
; version 2.1 of the License, or (at your option) any later version.
|
||||
;
|
||||
; This library is distributed in the hope that it will be useful,
|
||||
; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
; Lesser General Public License for more details.
|
||||
;
|
||||
; You should have received a copy of the GNU Lesser General Public
|
||||
; License along with this library; if not, write to the Free Software
|
||||
; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
|
||||
|
||||
.program pio_tx
|
||||
.side_set 1 opt
|
||||
|
||||
; We shift out the start and stop bit as part of the FIFO
|
||||
set x, 9
|
||||
|
||||
pull side 1 ; Force stop bit
|
||||
|
||||
; Send the bits
|
||||
bitloop:
|
||||
out pins, 1
|
||||
mov y, isr ; ISR is loaded by the setup routine with the period-1 count
|
||||
wait_bit:
|
||||
jmp y-- wait_bit
|
||||
jmp x-- bitloop
|
||||
|
||||
|
||||
|
||||
% c-sdk {
|
||||
|
||||
static inline void pio_tx_program_init(PIO pio, uint sm, uint offset, uint pin_tx) {
|
||||
// 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_gpio_init(pio, pin_tx);
|
||||
|
||||
pio_sm_config c = pio_tx_program_get_default_config(offset);
|
||||
|
||||
// OUT shifts to right, no autopull
|
||||
sm_config_set_out_shift(&c, true, false, 32);
|
||||
|
||||
// We are mapping both OUT and side-set to the same pin, because sometimes
|
||||
// we need to assert user data onto the pin (with OUT) and sometimes
|
||||
// assert constant values (start/stop bit)
|
||||
sm_config_set_out_pins(&c, pin_tx, 1);
|
||||
sm_config_set_sideset_pins(&c, pin_tx);
|
||||
|
||||
// We only need TX, so get an 8-deep FIFO!
|
||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
|
||||
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
|
||||
.program pio_rx
|
||||
|
||||
; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX.
|
||||
|
||||
start:
|
||||
set x, 18 ; Preload bit counter...we'll shift in the start bit and stop bit, and each bit will be double-recorded (to be fixed by RP2040 code)
|
||||
wait 0 pin 0 ; Stall until start bit is asserted
|
||||
|
||||
bitloop:
|
||||
; Delay until 1/2 way into the bit time
|
||||
mov y, osr
|
||||
wait_half:
|
||||
jmp y-- wait_half
|
||||
|
||||
; Read in the bit
|
||||
in pins, 1 ; Shift data bit into ISR
|
||||
jmp x-- bitloop ; Loop all bits
|
||||
|
||||
push ; Stuff it and wait for next start
|
||||
|
||||
|
||||
% c-sdk {
|
||||
static inline void pio_rx_program_init(PIO pio, uint sm, uint offset, uint pin) {
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false);
|
||||
pio_gpio_init(pio, pin);
|
||||
gpio_pull_up(pin);
|
||||
|
||||
pio_sm_config c = pio_rx_program_get_default_config(offset);
|
||||
sm_config_set_in_pins(&c, pin); // for WAIT, IN
|
||||
sm_config_set_jmp_pin(&c, pin); // for JMP
|
||||
// Shift to right, autopull disabled
|
||||
sm_config_set_in_shift(&c, true, false, 32);
|
||||
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
}
|
||||
|
||||
%}
|
||||
@@ -0,0 +1,107 @@
|
||||
// -------------------------------------------------- //
|
||||
// This file is autogenerated by pioasm; do not edit! //
|
||||
// -------------------------------------------------- //
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
#include "hardware/pio.h"
|
||||
#endif
|
||||
|
||||
// ------ //
|
||||
// pio_tx //
|
||||
// ------ //
|
||||
|
||||
#define pio_tx_wrap_target 0
|
||||
#define pio_tx_wrap 5
|
||||
|
||||
static const uint16_t pio_tx_program_instructions[] = {
|
||||
// .wrap_target
|
||||
0xe029, // 0: set x, 9
|
||||
0x98a0, // 1: pull block side 1
|
||||
0x6001, // 2: out pins, 1
|
||||
0xa046, // 3: mov y, isr
|
||||
0x0084, // 4: jmp y--, 4
|
||||
0x0042, // 5: jmp x--, 2
|
||||
// .wrap
|
||||
};
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
static const struct pio_program pio_tx_program = {
|
||||
.instructions = pio_tx_program_instructions,
|
||||
.length = 6,
|
||||
.origin = -1,
|
||||
};
|
||||
|
||||
static inline pio_sm_config pio_tx_program_get_default_config(uint offset) {
|
||||
pio_sm_config c = pio_get_default_sm_config();
|
||||
sm_config_set_wrap(&c, offset + pio_tx_wrap_target, offset + pio_tx_wrap);
|
||||
sm_config_set_sideset(&c, 2, true, false);
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline void pio_tx_program_init(PIO pio, uint sm, uint offset, uint pin_tx) {
|
||||
// Tell PIO to initially drive output-high on the selected pin, then map PIO
|
||||
// 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_gpio_init(pio, pin_tx);
|
||||
pio_sm_config c = pio_tx_program_get_default_config(offset);
|
||||
// OUT shifts to right, no autopull
|
||||
sm_config_set_out_shift(&c, true, false, 32);
|
||||
// We are mapping both OUT and side-set to the same pin, because sometimes
|
||||
// we need to assert user data onto the pin (with OUT) and sometimes
|
||||
// assert constant values (start/stop bit)
|
||||
sm_config_set_out_pins(&c, pin_tx, 1);
|
||||
sm_config_set_sideset_pins(&c, pin_tx);
|
||||
// We only need TX, so get an 8-deep FIFO!
|
||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// ------ //
|
||||
// pio_rx //
|
||||
// ------ //
|
||||
|
||||
#define pio_rx_wrap_target 0
|
||||
#define pio_rx_wrap 6
|
||||
|
||||
static const uint16_t pio_rx_program_instructions[] = {
|
||||
// .wrap_target
|
||||
0xe032, // 0: set x, 18
|
||||
0x2020, // 1: wait 0 pin, 0
|
||||
0xa047, // 2: mov y, osr
|
||||
0x0083, // 3: jmp y--, 3
|
||||
0x4001, // 4: in pins, 1
|
||||
0x0042, // 5: jmp x--, 2
|
||||
0x8020, // 6: push block
|
||||
// .wrap
|
||||
};
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
static const struct pio_program pio_rx_program = {
|
||||
.instructions = pio_rx_program_instructions,
|
||||
.length = 7,
|
||||
.origin = -1,
|
||||
};
|
||||
|
||||
static inline pio_sm_config pio_rx_program_get_default_config(uint offset) {
|
||||
pio_sm_config c = pio_get_default_sm_config();
|
||||
sm_config_set_wrap(&c, offset + pio_rx_wrap_target, offset + pio_rx_wrap);
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline void pio_rx_program_init(PIO pio, uint sm, uint offset, uint pin) {
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false);
|
||||
pio_gpio_init(pio, pin);
|
||||
gpio_pull_up(pin);
|
||||
pio_sm_config c = pio_rx_program_get_default_config(offset);
|
||||
sm_config_set_in_pins(&c, pin); // for WAIT, IN
|
||||
sm_config_set_jmp_pin(&c, pin); // for JMP
|
||||
// Shift to right, autopull disabled
|
||||
sm_config_set_in_shift(&c, true, false, 32);
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
// -------------------------------------------------- //
|
||||
// This file is autogenerated by pioasm; do not edit! //
|
||||
// -------------------------------------------------- //
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
#include "hardware/pio.h"
|
||||
#endif
|
||||
|
||||
// ---- //
|
||||
// tone //
|
||||
// ---- //
|
||||
|
||||
#define tone_wrap_target 0
|
||||
#define tone_wrap 7
|
||||
|
||||
static const uint16_t tone_program_instructions[] = {
|
||||
// .wrap_target
|
||||
0x80a0, // 0: pull block
|
||||
0xa027, // 1: mov x, osr
|
||||
0xb846, // 2: mov y, isr side 1
|
||||
0x0083, // 3: jmp y--, 3
|
||||
0x0045, // 4: jmp x--, 5
|
||||
0xb046, // 5: mov y, isr side 0
|
||||
0x0086, // 6: jmp y--, 6
|
||||
0x0042, // 7: jmp x--, 2
|
||||
// .wrap
|
||||
};
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
static const struct pio_program tone_program = {
|
||||
.instructions = tone_program_instructions,
|
||||
.length = 8,
|
||||
.origin = -1,
|
||||
};
|
||||
|
||||
static inline pio_sm_config tone_program_get_default_config(uint offset) {
|
||||
pio_sm_config c = pio_get_default_sm_config();
|
||||
sm_config_set_wrap(&c, offset + tone_wrap_target, offset + tone_wrap);
|
||||
sm_config_set_sideset(&c, 2, true, false);
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline void tone_program_init(PIO pio, uint sm, uint offset, uint pin) {
|
||||
pio_gpio_init(pio, pin);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
|
||||
pio_sm_config c = tone_program_get_default_config(offset);
|
||||
sm_config_set_sideset_pins(&c, pin);
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
; Tone for the Raspberry Pi Pico RP2040
|
||||
; Tone2 for the Raspberry Pi Pico RP2040
|
||||
;
|
||||
; Copyright (c) 2021 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
;
|
||||
@@ -18,32 +18,35 @@
|
||||
|
||||
; Side-set pin 0 is used for Tone output
|
||||
|
||||
.program tone
|
||||
; OSR == Halfcycle count
|
||||
|
||||
.program tone2
|
||||
.side_set 1 opt
|
||||
|
||||
pull
|
||||
mov x, osr
|
||||
pull ; TXFIFO -> OSR, or X -> OSR if no new period
|
||||
mov x, osr ; OSR -> X
|
||||
|
||||
high:
|
||||
mov y, isr side 1
|
||||
pull noblock ; Potentially grab new HALFCYCLECOUNT, OTW copy from backup in X
|
||||
mov x, osr ; OSR -> X
|
||||
mov y, osr side 1 ; HALFCYCLECOUNT -> Y
|
||||
highloop:
|
||||
jmp y-- highloop
|
||||
|
||||
jmp x-- low
|
||||
jmp y-- highloop ; while (y--) { /* noop delay */ }
|
||||
|
||||
low:
|
||||
mov y, isr side 0
|
||||
mov y, osr side 0 ; HALFCYCLECOUNT -> Y
|
||||
lowloop:
|
||||
jmp y-- lowloop
|
||||
jmp y-- lowloop ; while (y--) { /* noop delay */ }
|
||||
|
||||
jmp x-- high
|
||||
jmp high ; GOTO high
|
||||
|
||||
% c-sdk {
|
||||
static inline void tone_program_init(PIO pio, uint sm, uint offset, uint pin) {
|
||||
static inline void tone2_program_init(PIO pio, uint sm, uint offset, uint pin) {
|
||||
pio_gpio_init(pio, pin);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
|
||||
pio_sm_config c = tone_program_get_default_config(offset);
|
||||
pio_sm_config c = tone2_program_get_default_config(offset);
|
||||
sm_config_set_sideset_pins(&c, pin);
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
}
|
||||
%}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// -------------------------------------------------- //
|
||||
// This file is autogenerated by pioasm; do not edit! //
|
||||
// -------------------------------------------------- //
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
#include "hardware/pio.h"
|
||||
#endif
|
||||
|
||||
// ----- //
|
||||
// tone2 //
|
||||
// ----- //
|
||||
|
||||
#define tone2_wrap_target 0
|
||||
#define tone2_wrap 8
|
||||
|
||||
static const uint16_t tone2_program_instructions[] = {
|
||||
// .wrap_target
|
||||
0x80a0, // 0: pull block
|
||||
0xa027, // 1: mov x, osr
|
||||
0x8080, // 2: pull noblock
|
||||
0xa027, // 3: mov x, osr
|
||||
0xb847, // 4: mov y, osr side 1
|
||||
0x0085, // 5: jmp y--, 5
|
||||
0xb047, // 6: mov y, osr side 0
|
||||
0x0087, // 7: jmp y--, 7
|
||||
0x0002, // 8: jmp 2
|
||||
// .wrap
|
||||
};
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
static const struct pio_program tone2_program = {
|
||||
.instructions = tone2_program_instructions,
|
||||
.length = 9,
|
||||
.origin = -1,
|
||||
};
|
||||
|
||||
static inline pio_sm_config tone2_program_get_default_config(uint offset) {
|
||||
pio_sm_config c = pio_get_default_sm_config();
|
||||
sm_config_set_wrap(&c, offset + tone2_wrap_target, offset + tone2_wrap);
|
||||
sm_config_set_sideset(&c, 2, true, false);
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline void tone2_program_init(PIO pio, uint sm, uint offset, uint pin) {
|
||||
pio_gpio_init(pio, pin);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
|
||||
pio_sm_config c = tone2_program_get_default_config(offset);
|
||||
sm_config_set_sideset_pins(&c, pin);
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -27,9 +27,11 @@
|
||||
#include <hardware/adc.h>
|
||||
|
||||
static uint32_t analogScale = 255;
|
||||
static uint16_t analogFreq = 1000;
|
||||
static uint32_t analogFreq = 1000;
|
||||
static bool pwmInitted = false;
|
||||
static bool adcInitted = false;
|
||||
static uint16_t analogWritePseudoScale = 1;
|
||||
static uint16_t analogWriteSlowScale = 1;
|
||||
|
||||
auto_init_mutex(_dacMutex);
|
||||
|
||||
@@ -40,9 +42,9 @@ extern "C" void analogWriteFreq(uint32_t freq) {
|
||||
if (freq < 100) {
|
||||
DEBUGCORE("ERROR: analogWriteFreq too low (%d)\n", freq);
|
||||
analogFreq = 100;
|
||||
} else if (freq > 60000) {
|
||||
} else if (freq > 1000000) {
|
||||
DEBUGCORE("ERROR: analogWriteFreq too high (%d)\n", freq);
|
||||
analogFreq = 60000;
|
||||
analogFreq = 1000000;
|
||||
} else {
|
||||
analogFreq = freq;
|
||||
}
|
||||
@@ -77,6 +79,21 @@ extern "C" void analogWrite(pin_size_t pin, int val) {
|
||||
return;
|
||||
}
|
||||
if (!pwmInitted) {
|
||||
// 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);
|
||||
}
|
||||
// For high frequencies, we need to scale the output max value down to actually hit the frequency target
|
||||
analogWriteSlowScale = 1;
|
||||
while (((clock_get_hz(clk_sys) / (float)(analogScale * analogFreq)) < 2.0) && (analogScale > 32)) {
|
||||
analogWriteSlowScale++;
|
||||
analogScale /= 2;
|
||||
DEBUGCORE("Adjusting analogWrite values SS=%d, scale=%d\n", analogWriteSlowScale, analogScale);
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -86,6 +103,9 @@ extern "C" void analogWrite(pin_size_t pin, int val) {
|
||||
pwmInitted = true;
|
||||
}
|
||||
|
||||
val <<= analogWritePseudoScale;
|
||||
val >>= analogWriteSlowScale;
|
||||
|
||||
if (val < 0) {
|
||||
val = 0;
|
||||
} else if ((uint32_t)val > analogScale) {
|
||||
@@ -97,11 +117,15 @@ extern "C" void analogWrite(pin_size_t pin, int val) {
|
||||
}
|
||||
|
||||
auto_init_mutex(_adcMutex);
|
||||
static int _readBits = 10;
|
||||
|
||||
extern "C" int analogRead(pin_size_t pin) {
|
||||
CoreMutex m(&_adcMutex);
|
||||
|
||||
if ((pin < A0) || (pin > A3) || !m) {
|
||||
pin_size_t maxPin = max(A0, A3);
|
||||
pin_size_t minPin = min(A0, A3);
|
||||
|
||||
if ((pin < minPin) || (pin > maxPin) || !m) {
|
||||
DEBUGCORE("ERROR: Illegal analogRead pin (%d)\n", pin);
|
||||
return 0;
|
||||
}
|
||||
@@ -109,8 +133,8 @@ extern "C" int analogRead(pin_size_t pin) {
|
||||
adc_init();
|
||||
}
|
||||
adc_gpio_init(pin);
|
||||
adc_select_input(pin - A0);
|
||||
return adc_read();
|
||||
adc_select_input(pin - minPin);
|
||||
return (_readBits < 12) ? adc_read() >> (12 - _readBits) : adc_read() << (_readBits - 12);
|
||||
}
|
||||
|
||||
extern "C" float analogReadTemp() {
|
||||
@@ -130,3 +154,10 @@ extern "C" float analogReadTemp() {
|
||||
float t = 27.0f - ((v * 3.3f / 4096.0f) - 0.706f) / 0.001721f; // From the datasheet
|
||||
return t;
|
||||
}
|
||||
|
||||
extern "C" void analogReadResolution(int bits) {
|
||||
CoreMutex m(&_adcMutex);
|
||||
if (m && ((bits > 0) && (bits < 32))) {
|
||||
_readBits = bits;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ extern "C" void pinMode(pin_size_t ulPin, PinMode ulMode) {
|
||||
case INPUT:
|
||||
gpio_init(ulPin);
|
||||
gpio_set_dir(ulPin, false);
|
||||
gpio_disable_pulls(ulPin);
|
||||
break;
|
||||
case INPUT_PULLUP:
|
||||
gpio_init(ulPin);
|
||||
@@ -42,7 +43,24 @@ extern "C" void pinMode(pin_size_t ulPin, PinMode ulMode) {
|
||||
gpio_put(ulPin, 1);
|
||||
break;
|
||||
case OUTPUT:
|
||||
case OUTPUT_4MA:
|
||||
gpio_init(ulPin);
|
||||
gpio_set_drive_strength(ulPin, GPIO_DRIVE_STRENGTH_4MA);
|
||||
gpio_set_dir(ulPin, true);
|
||||
break;
|
||||
case OUTPUT_2MA:
|
||||
gpio_init(ulPin);
|
||||
gpio_set_drive_strength(ulPin, GPIO_DRIVE_STRENGTH_2MA);
|
||||
gpio_set_dir(ulPin, true);
|
||||
break;
|
||||
case OUTPUT_8MA:
|
||||
gpio_init(ulPin);
|
||||
gpio_set_drive_strength(ulPin, GPIO_DRIVE_STRENGTH_8MA);
|
||||
gpio_set_dir(ulPin, true);
|
||||
break;
|
||||
case OUTPUT_12MA:
|
||||
gpio_init(ulPin);
|
||||
gpio_set_drive_strength(ulPin, GPIO_DRIVE_STRENGTH_12MA);
|
||||
gpio_set_dir(ulPin, true);
|
||||
break;
|
||||
default:
|
||||
@@ -63,6 +81,7 @@ extern "C" void digitalWrite(pin_size_t ulPin, PinStatus ulVal) {
|
||||
DEBUGCORE("ERROR: Illegal pin in pinMode (%d)\n", ulPin);
|
||||
return;
|
||||
}
|
||||
gpio_set_function(ulPin, GPIO_FUNC_SIO);
|
||||
if (_pm[ulPin] == INPUT_PULLDOWN) {
|
||||
if (ulVal == LOW) {
|
||||
gpio_set_dir(ulPin, false);
|
||||
|
||||
@@ -45,26 +45,40 @@ extern "C" void noInterrupts() {
|
||||
// Only 1 GPIO IRQ callback for all pins, so we need to look at the pin it's for and
|
||||
// dispatch to the real callback manually
|
||||
auto_init_mutex(_irqMutex);
|
||||
static std::map<pin_size_t, voidFuncPtr> _map;
|
||||
class CBInfo {
|
||||
public:
|
||||
CBInfo(voidFuncPtr cb) : _cb(cb), _useParam(false), _param(nullptr) { };
|
||||
CBInfo(voidFuncPtrParam cbParam, void *param) : _cbParam(cbParam), _useParam(true), _param(param) { };
|
||||
void callback() {
|
||||
if (_useParam && _cbParam) {
|
||||
_cbParam(_param);
|
||||
} else if (_cb) {
|
||||
_cb();
|
||||
}
|
||||
}
|
||||
private:
|
||||
union {
|
||||
voidFuncPtr _cb;
|
||||
voidFuncPtrParam _cbParam;
|
||||
};
|
||||
bool _useParam;
|
||||
void *_param;
|
||||
};
|
||||
|
||||
|
||||
static std::map<pin_size_t, CBInfo> _map;
|
||||
|
||||
void _gpioInterruptDispatcher(uint gpio, uint32_t events) {
|
||||
(void) events;
|
||||
// Only need to lock around the std::map check, not the whole IRQ callback
|
||||
voidFuncPtr cb = nullptr;
|
||||
{
|
||||
CoreMutex m(&_irqMutex);
|
||||
if (m) {
|
||||
auto irq = _map.find(gpio);
|
||||
if (irq != _map.end()) {
|
||||
cb = irq->second;
|
||||
}
|
||||
CoreMutex m(&_irqMutex);
|
||||
if (m) {
|
||||
auto irq = _map.find(gpio);
|
||||
if (irq != _map.end()) {
|
||||
auto cb = irq->second;
|
||||
cb.callback();
|
||||
}
|
||||
}
|
||||
if (cb) {
|
||||
cb(); // Do the callback
|
||||
} else {
|
||||
// ERROR, but we're in an IRQ so do nothing
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void attachInterrupt(pin_size_t pin, voidFuncPtr callback, PinStatus mode) {
|
||||
@@ -84,7 +98,31 @@ extern "C" void attachInterrupt(pin_size_t pin, voidFuncPtr callback, PinStatus
|
||||
}
|
||||
noInterrupts();
|
||||
detachInterrupt(pin);
|
||||
_map.insert({pin, callback});
|
||||
CBInfo cb(callback);
|
||||
_map.insert({pin, cb});
|
||||
gpio_set_irq_enabled_with_callback(pin, events, true, _gpioInterruptDispatcher);
|
||||
interrupts();
|
||||
}
|
||||
|
||||
void attachInterruptParam(pin_size_t pin, voidFuncPtrParam callback, PinStatus mode, void *param) {
|
||||
CoreMutex m(&_irqMutex);
|
||||
if (!m) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t events;
|
||||
switch (mode) {
|
||||
case LOW: events = 1; break;
|
||||
case HIGH: events = 2; break;
|
||||
case FALLING: events = 4; break;
|
||||
case RISING: events = 8; break;
|
||||
case CHANGE: events = 4 | 8; break;
|
||||
default: return; // ERROR
|
||||
}
|
||||
noInterrupts();
|
||||
detachInterrupt(pin);
|
||||
CBInfo cb(callback, param);
|
||||
_map.insert({pin, cb});
|
||||
gpio_set_irq_enabled_with_callback(pin, events, true, _gpioInterruptDispatcher);
|
||||
interrupts();
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ extern "C" unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeo
|
||||
}
|
||||
|
||||
// Wait for assert
|
||||
while ((!!gpio_get(pin) != !state) && (time_us_64() < abort));
|
||||
while ((!!gpio_get(pin) != !!state) && (time_us_64() < abort));
|
||||
uint64_t begin = time_us_64();
|
||||
if (begin >= abort) {
|
||||
return 0;
|
||||
|
||||
+17
-1
@@ -13,6 +13,11 @@ int analogRead(pin_size_t pin = A0..A3)
|
||||
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()
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns the temperature, in Celsius, of the onboard thermal sensor.
|
||||
@@ -30,10 +35,21 @@ While up to 16 PWM channels can be generated, they are not independent
|
||||
and there are significant restrictions as to allowed pins in parallel.
|
||||
See the `RP2040 datasheet <https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf>`_ for full details.
|
||||
|
||||
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
|
||||
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
|
||||
still send in the range you specify, but the core itself will transparently
|
||||
map it into the allowable PWN range.
|
||||
|
||||
void analogWriteFreq(uint32_t freq)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Sets the master PWM frequency used (i.e. how often the PWM output cycles).
|
||||
From 100Hz to 60KHz are supported.
|
||||
From 100Hz to 1MHz are supported.
|
||||
|
||||
void analogWriteRange(uint32_t range) and analogWriteResolution(int res)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
+4
-4
@@ -19,7 +19,7 @@
|
||||
# import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
import sphinx_rtd_theme
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = []
|
||||
extensions = ['sphinx_rtd_theme']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
@@ -46,7 +46,7 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Arduino-Pico'
|
||||
copyright = u'2021, Earle F. Philhower, III'
|
||||
copyright = u'2022, Earle F. Philhower, III'
|
||||
author = u'Earle F. Philhower, III'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
@@ -82,7 +82,7 @@ todo_include_todos = False
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'alabaster'
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
FreeRTOS SMP
|
||||
============
|
||||
|
||||
The SMP (multicore) port of FreeRTOS is included with the core. This allows complex
|
||||
task operations and real preemptive multithreading in your sketches. While the
|
||||
``setup1`` and ``loop1`` way of multitasking is simplest for most folks, FreeRTOS
|
||||
is much more powerful.
|
||||
|
||||
Enabling FreeRTOS
|
||||
-----------------
|
||||
|
||||
To enable FreeRTOS, simply add
|
||||
|
||||
.. code:: c++
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
|
||||
to your sketch and it will be included and enabled automatically.
|
||||
|
||||
Configuration and Predefined Tasks
|
||||
----------------------------------
|
||||
|
||||
FreeRTOS is configured with 8 priority levels (0 through 7) and a process for
|
||||
``setup()/loop()``, ``setup1()/loop1()``, and the USB port will be created. The task
|
||||
quantum is 1 millisecond (i.e. 1,000 switches per second).
|
||||
|
||||
``setup()`` and ``loop()`` are assigned to only run on core 0, while ``setup1()`` and ``loop1()``
|
||||
only run in core 1 in this mode, the same as the default multithreading mode.
|
||||
|
||||
You can launch and manage additional processes using the standard FreeRTOS routines.
|
||||
|
||||
``delay()`` and ``yield()`` free the CPU for other tasks, while ``delayMicroseconds()`` does not.
|
||||
|
||||
Caveats
|
||||
-------
|
||||
|
||||
While the core now supports FreeRTOS, most (probably all) Arduino libraries were not written
|
||||
to support preemptive multithreading. This means that all calls to a particular library should
|
||||
be made from a single task.
|
||||
|
||||
In particular, the ``LittleFS`` and ``SDFS`` libraries can not be called from different
|
||||
threads. Do all ``File`` operations from a single thread or else undefined behavior
|
||||
(aka strange crashes or data corruption) can occur.
|
||||
|
||||
More Information
|
||||
----------------
|
||||
|
||||
For full FreeRTOS documentation look at `FreeRTOS.org <https://freertos.org/index.html>`__
|
||||
and `FreeRTOS SMP support <https://freertos.org/symmetric-multiprocessing-introduction.html>`__.
|
||||
+144
-52
@@ -1,89 +1,181 @@
|
||||
I2S (Digital Audio) Output Library
|
||||
==================================
|
||||
I2S (Digital Audio) Audio Library
|
||||
=================================
|
||||
|
||||
While the RP2040 chip on the Raspberry Pi Pico does not include a hardware
|
||||
I2S device, it is possible to use the PIO (Programmable I/O) state machines
|
||||
to implement one dynamically.
|
||||
|
||||
This I2S library uses the ``pico-extras`` I2S audio library and wraps it in
|
||||
an Arduino I2S library. It supports 16 bits/sample and frequencies of up
|
||||
to 48kHZ, with configurable BCLK, LRCLK(always pin "BCLK + 1"), and DOUT pins.
|
||||
Digital audio input and output are supported at 8, 16, 24, and 32 bits per
|
||||
sample.
|
||||
|
||||
Theoretically up to 6 I2S ports may be created, but in practice there
|
||||
may not be enough resources (DMA, PIO SM) to actually create and use so
|
||||
many.
|
||||
|
||||
Create an I2S port by instantiating a variable of the I2S class
|
||||
specifying the direction. Configure it using API calls below before
|
||||
using it.
|
||||
|
||||
**Note:** This I2S device takes over the entire PIO1 (second) unit and adjusts
|
||||
its clock frequency to meet the I2S needs. That means when only 4 Tones
|
||||
or only 4 Servos may be used when the I2S device is used.
|
||||
|
||||
I2S Class API
|
||||
-------------
|
||||
|
||||
I2S(OUTPUT)
|
||||
~~~~~~~~~~~
|
||||
Creates an I2S output port. Needs to be connected up to the
|
||||
desired pins (see below) and started before any output can happen.
|
||||
|
||||
I2S(INPUT)
|
||||
~~~~~~~~~~
|
||||
Creates an I2S input port. Needs to be connected up to the
|
||||
desired pins (see below) and started before any input can happen.
|
||||
|
||||
bool setBCLK(pin_size_t pin)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Sets the BCLK pin of the I2S device. The LRCLK/word clock will be ``pin + 1``
|
||||
due to limitations of the PIO state machines. Call this before ``I2S.begin()``
|
||||
Default BCLK = 26, LRCLK = 27
|
||||
due to limitations of the PIO state machines. Call this before ``I2S::begin()``
|
||||
|
||||
bool setDOUT(pin_size_t pin)
|
||||
bool setDATA(pin_size_t pin)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Sets the DOUT pin of the I2S device. Any pin may be used. Default DOUT = 28
|
||||
Call before ``I2S.begin()``
|
||||
Sets the DOUT or DIN pin of the I2S device. Any pin may be used.
|
||||
Call before ``I2S::begin()``
|
||||
|
||||
bool begin(long sampleRate)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Start the I2S device up with the given sample rate. The pins selected above
|
||||
will be turned to output and the I2S will begin to drive silence frames (all
|
||||
zero) out.
|
||||
bool setBitsPerSample(int bits)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Specify how many bits per audio sample to read or write. Note that
|
||||
for 24-bit samples, audio samples must be left-aligned (i.e. bits 31...8).
|
||||
Call before ``I2S::begin()``
|
||||
|
||||
bool setBuffers(size_t buffers, size_t bufferWords, int32_t silenceSample = 0)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Set the number of DMA buffers and their size in 32-bit words as well as
|
||||
the word to fill when no data is available to send to the I2S hardware.
|
||||
Call before ``I2S::begin()``.
|
||||
|
||||
bool setFrequency(long sampleRate)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
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 begin()/begin(long sampleRate)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Start the I2S device up with the given sample rate, or with the value set
|
||||
using the prior ``setFrequency`` call.
|
||||
|
||||
void end()
|
||||
~~~~~~~~~~
|
||||
Stops the I2S device. **Note, at present the memory allocated for I2S buffers
|
||||
is not freed leading to a memory leak when ``end()`` is called. This is due
|
||||
to the state of the ``pico-extras`` release which this code uses.**
|
||||
Stops the I2S device.
|
||||
|
||||
void flush()
|
||||
~~~~~~~~~~~~
|
||||
Sends any partial frames in memory to the I2S output device. They may NOT play
|
||||
immediately. Potential use case for this call would be when the frequency of
|
||||
the output will be changing.
|
||||
Waits until all the I2S buffers have been output.
|
||||
|
||||
size_t write(uint8_t)
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
Provided for compatibility, but not very useful. Writes a sample from 0...255
|
||||
to the I2S buffer. See ``write(int16_t)`` for a better one
|
||||
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
|
||||
user to keep track of left/right channels. Note this writes data equivalent
|
||||
to one channel's data, not the size of the passed in variable (i.e. if you have
|
||||
a 16-bit sample size and ``write((int8_t)-5); write((int8_t)5);`` you will have
|
||||
written **2 samples** to the I2S buffer of whatever the I2S size, not a single
|
||||
16-bit sample.
|
||||
|
||||
This call will block (wait) until space is available to actually write
|
||||
the data.
|
||||
|
||||
size_t write(int32_t val, bool sync)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Writes 32 bits of data to the I2S buffer (regardless of the configured I2S
|
||||
bit size). When ``sync`` is true, it will not return until the data has
|
||||
been writte. When ``sync`` is false, it will return ``0`` immediately if
|
||||
there is no space present in the I2S buffer.
|
||||
|
||||
size_t write(const uint8_t \*buffer, size_t size)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Transfers number of bytes from an application buffer to the I2S output buffer.
|
||||
Be aware that ``size`` is in *bytes** and not samples. Size must be a multiple
|
||||
of **4 bytes** (i.e. one left/right sample). Will not block, so check
|
||||
the return value to find out how many bytes were actually written.
|
||||
of **4 bytes**. Will not block, so check the return value to find out how
|
||||
many bytes were actually written.
|
||||
|
||||
int availableForWrite()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns the number of **32-bit L/R samples** that can be written without
|
||||
Returns the number of L/R samples that can be written without
|
||||
potentially blocking.
|
||||
|
||||
bool setFrequency(int newFreq)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Adjusts the I2S output frequency. When changing frequency while I2S output
|
||||
is underway, be sure to use ``I2S.flush()`` before changing the frequency to
|
||||
ensure the older data is played at the right frequency.
|
||||
int read()
|
||||
~~~~~~~~~~
|
||||
Reads a single sample of I2S data, whatever the I2S sample size is configured.
|
||||
Will not return until data is available.
|
||||
|
||||
size_t write(int16_t)
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
Writes a single 16-bit left or right sample to the I2S output buffer. The
|
||||
user is required to ensure that an even number of samples is written (i.e.
|
||||
left and right) over the application lifetime. The application also needs
|
||||
to track which sample is next to be written (right/left). For this reason,
|
||||
the ``write(void *b, size_t lrsamples)`` call may be easier and faster to use.
|
||||
int peek()
|
||||
~~~~~~~~~~
|
||||
Returns the next sample to be read from the I2S buffer (without actually
|
||||
removing it).
|
||||
|
||||
size_t write(const void \*buffer, size_t lrsamples)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Writes up to size left+right packed samples to the I2S device. Non-blocking,
|
||||
will writefrom 0...size samples and return that count. Be sure your app
|
||||
handles partial writes (i.e. by ``yield()`` ing and then retrying to write the
|
||||
remaining data.)
|
||||
void onTransmit(void (\*fn)(void))
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Sets a callback to be called when an I2S DMA buffer is fully transmitted.
|
||||
Will be in an interrupt context so the specified function must operate
|
||||
quickly and not use blocking calls like delay() or write to the I2S.
|
||||
|
||||
The ``onTransmit`` callback is not supported.
|
||||
void onReceive(void (\*fn)(void))
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Sets a callback to be called when an I2S DMA buffer is fully read in.
|
||||
Will be in an interrupt context so the specified function must operate
|
||||
quickly and not use blocking calls like delay() or read from the I2S.
|
||||
|
||||
See the `ESP8266Audio <https://github.com/earlephilhower/ESP8266Audio>`_ library
|
||||
for a working example, or look at the included sample tone generator.
|
||||
Sample Writing/Reading API
|
||||
--------------------------
|
||||
Because I2S streams consist of a natural left and right sample, it is often
|
||||
convenient to write or read both with a single call. The following calls
|
||||
allow applications to read or write both samples at the same time, and
|
||||
explicitly indicate the bit widths required (to avoid potential issues with
|
||||
type conversion on calls).
|
||||
|
||||
size_t write8(int8_t l, int8_t r)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Writes a left and right 8-bit sample to the I2S buffers. Blocks until space
|
||||
is available.
|
||||
|
||||
size_t write16(int16_t l, int16_t r)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Writes a left and right 16-bit sample to the I2S buffers. Blocks until space
|
||||
is available.
|
||||
|
||||
size_t write24(int32_t l, int32_t r)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Writes a left and right 24-bit sample to the I2S buffers. See note below
|
||||
about 24-bit mode. Blocks until space is available.
|
||||
|
||||
size_t write32(int32_t l, int32_t r)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Writes a left and right 32-bit sample to the I2S buffers. Blocks until space
|
||||
is available.
|
||||
|
||||
bool read8(int8_t \*l, int8_t \*r)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Reads a left and right 8-bit sample and returns ``true`` on success. Will block
|
||||
until data is available.
|
||||
|
||||
bool read16(int16_t \*l, int16_t \*r)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Reads a left and right 16-bit sample and returns ``true`` on success. Will block
|
||||
until data is available.
|
||||
|
||||
bool read24(int32_t \*l, int32_t \*r)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Reads a left and right 24-bit sample and returns ``true`` on success. See note below
|
||||
about 24-bit mode. Will block until data is available.
|
||||
|
||||
bool read32(int32_t \*l, int32_t \*r)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Reads a left and right 32-bit sample and returns ``true`` on success. Will block
|
||||
until data is available.
|
||||
|
||||
|
||||
Note About 24-bit Samples
|
||||
-------------------------
|
||||
24-bit samples are stored as left-aligned 32-bit values with bits 7..0
|
||||
ignored. Only the upper 24 bits 31...8 will be transmitted or
|
||||
received. The actual I2S protocol will only transmit or receive 24 bits
|
||||
in this mode, even though the data is 32-bit packed.
|
||||
|
||||
@@ -34,3 +34,40 @@ Debug messages from `printf` and the Core can be printed to a Serial port
|
||||
to allow for easier debugging. Select the desired port and verbosity.
|
||||
Selecting a port for debug output does not stop a sketch from using it
|
||||
for normal operations.
|
||||
|
||||
Generic RP2040 Support
|
||||
----------------------
|
||||
If your RP2040 board isn't in the menus you can still use it with the
|
||||
IDE bu using the `Board->Generic RP2040` menu option. You will need to
|
||||
then set the flash size (see above) and tell the IDE how to communicate
|
||||
with the flash chip using the `Tools->Boot Stage 2` menu.
|
||||
|
||||
Boot Stage 2 Options for Generic RP2040
|
||||
---------------------------------------
|
||||
The Arduino Pico needs to set up its internal flash interface to talk to
|
||||
whatever flash chip is in the system. While all flash chips support a
|
||||
basic (and slow) 1-bit operation using common timings, each different
|
||||
brand (and sometimes model) of flash chip require custom timings to work
|
||||
in QSPI (4-bit) mode. The `Boot Stage 2` menu lets you select from
|
||||
the supported timings.
|
||||
|
||||
The options with `/2` in them divide the system clock by 2 to drive the
|
||||
bus. Options with `/4` divide the clock by 4 and so are slower but more
|
||||
compatible.
|
||||
|
||||
If you can't match a chip name in the menu to your flash chip, a simple
|
||||
test can be run to determine which is correct. Simpily load the `Blink`
|
||||
example, select the first option in the `Boot Stage 2` menu, and upload.
|
||||
If that works, note it and continue. Iterate through the options and
|
||||
note which ones work. If an option doesn't work, unplug the chip and
|
||||
hold the BOOTSEL button down while re-inserting it to enter the ROM
|
||||
uploader mode. (The CPU and flash will not be harmed if the test fails.)
|
||||
|
||||
If one of the custom bootloaders (not `Generic SPI /2 or /4`) worked, use
|
||||
that option to get best performance. If none worked other than the
|
||||
`Generic SPI /2 or /4` then use that. The `/2` options of all models
|
||||
is preferred as it is faster, but some boards do require `/4` on the
|
||||
custom chip interfaces.
|
||||
|
||||
When in doubt, `Generic SPI /4` should work with any flash chip but is
|
||||
slow.
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 119 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 93 KiB |
@@ -20,6 +20,8 @@ For the latest version, always check https://github.com/earlephilhower/arduino-p
|
||||
Installation <install>
|
||||
IDE Menus <ide>
|
||||
|
||||
Platform.IO Integration <platformio>
|
||||
|
||||
Pin (Re)Assignment <pins>
|
||||
|
||||
Analog I/O <analog>
|
||||
@@ -27,6 +29,7 @@ For the latest version, always check https://github.com/earlephilhower/arduino-p
|
||||
EEPROM <eeprom>
|
||||
I2S Audio <i2s>
|
||||
Serial USB and UARTs <serial>
|
||||
"Software Serial" PIO UART <piouart>
|
||||
Servo <servo>
|
||||
SPI <spi>
|
||||
Wire(I2C) <wire>
|
||||
@@ -34,6 +37,8 @@ For the latest version, always check https://github.com/earlephilhower/arduino-p
|
||||
USB (Arduino and Adafruit_TinyUSB) <usb>
|
||||
Multicore Processing <multicore>
|
||||
|
||||
FreeRTOS SMP (multicore) <freertos>
|
||||
|
||||
Ported/Optimized Libraries <libraries>
|
||||
Using Pico-SDK <sdk>
|
||||
|
||||
|
||||
+22
-5
@@ -40,8 +40,6 @@ To install via GIT (for latest and greatest versions):
|
||||
git submodule update --init
|
||||
cd pico-sdk
|
||||
git submodule update --init
|
||||
cd pico-extras
|
||||
git submodule update --init
|
||||
cd ../tools
|
||||
python3 ./get.py
|
||||
|
||||
@@ -135,6 +133,25 @@ The first line creates a file with the USB vendor and ID of the Picoprobe and te
|
||||
|
||||
Once Picoprobe permissions are set up properly, then select the board "Raspberry Pi Pico (Picoprobe)" in the Tools menu and upload as normal.
|
||||
|
||||
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.
|
||||
Uploading Sketches with pico-debug
|
||||
----------------------------------
|
||||
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`):
|
||||
|
||||
.. code::
|
||||
|
||||
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 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, 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".
|
||||
|
||||
+10
-4
@@ -41,6 +41,12 @@ void rp2040.resumeOtherCore()
|
||||
|
||||
Resumes processing in the other core, where it left off.
|
||||
|
||||
|
||||
void rp2040.restartCore1()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Hard resets Core1 from Core 0 and restarts its operation from ``setup1()``.
|
||||
|
||||
Communicating Between Cores
|
||||
---------------------------
|
||||
|
||||
@@ -50,23 +56,23 @@ use the following functions to access a software-managed, multicore safe
|
||||
FIFO.
|
||||
|
||||
void rp2040.fifo.push(uint32_t)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Pushes a value to the other core. Will block if the FIFO is full.
|
||||
|
||||
bool rp2040.fifo.push_nb(uint32_t)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Pushes a value to the other core. If the FIFO is full, returns ``false``
|
||||
immediately and doesn't block. If the push is successful, returns ``true``.
|
||||
|
||||
uint32_t rp2040.fifo.pop()
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Reads a value from this core's FIFO. Blocks until one is available.
|
||||
|
||||
bool rp2040.fifo.pop_nb(uint32_t *dest)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Reads a value from this core's FIFO and places it in dest. Will return
|
||||
``true`` if successful, or ``false`` if the pop would block.
|
||||
|
||||
@@ -26,6 +26,8 @@ Serial1 (UART0), Serial2 (UART1)
|
||||
|
||||
::setRX(pin)
|
||||
::setTX(pin)
|
||||
::setRTS(pin)
|
||||
::setCTS(pin)
|
||||
|
||||
SPI (SPI0), SPI1 (SPI1)
|
||||
-----------------------
|
||||
@@ -33,6 +35,7 @@ SPI (SPI0), SPI1 (SPI1)
|
||||
.. code:: cpp
|
||||
|
||||
::setSCK(pin)
|
||||
::setCS(pin)
|
||||
::setRX(pin)
|
||||
::setTX(pin)
|
||||
|
||||
@@ -58,3 +61,7 @@ it use a non-default pinout with a simple call
|
||||
SD.begin(5);
|
||||
}
|
||||
|
||||
Pad Strength
|
||||
============
|
||||
|
||||
The Raspberry Pi Pico has the ability to set the current that a pin (actually the pad associated with it) is capable of supplying. The current can be set to values of 2mA, 4mA, 8mA and 12mA. By default, on a reset, the setting is 4mA. A `pinMode(x, OUTPUT)`, where `x` is the pin number, is also the default setting. 4 settings have been added for use with `pinMode`: `OUTPUT_2MA`, `OUTPUT_4MA`, which has the same behavior as `OUTPUT`, `OUTPUT_8MA` and `OUTPUT_12MA`.
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
"SoftwareSerial" PIO-based UART
|
||||
================================
|
||||
|
||||
Equivalent to the Arduino SoftwareSerial library, an emulated UART using
|
||||
one or two PIO state machines is included in the Arduino-Pico core. This
|
||||
allows for up to 4 bidirectional or up to 8 unidirectional serial ports to
|
||||
be run from the RP2040 without requiring additional CPU resources.
|
||||
|
||||
Instantiate a ``SerialPIO(txpin, rxpin, fifosize)`` object in your sketch and then
|
||||
use it the same as any other serial port. Even, odd, and no parity modes
|
||||
are supported, as well as data sizes from 5- to 8-bits. Fifosize, if not
|
||||
specified, defaults to 32 bytes.
|
||||
|
||||
To instantiate only a serial transmit or receive unit, pass in
|
||||
``SerialPIO::NOPIN`` as the ``txpin`` or ``rxpin``.
|
||||
|
||||
For example, to make a transmit-only port on GP16
|
||||
.. code:: cpp
|
||||
|
||||
SerialPIO transmitter( 16, SerialPIO::NOPIN );
|
||||
|
||||
For detailed information about the Serial ports, see the
|
||||
Arduino `Serial Reference <https://www.arduino.cc/reference/en/language/functions/communication/serial/>`_ .
|
||||
|
||||
|
||||
SoftwareSerial Emulation
|
||||
========================
|
||||
A ``SoftwareSerial`` wrapper is included to provide plug-and-play compatibility
|
||||
with the Arduino `Software Serial <https://docs.arduino.cc/learn/built-in-libraries/software-serial>`_
|
||||
library. Use the normal ``#include <SoftwareSerial.h>`` to include it. The following
|
||||
differences from the Arduino standard are present:
|
||||
|
||||
* Inverted mode is not supported
|
||||
* All ports are always listening
|
||||
* ``listen`` call is a no-op
|
||||
* ``isListening()`` always returns ``true``
|
||||
@@ -0,0 +1,305 @@
|
||||
Using this core with PlatformIO
|
||||
===============================
|
||||
|
||||
What is PlatformIO?
|
||||
-------------------
|
||||
|
||||
`PlatformIO <https://platformio.org/>`__ is a free, open-source build-tool written in Python, which also integrates into VSCode code as an extension.
|
||||
|
||||
PlatformIO significantly simplifies writing embedded software by offering a unified build system, yet being able to create project files for many different IDEs, including VSCode, Eclipse, CLion, etc.
|
||||
Through this, PlatformIO can offer extensive features such as IntelliSense (autocomplete), debugging, unit testing etc., which not available in the standard Arduino IDE.
|
||||
|
||||
The Arduino IDE experience:
|
||||
|
||||
.. image:: images/the_arduinoide_experience.png
|
||||
|
||||
The PlatformIO experience:
|
||||
|
||||
.. image:: images/the_platformio_experience.png
|
||||
|
||||
Refer to the general documentation at https://docs.platformio.org/.
|
||||
|
||||
Especially useful is the `Getting started with VSCode + PlatformIO <https://docs.platformio.org/en/latest/integration/ide/vscode.html#installation>`_, `CLI reference <https://docs.platformio.org/en/latest/core/index.html>`_ and the `platformio.ini options <https://docs.platformio.org/en/latest/projectconf/index.html>`_ page.
|
||||
|
||||
Hereafter it is assumed that you have a basic understanding of PlatformIO in regards to project creation, project file structure and building and uploading PlatformIO projects, through reading the above pages.
|
||||
|
||||
Current state of development
|
||||
----------------------------
|
||||
|
||||
At the time of writing, PlatformIO integration for this core is a work-in-progress and not yet merged into mainline PlatformIO. This is subject to change once `this pull request <https://github.com/platformio/platform-raspberrypi/pull/36>`_ is merged.
|
||||
|
||||
If you want to use the PlatformIO integration right now, make sure you first create a standard Raspberry Pi Pico + Arduino project within PlatformIO.
|
||||
This will give you a project with the ``platformio.ini``
|
||||
|
||||
.. code:: ini
|
||||
|
||||
[env:pico]
|
||||
platform = raspberrypi
|
||||
board = pico
|
||||
framework = arduino
|
||||
|
||||
Here, you need to change the `platform` to take advantage of the features described hereunder and switch to the new core.
|
||||
|
||||
.. code:: ini
|
||||
|
||||
[env:pico]
|
||||
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
|
||||
board = pico
|
||||
framework = arduino
|
||||
board_build.core = earlephilhower
|
||||
|
||||
When the support for this core has been merged into mainline PlatformIO, this notice will be removed and a standard `platformio.ini` as shown above will work as a base.
|
||||
|
||||
Deprecation warnings
|
||||
---------------------
|
||||
|
||||
Previous versions of this documentation told users to inject the framework and toolchain package into the project by using
|
||||
|
||||
.. code:: ini
|
||||
|
||||
; note that download link for toolchain is specific for OS. see https://github.com/earlephilhower/pico-quick-toolchain/releases.
|
||||
platform_packages =
|
||||
maxgerhardt/framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git
|
||||
maxgerhardt/toolchain-pico@https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.3.1-a/x86_64-w64-mingw32.arm-none-eabi-7855b0c.210706.zip
|
||||
|
||||
This is now **deprecated** and should not be done anymore. Users should delete these ``platform_packages`` lines and update the platform integration by issuing the command
|
||||
|
||||
.. code:: bash
|
||||
|
||||
pio pkg update -g -p https://github.com/maxgerhardt/platform-raspberrypi.git
|
||||
|
||||
in the `PlatformIO CLI <https://docs.platformio.org/en/latest/integration/ide/vscode.html#platformio-core-cli>`_. The same can be achieved by using the VSCode PIO Home -> Platforms -> Updates GUI.
|
||||
|
||||
The toolchain, which was also renamed to ``toolchain-rp2040-earlephilhower`` is downloaded automatically from the registry. The same goes for the ``framework-arduinopico`` toolchain package, which points directly to the Arduino-Pico Github repository.
|
||||
However, users can still select a custom fork or branch of the core if desired so, as detailed in a chapter below.
|
||||
|
||||
Selecting the new core
|
||||
----------------------
|
||||
|
||||
Prerequisite for using this core is to tell PlatformIO to switch to it.
|
||||
There will be board definition files where the Earle-Philhower core will
|
||||
be the default since it's a board that only exists in this core (and not
|
||||
the other https://github.com/arduino/ArduinoCore-mbed). To switch boards
|
||||
for which this is not the default core (which are only
|
||||
``board = pico`` and ``board = nanorp2040connect``), the directive
|
||||
|
||||
.. code:: ini
|
||||
|
||||
board_build.core = earlephilhower
|
||||
|
||||
must be added to the ``platformio.ini``. This controls the `core
|
||||
switching
|
||||
logic <https://github.com/maxgerhardt/platform-raspberrypi/blob/77e0d3a29d1dbf00fd3ec3271104e3bf4820869c/builder/frameworks/arduino/arduino.py#L27-L32>`__.
|
||||
|
||||
When using Arduino-Pico-only boards like ``board = rpipico`` or ``board = adafruit_feather``, this is not needed.
|
||||
|
||||
Flash size
|
||||
----------
|
||||
|
||||
Controlled via specifying the size allocated for the filesystem.
|
||||
Available sketch size is calculated accordingly by using (as in
|
||||
``makeboards.py``) that number and the (constant) EEPROM size (4096
|
||||
bytes) and the total flash size as known to PlatformIO via the board
|
||||
definition file. The expression on the right can involve "b","k","m"
|
||||
(bytes/kilobytes/megabytes) and floating point numbers. This makes it
|
||||
actually more flexible than in the Arduino IDE where there is a finite
|
||||
list of choices. Calculations happen in `the
|
||||
platform <https://github.com/maxgerhardt/platform-raspberrypi/blob/77e0d3a29d1dbf00fd3ec3271104e3bf4820869c/builder/main.py#L118-L184>`__.
|
||||
|
||||
.. code:: ini
|
||||
|
||||
; in reference to a board = pico config (2MB flash)
|
||||
; Flash Size: 2MB (Sketch: 1MB, FS:1MB)
|
||||
board_build.filesystem_size = 1m
|
||||
; Flash Size: 2MB (No FS)
|
||||
board_build.filesystem_size = 0m
|
||||
; Flash Size: 2MB (Sketch: 0.5MB, FS:1.5MB)
|
||||
board_build.filesystem_size = 1.5m
|
||||
|
||||
CPU Speed
|
||||
---------
|
||||
|
||||
As for all other PlatformIO platforms, the ``f_cpu`` macro value (which
|
||||
is passed to the core) can be changed as
|
||||
`documented <https://docs.platformio.org/en/latest/boards/raspberrypi/pico.html#configuration>`__
|
||||
|
||||
.. code:: ini
|
||||
|
||||
; 133MHz
|
||||
board_build.f_cpu = 133000000L
|
||||
|
||||
Debug Port
|
||||
----------
|
||||
|
||||
Via
|
||||
`build_flags <https://docs.platformio.org/en/latest/projectconf/section_env_build.html#build-flags>`__
|
||||
as done for many other cores
|
||||
(`example <https://docs.platformio.org/en/latest/platforms/ststm32.html#configuration>`__).
|
||||
|
||||
.. code:: ini
|
||||
|
||||
; Debug Port: Serial
|
||||
build_flags = -DDEBUG_RP2040_PORT=Serial
|
||||
; Debug Port: Serial 1
|
||||
build_flags = -DDEBUG_RP2040_PORT=Serial1
|
||||
; Debug Port: Serial 2
|
||||
build_flags = -DDEBUG_RP2040_PORT=Serial2
|
||||
|
||||
Debug Level
|
||||
-----------
|
||||
|
||||
Done again by directly adding the needed `build
|
||||
flags <https://github.com/earlephilhower/arduino-pico/blob/05356da2c5552413a442f742e209c6fa92823666/boards.txt#L104-L114>`__.
|
||||
When wanting to define multiple build flags, they must be accumulated in
|
||||
either a sing line or a newline-separated expression.
|
||||
|
||||
.. code:: ini
|
||||
|
||||
; Debug level: Core
|
||||
build_flags = -DDEBUG_RP2040_CORE
|
||||
; Debug level: SPI
|
||||
build_flags = -DDEBUG_RP2040_SPI
|
||||
; Debug level: Wire
|
||||
build_flags = -DDEBUG_RP2040_WIRE
|
||||
; Debug level: All
|
||||
build_flags = -DDEBUG_RP2040_WIRE -DDEBUG_RP2040_SPI -DDEBUG_RP2040_CORE
|
||||
; Debug level: NDEBUG
|
||||
build_flags = -DNDEBUG
|
||||
|
||||
; example: Debug port on serial 2 and all debug output
|
||||
build_flags = -DDEBUG_RP2040_WIRE -DDEBUG_RP2040_SPI -DDEBUG_RP2040_CORE -DDEBUG_RP2040_PORT=Serial2
|
||||
; equivalent to above
|
||||
build_flags =
|
||||
-DDEBUG_RP2040_WIRE
|
||||
-DDEBUG_RP2040_SPI
|
||||
-DDEBUG_RP2040_CORE
|
||||
-DDEBUG_RP2040_PORT=Serial2
|
||||
|
||||
C++ Exceptions
|
||||
--------------
|
||||
|
||||
Exceptions are disabled by default. To enable them, use
|
||||
|
||||
.. code:: ini
|
||||
|
||||
; Enable Exceptions
|
||||
build_flags = -DPIO_FRAMEWORK_ARDUINO_ENABLE_EXCEPTIONS
|
||||
|
||||
Stack Protector
|
||||
---------------
|
||||
|
||||
To enable GCC's stack protection feature, use
|
||||
|
||||
.. code:: ini
|
||||
|
||||
; Enable Stack Protector
|
||||
build_flags = -fstack-protector
|
||||
|
||||
|
||||
RTTI
|
||||
----
|
||||
|
||||
RTTI (run-time type information) is disabled by default. To enable it, use
|
||||
|
||||
.. code:: ini
|
||||
|
||||
; Enable RTTI
|
||||
build_flags = -DPIO_FRAMEWORK_ARDUINO_ENABLE_RTTI
|
||||
|
||||
USB Stack
|
||||
---------
|
||||
|
||||
Not specifying any special build flags regarding this gives one the
|
||||
default Pico SDK USB stack. To change it, add
|
||||
|
||||
.. code:: ini
|
||||
|
||||
; Adafruit TinyUSB
|
||||
build_flags = -DUSE_TINYUSB
|
||||
; No USB stack
|
||||
build_flags = -DPIO_FRAMEWORK_ARDUINO_NO_USB
|
||||
|
||||
Note that the special "No USB" setting is also supported, through the
|
||||
shortcut-define ``PIO_FRAMEWORK_ARDUINO_NO_USB``.
|
||||
|
||||
|
||||
Selecting a different core version
|
||||
----------------------------------
|
||||
|
||||
If you wish to use a different version of the core, e.g., the latest git
|
||||
``master`` version, you can use a
|
||||
`platform_packages <https://docs.platformio.org/en/latest/projectconf/section_env_platform.html#platform-packages>`__
|
||||
directive to do so. Simply specify that the framework package
|
||||
(``framework-arduinopico``) comes from a different source.
|
||||
|
||||
.. code:: ini
|
||||
|
||||
platform_packages =
|
||||
framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#master
|
||||
|
||||
Whereas the ``#master`` can also be replaced by a ``#branchname`` or a
|
||||
``#commithash``. If left out, it will pull the default branch, which is ``master``.
|
||||
|
||||
The ``file://`` and ``symlink://`` pseudo-protocols can also be used instead of ``https://`` to point to a
|
||||
local copy of the core (with e.g. some modifications) on disk (`see documentation <https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_install.html?#local-folder>`_).
|
||||
|
||||
Note that this can only be done for versions that have the PlatformIO
|
||||
builder script it in, so versions before 1.9.2 are not supported.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
The following example ``platformio.ini`` can be used for a Raspberry Pi Pico
|
||||
and 0.5MByte filesystem.
|
||||
|
||||
.. code:: ini
|
||||
|
||||
[env:pico]
|
||||
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
|
||||
board = pico
|
||||
framework = arduino
|
||||
; board can use both Arduino cores -- we select Arduino-Pico here
|
||||
board_build.core = earlephilhower
|
||||
board_build.filesystem_size = 0.5m
|
||||
|
||||
|
||||
The initial project structure should be generated just creating a new
|
||||
project for the Pico and the Arduino framework, after which the
|
||||
auto-generated ``platformio.ini`` can be adapted per above.
|
||||
|
||||
Debugging
|
||||
---------
|
||||
|
||||
With recent updates to the toolchain and OpenOCD, debugging firmwares is also possible.
|
||||
|
||||
To specify the debugging adapter, use ``debug_tool`` (`documentation <https://docs.platformio.org/en/latest/projectconf/section_env_debug.html#debug-tool>`_). Supported values are:
|
||||
|
||||
* ``picoprobe``
|
||||
* ``cmsis-dap``
|
||||
* ``jlink``
|
||||
* ``raspberrypi-swd``
|
||||
|
||||
These values can also be used in ``upload_protocol`` if you want PlatformIO to upload the regular firmware through this method, which you likely want.
|
||||
|
||||
Especially the PicoProbe method is convenient when you have two Raspberry Pi Pico boards. One of them can be flashed with the PicoProbe firmware (`documentation <https://www.raspberrypi.com/documentation/microcontrollers/raspberry-pi-pico.html#debugging-using-another-raspberry-pi-pico>`_) and is then connected to the target Raspberry Pi Pico board (see `documentation <https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf>`_ chapter "Picoprobe Wiring"). Remember that on Windows, you have to use `Zadig <https://zadig.akeo.ie/>`_ to also load "WinUSB" drivers for the "Picoprobe (Interface 2)" device so that OpenOCD can speak to it.
|
||||
|
||||
With that set up, debugging can be started via the left debugging sidebar and works nicely: Setup breakpoints, inspect the value of variables in the code, step through the code line by line. When a breakpoint is hit or execution is halted, you can even see the execution state both Cortex-M0+ cores of the RP2040.
|
||||
|
||||
.. image:: images/pio_debugging.png
|
||||
|
||||
For further information on customizing debug options, like the initial breakpoint or debugging / SWD speed, consult `the documentation <https://docs.platformio.org/en/latest/projectconf/section_env_debug.html>`_.
|
||||
|
||||
Filesystem Uploading
|
||||
--------------------
|
||||
|
||||
For the Arduino IDE, `a plugin <https://github.com/earlephilhower/arduino-pico#uploading-filesystem-images>`_ is available that enables a data folder to be packed as a LittleFS filesystem binary and uploaded to the Pico.
|
||||
|
||||
This functionality is also built-in in the PlatformIO integration. Open the `project tasks <https://docs.platformio.org/en/latest/integration/ide/vscode.html#project-tasks>`_ and expand the "Platform" tasks:
|
||||
|
||||
.. image:: images/pio_fs_upload.png
|
||||
|
||||
The files you want to upload should be placed in a folder called ``data`` inside the project. This can be customized `if needed <https://docs.platformio.org/en/latest/projectconf/section_platformio.html#data-dir>`_.
|
||||
|
||||
The task "Build Filesystem Image" will take all files in the data directory and create a ``littlefs.bin`` file from it using the ``mklittlefs`` tool.
|
||||
|
||||
The task "Upload Filesystem Image" will upload the filesystem image to the Pico via the specified ``upload_protocol``.
|
||||
@@ -38,3 +38,15 @@ using the SDK, the Arduino core may have issues running properly with
|
||||
them. In particular, anything involving flash writes (i.e. EEPROM,
|
||||
filesystems) will probably crash due to CORE1 attempting to read from
|
||||
flash while CORE0 is writing to it.
|
||||
|
||||
PIOASM (Compiling for the PIO processors)
|
||||
-----------------------------------------
|
||||
A precompiled version of the PIOASM tool is included in the download
|
||||
package and can be run from the CLI.
|
||||
|
||||
There is also a fully online version of PIOASM that runs in a web
|
||||
browser without any CLI required, thanks to @jake653: https://wokwi.com/tools/pioasm
|
||||
(GitHub source: https://github.com/wokwi/pioasm-wasm)
|
||||
|
||||
There is also Docker code available for the tool at:
|
||||
https://github.com/kahara/pioasm-docker
|
||||
|
||||
@@ -24,5 +24,22 @@ Configure their pins using the ``setXXX`` calls prior to calling ``begin()``
|
||||
Serial1.setTX(pin);
|
||||
Serial1.begin(baud);
|
||||
|
||||
The size of the receive FIFO may also be adjusted from the default 32 bytes by
|
||||
using the ``setFIFOSize`` call prior to calling ``begin()``
|
||||
|
||||
.. code:: cpp
|
||||
Serial1.setFIFOSize(128);
|
||||
Serial1.begin(baud);
|
||||
|
||||
The FIFO is normally handled via an interrupt, which reduced CPU load and
|
||||
makes it less likely to lose characters.
|
||||
|
||||
For applications where an IRQ driven serial port is not appropriate, use
|
||||
``setPollingMode(true)`` before calling ``begin()``
|
||||
|
||||
.. code:: cpp
|
||||
Serial1.setPollingMode(true);
|
||||
Serial1.begin(300)
|
||||
|
||||
For detailed information about the Serial ports, see the
|
||||
Arduino `Serial Reference <https://www.arduino.cc/reference/en/language/functions/communication/serial/>`_ .
|
||||
|
||||
+29
-6
@@ -9,12 +9,35 @@
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
setup1 KEYWORD2
|
||||
loop2 KEYWORD2
|
||||
|
||||
setup1 KEYWORD2
|
||||
loop1 KEYWORD2
|
||||
|
||||
analogWriteFreq KEYWORD2
|
||||
analogWriteRange KEYWORD2
|
||||
analogWriteResolution KEYWORD2
|
||||
analogWriteResolution KEYWORD2
|
||||
|
||||
######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
push KEYWORD2
|
||||
push_nb KEYWORD2
|
||||
pop KEYWORD2
|
||||
pop_nb KEYWORD2
|
||||
|
||||
rp2040 KEYWORD2
|
||||
RP2040 KEYWORD2
|
||||
usToPIOCycles KEYWORD2
|
||||
f_cpu KEYWORD2
|
||||
getCycleCount KEYWORD2
|
||||
getCycleCount64 KEYWORD2
|
||||
idleOtherCore KEYWORD2
|
||||
resumeOtherCore KEYWORD2
|
||||
|
||||
PIOProgram KEYWORD2
|
||||
prepare KEYWORD2
|
||||
SerialPIO KEYWORD2
|
||||
setFIFOSize KEYWORD2
|
||||
setPollingMode KEYWORD2
|
||||
|
||||
OUTPUT_2MA LITERAL1
|
||||
OUTPUT_4MA LITERAL1
|
||||
OUTPUT_8MA LITERAL1
|
||||
OUTPUT_12MA LITERAL1
|
||||
|
||||
Binary file not shown.
@@ -24,7 +24,7 @@
|
||||
MEMORY
|
||||
{
|
||||
FLASH(rx) : ORIGIN = 0x10000000, LENGTH = __FLASH_LENGTH__
|
||||
RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k
|
||||
RAM(rwx) : ORIGIN = 0x20000000, LENGTH = __RAM_LENGTH__
|
||||
SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k
|
||||
SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
#define _PICO_VERSION_H
|
||||
|
||||
#define PICO_SDK_VERSION_MAJOR 1
|
||||
#define PICO_SDK_VERSION_MINOR 1
|
||||
#define PICO_SDK_VERSION_MINOR 3
|
||||
#define PICO_SDK_VERSION_REVISION 2
|
||||
#define PICO_SDK_VERSION_STRING "1.1.2"
|
||||
#define PICO_SDK_VERSION_STRING "1.3.2-develop"
|
||||
|
||||
#endif
|
||||
|
||||
+6
-11
@@ -1,13 +1,8 @@
|
||||
-iwithprefixbefore/cores/rp2040/api/deprecated-avr-comp/
|
||||
-iwithprefixbefore/lib/pico_base/
|
||||
-iwithprefixbefore/pico-examples/build/generated/pico_base
|
||||
-iwithprefixbefore/pico-extras/src/common/pico_audio/include
|
||||
-iwithprefixbefore/pico-extras/src/common/pico_util_buffer/include
|
||||
-iwithprefixbefore/pico-extras/src/rp2_common/pico_audio_i2s/include
|
||||
-iwithprefixbefore/pico-sdk/lib/tinyusb/src/
|
||||
-iwithprefixbefore/pico-sdk/src/boards/include
|
||||
-iwithprefixbefore/pico-sdk/src/common/pico_base/include
|
||||
-iwithprefixbefore/pico-sdk/src/common/pico_base/include/
|
||||
-iwithprefixbefore/pico-sdk/src/common/pico_binary_info/include
|
||||
-iwithprefixbefore/pico-sdk/src/common/pico_bit_ops/include
|
||||
-iwithprefixbefore/pico-sdk/src/common/pico_divider/include
|
||||
@@ -16,19 +11,23 @@
|
||||
-iwithprefixbefore/pico-sdk/src/common/pico_time/include
|
||||
-iwithprefixbefore/pico-sdk/src/common/pico_util/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2040/hardware_regs/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2040/hardware_regs/include/
|
||||
-iwithprefixbefore/pico-sdk/src/rp2040/hardware_regs/include/
|
||||
-iwithprefixbefore/pico-sdk/src/rp2040/hardware_structs/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/cmsis/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/cmsis/stub/CMSIS/Core/Include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/cmsis/stub/CMSIS/Device/RaspberryPi/RP2040/Include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_adc/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_base/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_claim/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_clocks/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_divider/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_dma/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_exception/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_flash/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_gpio/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_i2c/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_interp/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_irq/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_rtc/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_pio/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_pll/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_pwm/include
|
||||
@@ -36,7 +35,6 @@
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_spi/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_sync/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_timer/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_timer/include/
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_uart/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_vreg/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/hardware_watchdog/include
|
||||
@@ -46,12 +44,9 @@
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_float/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_int64_ops/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_multicore/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_multicore/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_platform/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_platform/include/
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_printf/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_runtime/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_stdio/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_stdio_uart/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_stdio_usb/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2_common/pico_unique_id/include
|
||||
|
||||
@@ -71,7 +71,6 @@
|
||||
-Wl,--wrap=atanf
|
||||
-Wl,--wrap=atanh
|
||||
-Wl,--wrap=atanhf
|
||||
-Wl,--wrap=calloc
|
||||
-Wl,--wrap=cbrt
|
||||
-Wl,--wrap=cbrtf
|
||||
-Wl,--wrap=ceil
|
||||
@@ -105,7 +104,6 @@
|
||||
-Wl,--wrap=fmaf
|
||||
-Wl,--wrap=fmod
|
||||
-Wl,--wrap=fmodf
|
||||
-Wl,--wrap=free
|
||||
-Wl,--wrap=hypot
|
||||
-Wl,--wrap=hypotf
|
||||
-Wl,--wrap=ldexp
|
||||
@@ -118,7 +116,6 @@
|
||||
-Wl,--wrap=log2
|
||||
-Wl,--wrap=log2f
|
||||
-Wl,--wrap=logf
|
||||
-Wl,--wrap=malloc
|
||||
-Wl,--wrap=memcpy
|
||||
-Wl,--wrap=memset
|
||||
-Wl,--wrap=__popcountdi2
|
||||
@@ -147,3 +144,4 @@
|
||||
-Wl,--wrap=tanhf
|
||||
-Wl,--wrap=trunc
|
||||
-Wl,--wrap=truncf
|
||||
-Wl,--wrap=__getreent
|
||||
|
||||
Submodule libraries/Adafruit_TinyUSB_Arduino updated: d26aa1bbd2...9c03087bcd
Submodule
+1
Submodule libraries/ESP8266SdFat added at 43a65b42d5
@@ -0,0 +1,81 @@
|
||||
// Demonstrates a simple use of the setup1()/loop1() functions
|
||||
// for a multiprocessor run.
|
||||
|
||||
// Will output something like, where C0 is running on core 0 and
|
||||
// C1 is on core 1, in parallel.
|
||||
|
||||
// 11:23:07.507 -> C0: Blue leader standing by...
|
||||
// 11:23:07.507 -> C1: Red leader standing by...
|
||||
// 11:23:07.507 -> C1: Stay on target...
|
||||
// 11:23:08.008 -> C1: Stay on target...
|
||||
// 11:23:08.505 -> C0: Blue leader standing by...
|
||||
// 11:23:08.505 -> C1: Stay on target...
|
||||
// 11:23:09.007 -> C1: Stay on target...
|
||||
// 11:23:09.511 -> C0: Blue leader standing by...
|
||||
// 11:23:09.511 -> C1: Stay on target...
|
||||
// 11:23:10.015 -> C1: Stay on target...
|
||||
|
||||
// Released to the public domain
|
||||
#include <FreeRTOS.h>
|
||||
#include <task.h>
|
||||
#include <map>
|
||||
#include <EEPROM.h>
|
||||
|
||||
std::map<eTaskState, const char *> eTaskStateName { {eReady, "Ready"}, { eRunning, "Running" }, {eBlocked, "Blocked"}, {eSuspended, "Suspended"}, {eDeleted, "Deleted"} };
|
||||
void ps() {
|
||||
int tasks = uxTaskGetNumberOfTasks();
|
||||
TaskStatus_t *pxTaskStatusArray = new TaskStatus_t[tasks];
|
||||
unsigned long runtime;
|
||||
tasks = uxTaskGetSystemState( pxTaskStatusArray, tasks, &runtime );
|
||||
Serial.printf("# Tasks: %d\n", tasks);
|
||||
Serial.println("ID, NAME, STATE, PRIO, CYCLES");
|
||||
for (int i=0; i < tasks; i++) {
|
||||
Serial.printf("%d: %-16s %-10s %d %lu\n", i, pxTaskStatusArray[i].pcTaskName, eTaskStateName[pxTaskStatusArray[i].eCurrentState], pxTaskStatusArray[i].uxCurrentPriority, pxTaskStatusArray[i].ulRunTimeCounter);
|
||||
}
|
||||
delete[] pxTaskStatusArray;
|
||||
}
|
||||
|
||||
|
||||
void blink(void *param) {
|
||||
(void) param;
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
while (true) {
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
delay(750);
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
delay(250);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
xTaskCreate(blink, "BLINK", 128, nullptr, 1, nullptr);
|
||||
delay(5000);
|
||||
}
|
||||
|
||||
volatile int val= 0;
|
||||
void loop() {
|
||||
Serial.printf("C0: Blue leader standing by...\n");
|
||||
ps();
|
||||
Serial.printf("val: %d\n", val);
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
// Running on core1
|
||||
void setup1() {
|
||||
delay(5000);
|
||||
Serial.printf("C1: Red leader standing by...\n");
|
||||
}
|
||||
|
||||
void loop1() {
|
||||
static int x = 0;
|
||||
Serial.printf("C1: Stay on target...\n");
|
||||
val++;
|
||||
if (++x < 10) {
|
||||
EEPROM.begin(512);
|
||||
EEPROM.write(0,x);
|
||||
EEPROM.commit();
|
||||
}
|
||||
delay(1000);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
# Syntax Coloring Map For FreeRTOS
|
||||
# https://arduino.github.io/arduino-cli/library-specification/#keywords
|
||||
# Formatted by a single true tab (not spaces)
|
||||
|
||||
# Datatypes (KEYWORD1)
|
||||
StackType_t KEYWORD1
|
||||
BaseType_t KEYWORD1
|
||||
UBaseType_t KEYWORD1
|
||||
TickType_t KEYWORD1
|
||||
|
||||
TaskHandle_t KEYWORD1
|
||||
QueueHandle_t KEYWORD1
|
||||
TimerHandle_t KEYWORD1
|
||||
SemaphoreHandle_t KEYWORD1
|
||||
StreamBufferHandle_t KEYWORD1
|
||||
MessageBufferHandle_t KEYWORD1
|
||||
EventGroupHandle_t KEYWORD1
|
||||
|
||||
# Methods and Functions (KEYWORD2)
|
||||
xSemaphoreCreateMutex KEYWORD2
|
||||
xSemaphoreCreateBinary KEYWORD2
|
||||
xSemaphoreTake KEYWORD2
|
||||
xSemaphoreTakeFromISR KEYWORD2
|
||||
xSemaphoreGive KEYWORD2
|
||||
xSemaphoreGiveFromISR KEYWORD2
|
||||
xTaskCreate KEYWORD2
|
||||
vTaskDelete KEYWORD2
|
||||
vTaskDelay KEYWORD2
|
||||
xTaskDelayUntil KEYWORD2
|
||||
xQueueCreate KEYWORD2
|
||||
xQueueSend KEYWORD2
|
||||
xQueueReceive KEYWORD2
|
||||
pcTaskGetName KEYWORD2
|
||||
ulTaskNotifyTake KEYWORD2
|
||||
vTaskNotifyGiveFromISR KEYWORD2
|
||||
taskYIELD KEYWORD2
|
||||
vTaskSuspend KEYWORD2
|
||||
vTaskResume KEYWORD2
|
||||
xTaskResumeFromISR KEYWORD2
|
||||
xTaskGetTickCount KEYWORD2
|
||||
xTaskGetTickCountFromISR KEYWORD2
|
||||
uxTaskGetNumberOfTasks KEYWORD2
|
||||
uxTaskGetStackHighWaterMark KEYWORD2
|
||||
|
||||
# Instances (KEYWORD2)
|
||||
|
||||
# Structures (KEYWORD3)
|
||||
TaskParameters_t KEYWORD3
|
||||
TaskStatus_t KEYWORD3
|
||||
ListItem_t KEYWORD3
|
||||
MiniListItem_t KEYWORD3
|
||||
HeapStats_t KEYWORD3
|
||||
|
||||
# Constants (LITERAL1)
|
||||
portUSE_WDTO LITERAL1
|
||||
portTICK_PERIOD_MS LITERAL1
|
||||
configTICK_RATE_HZ LITERAL1
|
||||
configCPU_CLOCK_HZ LITERAL1
|
||||
configMAX_PRIORITIES LITERAL1
|
||||
configMINIMAL_STACK_SIZE LITERAL1
|
||||
Submodule
+1
Submodule libraries/FreeRTOS/lib/FreeRTOS-Kernel added at 6047923beb
@@ -0,0 +1,10 @@
|
||||
name=FreeRTOS
|
||||
version=1.0.0
|
||||
author=Graham Sanderson <info@freertos.org>
|
||||
maintainer=Graham Sanderson <info@freertos.org>
|
||||
sentence=<h3>FreeRTOS Real Time Operating System implemented for RP2040.</h3>
|
||||
paragraph=Taken from the official FreeRTOS SMP branch.
|
||||
category=Timing
|
||||
url=https://github.com/FreeRTOS/FreeRTOS-Kernel
|
||||
architectures=rp2040
|
||||
license=MIT
|
||||
@@ -0,0 +1,39 @@
|
||||
**ATTENTION**
|
||||
Please be aware that this library was copied from the original Arduino AVR FreeRTOS library and may still contain files from that library that have no meaning in the context of this package, i.e. the licensing and code of conduct file! Each file retains the license and copyright from where it was taken (FreeRTOS repository or AVR FreeRTOS library, respectively.)
|
||||
|
||||
______
|
||||
|
||||
This is a copy of the RP2040 port of the FreeRTOS SMP branch, packaged as an Arduino library.
|
||||
|
||||
It has been created to provide access to FreeRTOS capabilities, with full compatibility to the Arduino environment.
|
||||
It does this by keeping hands off almost everything, and only touching the minimum of hardware to be successful.
|
||||
|
||||
## General
|
||||
|
||||
FreeRTOS has a multitude of configuration options, which can be specified from within the FreeRTOSConfig.h file.
|
||||
To keep commonality with all of the Arduino hardware options, some sensible defaults have been selected.
|
||||
|
||||
System ticks are 1ms, which means that Tasks can only be scheduled to run up to 1000 times per second.
|
||||
|
||||
Stack for the `loop()` function has been set at 256 bytes. This can be configured by adjusting the `configMINIMAL_STACK_SIZE` parameter. If you have stack overflow issues, just increase it.
|
||||
Users should prefer to allocate larger structures, arrays, or buffers using `pvPortMalloc()`, rather than defining them locally on the stack.
|
||||
|
||||
Memory for the heap is allocated by the normal `malloc()` function, wrapped by `pvPortMalloc()`.
|
||||
This option has been selected because it is automatically adjusted to use the capabilities of each device.
|
||||
Other heap allocation schemes are supported by FreeRTOS, and they can used with additional configuration.
|
||||
|
||||
## Errors
|
||||
|
||||
* Stack Overflow: If any stack (for the `loop()` or) for any Task overflows, there will be a slow LED blink, with 4 second cycle.
|
||||
* Heap Overflow: If any Task tries to allocate memory and that allocation fails, there will be a fast LED blink, with 100 millisecond cycle.
|
||||
|
||||
## Files & Configuration
|
||||
|
||||
* `RP2040_FreeRTOS.h` : Must always be `#include` first. It references other configuration files, and sets defaults where necessary.
|
||||
* `FreeRTOSConfig.h` : Contains a multitude of API and environment configurations.
|
||||
* `variantHooks.cpp` : Contains the RP2040 specific configurations for this port of FreeRTOS.
|
||||
* `heap_3.c` : Contains the heap allocation scheme based on `malloc()`. Other schemes are available, but depend on user configuration for specific MCU choice.
|
||||
|
||||
## Sources / Credits ✨
|
||||
|
||||
This library is built on the efforts of the authors of the FreeRTOS SMP port for the RP2040, using the original Arduino FreeRTOS library as a template for the library package and the Arduino specific behavior.
|
||||
@@ -0,0 +1 @@
|
||||
#include "../lib/FreeRTOS-Kernel/include/FreeRTOS.h"
|
||||
@@ -0,0 +1,128 @@
|
||||
/* Cores */
|
||||
#define configNUM_CORES 2
|
||||
#define configUSE_CORE_AFFINITY 1
|
||||
#define configRUN_MULTIPLE_PRIORITIES 1
|
||||
|
||||
#define configUSE_PREEMPTION 1
|
||||
#define configUSE_IDLE_HOOK 1
|
||||
#define configUSE_MINIMAL_IDLE_HOOK 1
|
||||
#define configUSE_TICK_HOOK 1
|
||||
#define configCPU_CLOCK_HZ ( ( unsigned long ) F_CPU )
|
||||
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 )
|
||||
#define configMAX_PRIORITIES ( 8 )
|
||||
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 256 )
|
||||
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 164 * 1024 ) )
|
||||
#define configMAX_TASK_NAME_LEN ( 10 )
|
||||
#define configUSE_TRACE_FACILITY 1
|
||||
#define configUSE_16_BIT_TICKS 0
|
||||
#define configIDLE_SHOULD_YIELD 1
|
||||
#define configUSE_MUTEXES 1
|
||||
#define configQUEUE_REGISTRY_SIZE 8
|
||||
#define configUSE_RECURSIVE_MUTEXES 1
|
||||
#define configUSE_MALLOC_FAILED_HOOK 1
|
||||
#define configUSE_APPLICATION_TASK_TAG 0
|
||||
#define configUSE_COUNTING_SEMAPHORES 1
|
||||
#define configUSE_QUEUE_SETS 1
|
||||
#define configSUPPORT_DYNAMIC_ALLOCATION 1
|
||||
#define configSUPPORT_STATIC_ALLOCATION 0
|
||||
#define configSTACK_DEPTH_TYPE uint32_t
|
||||
|
||||
#define configUSE_NEWLIB_REENTRANT 1
|
||||
#include <reent.h>
|
||||
extern void __register_impure_ptr(struct _reent *p);
|
||||
#define portSET_IMPURE_PTR(x) __register_impure_ptr(x)
|
||||
|
||||
/* Run time stats related definitions. */
|
||||
void vMainConfigureTimerForRunTimeStats( void );
|
||||
unsigned long ulMainGetRunTimeCounterValue( void );
|
||||
#define configGENERATE_RUN_TIME_STATS 1
|
||||
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() //vMainConfigureTimerForRunTimeStats()
|
||||
#define portGET_RUN_TIME_COUNTER_VALUE() ulMainGetRunTimeCounterValue()
|
||||
|
||||
/* Co-routine definitions. */
|
||||
#define configUSE_CO_ROUTINES 1
|
||||
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
|
||||
|
||||
/* Software timer definitions. */
|
||||
#define configUSE_TIMERS 1
|
||||
#define configTIMER_TASK_PRIORITY ( 2 )
|
||||
#define configTIMER_QUEUE_LENGTH 5
|
||||
#define configTIMER_TASK_STACK_DEPTH ( 80 )
|
||||
|
||||
/* Set the following definitions to 1 to include the API function, or zero
|
||||
to exclude the API function. */
|
||||
#define INCLUDE_xTaskGetCurrentTaskHandle 1
|
||||
#define INCLUDE_vTaskPrioritySet 1
|
||||
#define INCLUDE_uxTaskPriorityGet 1
|
||||
#define INCLUDE_vTaskDelete 1
|
||||
#define INCLUDE_vTaskCleanUpResources 1
|
||||
#define INCLUDE_vTaskSuspend 1
|
||||
#define INCLUDE_vTaskDelayUntil 1
|
||||
#define INCLUDE_vTaskDelay 1
|
||||
#define INCLUDE_eTaskGetState 1
|
||||
|
||||
/* This demo makes use of one or more example stats formatting functions. These
|
||||
format the raw data provided by the uxTaskGetSystemState() function in to human
|
||||
readable ASCII form. See the notes in the implementation of vTaskList() within
|
||||
FreeRTOS/Source/tasks.c for limitations. */
|
||||
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
|
||||
|
||||
#define configUSE_MUTEXES 1
|
||||
#define INCLUDE_uxTaskGetStackHighWaterMark 1
|
||||
#define INCLUDE_xTaskGetIdleTaskHandle 1
|
||||
#define configUSE_MALLOC_FAILED_HOOK 1
|
||||
#define configCHECK_FOR_STACK_OVERFLOW 2
|
||||
/* Normal assert() semantics without relying on the provision of an assert.h
|
||||
header file. */
|
||||
|
||||
/* Cortex-M specific definitions. */
|
||||
#undef __NVIC_PRIO_BITS
|
||||
#ifdef __NVIC_PRIO_BITS
|
||||
/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
|
||||
#define configPRIO_BITS __NVIC_PRIO_BITS
|
||||
#else
|
||||
#define configPRIO_BITS 3 /* 8 priority levels */
|
||||
#endif
|
||||
|
||||
/* The lowest interrupt priority that can be used in a call to a "set priority"
|
||||
function. */
|
||||
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0x7
|
||||
|
||||
/* The highest interrupt priority that can be used by any interrupt service
|
||||
routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL
|
||||
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
|
||||
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
|
||||
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
|
||||
|
||||
/* Interrupt priorities used by the kernel port layer itself. These are generic
|
||||
to all Cortex-M ports, and do not rely on any particular library functions. */
|
||||
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
|
||||
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
|
||||
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
|
||||
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
|
||||
|
||||
extern void rtosFatalError(void);
|
||||
#define configASSERT( x ) \
|
||||
if( ( x ) == 0 ) { portDISABLE_INTERRUPTS(); rtosFatalError(); }
|
||||
|
||||
/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
|
||||
standard names - or at least those used in the unmodified vector table. */
|
||||
//#define xPortPendSVHandler PendSV_Handler
|
||||
//#define xPortSysTickHandler SysTick_Handler
|
||||
//#define vPortSVCHandler SVC_Handler
|
||||
|
||||
/* The size of the global output buffer that is available for use when there
|
||||
are multiple command interpreters running at once (for example, one on a UART
|
||||
and one on TCP/IP). This is done to prevent an output buffer being defined by
|
||||
each implementation - which would waste RAM. In this case, there is only one
|
||||
command interpreter running. */
|
||||
#define configCOMMAND_INT_MAX_OUTPUT_SIZE 2048
|
||||
|
||||
|
||||
#define configUSE_DYNAMIC_EXCEPTION_HANDLERS 0
|
||||
#define configSUPPORT_PICO_SYNC_INTEROP 1
|
||||
#define configSUPPORT_PICO_TIME_INTEROP 1
|
||||
|
||||
|
||||
#include "rp2040_config.h"
|
||||
//#include "task.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../lib/FreeRTOS-Kernel/include/StackMacros.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../lib/FreeRTOS-Kernel/include/atomic.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../lib/FreeRTOS-Kernel/croutine.c"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../lib/FreeRTOS-Kernel/include/croutine.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../lib/FreeRTOS-Kernel/include/deprecated_definitions.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../lib/FreeRTOS-Kernel/event_groups.c"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../lib/FreeRTOS-Kernel/include/event_groups.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../lib/FreeRTOS-Kernel/portable/ThirdParty/GCC/RP2040/include/freertos_sdk_config.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../lib/FreeRTOS-Kernel/portable/MemMang/heap_3.c"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../lib/FreeRTOS-Kernel/portable/ThirdParty/GCC/RP2040/idle_task_static_memory.c"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../lib/FreeRTOS-Kernel/list.c"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../lib/FreeRTOS-Kernel/include/list.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../lib/FreeRTOS-Kernel/include/message_buffer.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../lib/FreeRTOS-Kernel/include/mpu_prototypes.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../lib/FreeRTOS-Kernel/include/mpu_wrappers.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../lib/FreeRTOS-Kernel/portable/ThirdParty/GCC/RP2040/port.c"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../lib/FreeRTOS-Kernel/include/portable.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../lib/FreeRTOS-Kernel/portable/ThirdParty/GCC/RP2040/include/portmacro.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../lib/FreeRTOS-Kernel/include/projdefs.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../lib/FreeRTOS-Kernel/queue.c"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../lib/FreeRTOS-Kernel/include/queue.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../lib/FreeRTOS-Kernel/portable/ThirdParty/GCC/RP2040/include/rp2040_config.h"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user