Compare commits

...
110 Commits
Author SHA1 Message Date
Earle F. Philhower, III de069cf8c9 Update version 2022-06-24 11:25:21 -07:00
Earle F. Philhower, III 8cf8923a18 Update to Adafruit_TinyUSB_Arduino 1.12.0 (#654)
* Update to Adafruit_TinyUSB_Arduino 1.12.0

* DualRole requires add'l libraries, skip in CI
2022-06-24 11:24:31 -07:00
Earle F. Philhower, III d954510c9d Update to latest pico-sdk develop branch (#653)
Fixes #651
2022-06-24 02:19:24 -07:00
Earle F. Philhower, III 8966a9b094 Actually pass in the PIO key for auto-publishing (#652)
Need to manually list the secrets to export to the environment.
2022-06-23 17:12:21 -07:00
Earle F. Philhower, III 3d9611d020 Update FreeRTOS SMP branch to latest upstream (#649) 2022-06-22 17:18:50 -07:00
Earle F. Philhower, III 78b7f26076 Update README.md 2022-06-20 19:17:52 -07:00
Earle F. Philhower, III 7233c39166 Update to latest pico-sdk, allow lock.c->lock.cpp (#646)
Minor change to keep the core all CPP.  Patch just made it into pico-sdk
develop branch allowing recursive mutexes to be auto_init in C++.
Update and rebuild libpico.a.
2022-06-20 11:14:31 -07:00
Earle F. Philhower, III 0ee072671b Update version 2022-06-20 08:30:24 -07:00
Earle F. Philhower, III 1130007761 Make mutexes init in DATA, not ((counstructor)) (#645)
There was a race condition in making mutexes that were only init in
an __attribute((constructor)) code block.  For example, a global
object might do a `malloc` in its constructor which would depend on
the malloc mutex...which may not yet have been initted.

Make them initted in the .data section, instead, which is guaranteed
good before any global constructors are called.
2022-06-20 08:29:08 -07:00
Earle F. Philhower, III a0060d9c3a Add dependency file generation to build (#644)
Fixes #643
2022-06-20 07:50:28 -07:00
Earle F. Philhower, III 66eb0613b0 Major multicore fixes Newlib and FreeRTOS (#640)
Instead of wrapping the memory functions in the link stage, rebuild
Newlib and enable retargetable locks.  Override the weak definitions
in the libc.a with our own, SDK based ones.

The wrapping utilized before catches app-level memory allocations
but misses allocations inside Newlib libc (like printf/etc.).

Each core needs its own _impure_ptr or else crashes like the one seen
in parallel printf_floats can happen.  Enable it in the toolchain
build and implement a simple swapper here.

FreeRTOS SMP doesn't support Newlib's dynamic reent which is needed
to allow save MT support.  Minor patch to FreeRTOS and update the
FreeRTOS variant.cpp and setup to support it.
2022-06-20 07:35:30 -07:00
Earle F. Philhower, III af8f544913 Update pio publish to match PIO PR build (#641) 2022-06-19 09:14:08 -07:00
Earle F. Philhower, III 803184c164 Update to latest Adafruit_TinyUSB (#639)
Fixes #582
2022-06-19 08:57:14 -07:00
Earle F. Philhower, III 857f91771f Update to develop branch of pico-sdk (#636)
The realloc() wrapper was included in the develop branch of pico-sdk, so
use it instead of a local, unreproducible version of the SDK.

Should have no effect on code.
2022-06-17 12:13:01 -07:00
Maximilian Gerhardt 592418d9f3 Minor PIO Docs Fixup (#635)
* Fix link display in PlatformIO Docs, Remove JLink warning

* Remove unfinished sentence
2022-06-17 09:49:51 -07:00
Maximilian Gerhardt 0a23dfeb42 Update PlatformIO board files, add PIO CI (#634)
* Shift arduino attribute higher in JSON file

* Try out PlatformIO CI

* Trigger CI

* Clone recursively, actually use own repo

* Fix YAML

* Build Verbose

* Revert to checkout@v2 version, add TinyUSB to examples

* Try fix TInyUSB example

* Pull repo recursively

* Use v3 after all, correct path to example

* Only do CI on PR
2022-06-17 08:57:18 -07:00
Maximilian Gerhardt 4a94677ee5 Update PlatformIO builder script and docs (#633)
* Update board generation, use renamed function

* Update documentation with new platform integration state

* Remove accidentally pushed file

* Use correct update command

* Use correct highlighting

* Use correct language

* Add section on debugging

* Add docs on filesystem, minor corrections

* Use -iprefix in compilation, but still expose all include paths to IDE

* Add exception and RTTI support, document them

* Fix typo
2022-06-16 18:59:46 -07:00
mnltake 3414b73172 Add analogReadResolution() docs (#631) 2022-06-16 08:16:58 -07:00
Pontus Oldberg 3bcbb24603 Updated helper class for Challenger NB board. (#629) 2022-06-15 09:16:47 -07:00
Ha Thach 26752f6f90 Aadd f_cpu 120, 240 mhz option (#628) 2022-06-15 09:12:43 -07:00
Earle F. Philhower, III 8999ec2c7a Add Platform.IO publish to release process 2022-06-14 13:07:19 -07:00
Earle F. Philhower, III 778cf4cb9e Update version 2022-06-14 12:34:22 -07:00
Earle F. Philhower, III b6a9e756e4 Remove unneeded and invalid file changes
The makever script sets up the repo properly, so no need to patch things
in the package stage.
2022-06-14 12:32:26 -07:00
Earle F. Philhower, III 285c964c98 Update version 2022-06-14 12:05:48 -07:00
Earle F. Philhower, III 1d6f66f834 Shrink flash and RAM usage even more (#627)
Remove MIDI support from the core's main TinyUSB.  MIDI is still supported
using the Adafruit TinyUSB library, just like before (the core never
did use it).

Compile the Pico-SDK using `-Os`

Remove unneeded warning in UF2 upload

Blink.ino shows a savings of 3.5KB flash and 400 bytes of RAM.

For comparison, with this PR blink.ino reports:
````
Sketch uses 49908 bytes (3%) of program storage space. Maximum is 1568768 bytes.
Global variables use 7024 bytes (2%) of dynamic memory, leaving 255120 bytes for local variables. Maximum is 262144 bytes.
````

As comparison, using the MBED core blink.ino reports:
````
Sketch uses 78882 bytes (0%) of program storage space. Maximum is 16777216 bytes.
Global variables use 42780 bytes (15%) of dynamic memory, leaving 227556 bytes for local variables. Maximum is 270336 bytes.
````

So, with this PR we use **37% less flash** and and **87% less RAM**
for simple sketches.
2022-06-13 11:54:38 -07:00
Earle F. Philhower, III 3fb8cbf39f Temporary fix for muilticore realloc (#625)
This PR includes a pico-sdk built using the fix in
https://github.com/raspberrypi/pico-sdk/pull/864

To properly fix it requires a new pico-sdk release. but until then I am
building against my own fork of pico-sdk and including the binary.
When 1.3.2 is out (and assuming my PR is approved) I'll update the pico-sdk
link.

Fixes #614
2022-06-12 13:38:02 -07:00
Earle F. Philhower, III c352105176 Remove IRQ-level malloc from USB setup
Fixes a hang found while debugging #614.  Do all memory allocations and
USB descriptor setup in main code prior to `tusb_init()`.  Avoids potential
deadlock in cases where the app is allocating while the USB port is being
set up.
2022-06-10 22:58:23 -07:00
Earle F. Philhower, III c4623f4297 Disable Wire slave interrupt on ::end (#621)
Fixes #620
2022-06-10 11:42:04 -07:00
Maximilian Gerhardt 1c85a1ca52 Activate default -Os optimization for Platform.IO (#619) 2022-06-10 09:28:26 -07:00
Earle F. Philhower, III fdf30febea Hide get.py download percent when not interactive (#618)
When get.py is run in a script the percent-update printouts shown while
downloading the toolchain end up as 100s to 1000s of lines in log files.

When stdout is not a terminal, avoid printing these percentages and
shrink logfiles significantly. Errors/etc. are still reported as normal.
2022-06-09 18:00:45 -07:00
Earle F. Philhower, III 7d5e7f5dda Remove obsolete include paths from build.inc (#616)
As noted in #615
2022-06-09 15:35:24 -07:00
Maximilian Gerhardt e3a1debc65 Fix IDE include paths / Intellisense (#615) 2022-06-09 15:30:22 -07:00
Earle F. Philhower, III 578d3d2a76 Reduce flash, RAM use, reduce download/install (#613)
Free up 4K of RAM and 6K of flash when no exceptions are enables (default).
The original toolchain was including exception code in libstdc++ by default.

Reduce installation by ~50MB and download by ~25M by removing unused LTO
gcc support.
2022-06-07 16:07:07 -07:00
Earle F. Philhower, III e3053f69f8 Add GCC stack protector support (#611) 2022-06-06 20:07:40 -07:00
Earle F. Philhower, III 04ec75e515 Try and upload ASAP instead of hard-waiting 10sec (#610)
Fixes #609 and a niggling slowness in uploads.  Try to find the drive
in a loop for 10 seconds, instead of only checking once after 10 seconds.
Avoid 100% CPU usage while waiting for Pico drive
2022-06-06 18:21:34 -07:00
Earle F. Philhower, III 420d669a4b Add C++ exception option to menus (#607)
Depends on new toolchain from
https://github.com/earlephilhower/pico-quick-toolchain/pull/11

Fixes #251
2022-06-06 10:28:54 -07:00
FeuerSturm 9e3f6ade61 Fix WizFi360-EVB-Pico pins_arduino.h (#608)
Slight difference to generic RP2040
PIN_SERIAL2_TX + PIN_SERIAL2_RX are
switched with PIN_WIRE0_SDA + PIN_WIRE0_SCL
2022-06-05 16:59:22 -07:00
Earle F. Philhower, III 0aa0f973aa Update version 2022-06-05 10:18:10 -07:00
Earle F. Philhower, III 8fc9c19af9 Add new WizNet boards to README 2022-06-05 10:17:37 -07:00
Earle F. Philhower, III 10b869ef30 Fix XIAO RP2040, allow swapping HW units 0/1 (#606)
Add plumbing to allow `Wire`, `Serial1`, `SPI1` to map to the 2nd
hardware unit for devices where the PCB layout only brings out the
2nd port.

Fix the Seeedstudio XAIO pins

Fixes #594
2022-06-04 21:37:28 -07:00
FeuerSturm c863dad73e New WIZnet boards (#605)
- added WIZnet WizFi360-EVB-Pico
- added WIZnet W5500-EVB-Pico
- corrected WIZnet W5100S-EVB-Pico PID
2022-06-03 11:56:13 -07:00
Earle F. Philhower, III 24583494c6 Fix Wire::available() when ::requestFrom fails (#604)
Avoids returning 2^32 bytes in the case of a requestFrom read fails
due to timeout.
2022-06-01 17:58:54 -07:00
Earle F. Philhower, III 4ede3ff8a4 Add attachInterruptParam (#602)
Fixed #601

Allows apps to provide a callback parameter in the `attachInterrupt` call
to be passed back to the function when the interrupt happens.
2022-05-31 18:14:34 -07:00
Arya11111andArya11111 a58154b52b Modify DFRobot Beetle pin_arduinos.h (#597)
Co-authored-by: Arya11111 <xuepeng@dfrobot.com>
2022-05-25 15:13:25 -07:00
Earle F. Philhower, III 9e659b003c Deallocate resources on SerialPIO::end (#596)
Fixes #593

When SerialPIO::end is called, stop the PIO SMs and potentially
disable the IRQ handler if this is the last RX port on that PIO.
2022-05-25 03:08:50 -07:00
Earle F. Philhower, III 7d30e6da61 Update piouart.rst 2022-05-24 08:09:01 -07:00
Pontus Oldberg a9b24827e2 Updated PID for WiFi/BLE board and other changes (#595) 2022-05-24 08:07:49 -07:00
Earle F. Philhower, III fc55e1e778 Update version 2022-05-20 12:05:12 -07:00
Earle F. Philhower, III 0188ccf696 Remove obsolete programmers.txt entries (not used) 2022-05-20 11:59:29 -07:00
Earle F. Philhower, III 65d50e4d8b Upgrade to Pico-SDK 1.3.1 (#588)
Looks like only minor changes to the SDK, should not affect the core.
2022-05-20 11:53:20 -07:00
Earle F. Philhower, III 3ce961f84d Add in missing JSON files 2022-05-20 05:27:57 -07:00
Earle F. Philhower, III eaf5e049eb Update package_pico_index.template.json 2022-05-20 05:27:03 -07:00
Pontus Oldberg e4153b03bc Add new Challenger RP2040 WiFi/BLE board (#587) 2022-05-20 05:26:01 -07:00
Earle F. Philhower, III a3a5abac23 Fix Wire I2C restart handling (#586)
Fixes #585

On an I2C bus restart, call the onReceive callback and clear the buffer.

Thanks to @DWiskow for the debug and patch!
2022-05-19 16:00:52 -07:00
Earle F. Philhower, III 8054015bff Clean up misc obsolete references (#584)
Remove pico-extras from makelibpico.sh
Remove refs to SPIFFS in FS.h
Remove travis::: markers
Update Arduino IDE URL
2022-05-18 17:33:16 -07:00
Arya11111andArya11111 646d56046f DFRobot Beetle modify some notes. (#580)
Co-authored-by: Arya11111 <xuepeng@dfrobot.com>
2022-05-11 07:29:59 -07:00
Earle F. Philhower, III 7b296a4dc9 Remove pico-extras from package 2022-05-10 08:44:18 -07:00
Earle F. Philhower, III 61ac01c31b Update version 2022-05-10 08:42:22 -07:00
Earle F. Philhower, III 79a2128704 Remove doc refs to Pico-Extras
Fixes #578
2022-05-08 09:27:48 -07:00
Earle F. Philhower, III 0c9359cddd Fix picoprobe and pico-debug upload recipes (#577)
Need to munge the platform.txt on release.

Fixes #576
2022-05-07 18:53:02 -07:00
Arya11111andArya11111 dbf855e1aa Modify DFRobot pins_arduino.h (#573)
Co-authored-by: Arya11111 <xuepeng@dfrobot.com>
2022-05-05 11:35:20 -07:00
Earle F. Philhower, III 0afd6ae54f Fix order of bootsel IRQ/idle calls (#572)
Reverse the order of IRQ and idle restore to match the way
they were initiated.
2022-05-04 18:48:35 -07:00
Earle F. Philhower, III 07500e89db Rewrite I2S from scratch, add I2S input support (#569)
Rewrite the I2S code from scratch to eliminate the dependence on the
pico-extras implementation and to support I2S input as well.

8-bit, 16-bit, 24-bit, and 32-bit words are supported.

Multiple I2S ports are allowed (theoretically up to 6 because
2 DMA channels are required per port).

I2S input and I2S output are supported.

Add input example

Fixes #535
Fixes #99
Fixes #562
2022-05-04 18:43:27 -07:00
iKK001andStephan Korner 3adc1c5321 Correct Serial1 and Serial2 pinout definitions added (#570)
Co-authored-by: Stephan Korner <stephan.korner@trihow.com>
2022-05-04 13:04:01 -07:00
ogatatsu 55394a7dcd Fix compile error when configSUPPORT_STATIC_ALLOCATION is set to 1 (#564) (#566) 2022-04-30 19:07:21 -07:00
Earle F. Philhower, III 230758b1ee Add invert option to SWSerial (#563)
Thanks to @StefanKellerAC !
2022-04-28 08:17:12 -07:00
Earle F. Philhower, III d7e02c6f34 Fix SWSerial polarity. (#560) 2022-04-27 21:05:16 -07:00
Earle F. Philhower, III c32ad45724 Clean up some minor doc errors/formats (#559) 2022-04-27 16:07:27 -07:00
Earle F. Philhower, III 541e23d663 Update to major version 2.0.0 2022-04-27 12:10:55 -07:00
Earle F. Philhower, III 65fe176795 Make default CPU speed 133MHz (#557)
The chip supports 133MHz and the other Pico core already defaults to
the higher speed, so make 133 the default.

It can still be changed through the menus and will stay at 125 unless
pre-existing users change it so they will see no difference.
2022-04-27 11:31:21 -07:00
Earle F. Philhower, III cfc91804c8 Fix memory corruption introducted in FreeRTOS port (#556)
To remove compiler warning the valid core macro was modified to only check
that the core passed in was < # of total cores.  Unfortunately there are
parts of the FreeRTOS code where the passed in core # is -1.  The upstream
catches this and returns FALSE, but my hacked version returned TRUE.  This
caused interesting memory corruption errors and crashes when the
current task block[-1] was updated.

Undo the change and fix the 1 spot where a warning happens instead.

Undo the forced compiler -O0 for port.c, it was only masking the fault.
2022-04-27 09:42:15 -07:00
Earle F. Philhower, III 88d213a30b Additional FreeRTOS adjustments (#555)
Use low power WFE when idle.

Set PORT.C to built `-O0` always because it seems to occasinally end
up with interrupts disabled in task code, causing the SYSTICK never to
fire and killing task switching.

No need for dynamic exceptions.  We don't move the execbase.
2022-04-27 03:53:07 -07:00
Earle F. Philhower, III 189b796499 More FreeRTOS ISR work 2022-04-25 19:11:38 -07:00
Earle F. Philhower, III af7f430227 Change the RTD theme (#554)
Alabaster is not my favorite, try something with colors...
2022-04-25 18:03:07 -07:00
Andy2No 797abb5a7f Update SimpleTone.ino (#552)
Adds code to define which pins are used, moving them from the defaults, which are the same as the only three analogue input pins, and adding comments to explain how to change them.

The original didn't give any clues about which pins were used, which isn't ideal for a beginner - it was necessary to look at the code for the library, to work that out.

The new code redundantly defines a pWS pin number (as pBCLK+1), which isn't used in the example, but is meant as a reminder to the person using it, of how to wire up WS.
2022-04-25 12:21:34 -07:00
Earle F. Philhower, III ffff21a936 Update FreeRTOS library to avoid random crashes (#553) 2022-04-25 12:10:48 -07:00
Earle F. Philhower, III 781fb3ab48 Update docs, increase max analogWriteFreq to 1MHz (#551) 2022-04-25 08:23:50 -07:00
Earle F. Philhower, III bda630e419 Add FreeRTOS support thanks to @hfellner (#533)
Using all the work from @hfellner and others, add FreeRTOS
SMP support.

Allow idling cores through the FreeRTOS FIFO queue to
allow for file system and EEPROM support.

Make delay a weak function so FreeRTOS can override.

Add cycle count support under FreeRTOS using a PIO SM.

Use a task-based approach for handling the USB periodic work
instead of the IRQ-based one in the main core.

Set 8 prio levels so it fits in 3 bits nicely (0..7).
2022-04-24 20:59:57 -07:00
Earle F. Philhower, III 9afdc48ddd Add SoftwareSerial wrapper around SerialPIO (#548)
I receive mails weekly asking how to use `SoftwareSerial` on this core.
Avoid the issue by including a simple wrapper class around `SerialPIO`
which gives the proper class name and constructor parameters.

Note that inverted mode is not supported.
2022-04-24 15:07:55 -07:00
Earle F. Philhower, III 3ee031abc9 Add ::overflow() return to SerialUART/SerialPIO (#547)
Matching the Arduino SoftwareSerial API
2022-04-24 14:44:23 -07:00
Earle F. Philhower, III d1f9bce083 Document the Generic RP2040 option (#546)
Fixes #522
2022-04-24 11:04:17 -07:00
Earle F. Philhower, III 670957487a Update version 2022-04-22 12:55:08 -07:00
Earle F. Philhower, III 1c0133704e Update to LittleFS 2.5 (#545) 2022-04-22 12:22:27 -07:00
Earle F. Philhower, III 5927d4ee2b Fix SerialPIO sampling, avoid reading random garbage (#544)
Adjust the 1/2 bit time to match the number of extra cycles in the actual
PIO loop.

Throw out the entire start bit, which results in sampling the data at the
midpoint and not the starting time of a bit (which was causing random
failures on read data).

Tested at 300bps all the way to 2,000,000bps using a loopback connection.

Fixes #360
2022-04-21 19:48:30 -07:00
Earle F. Philhower, III 5dee051a7a Update version 2022-04-17 14:40:27 -07:00
Earle F. Philhower, III 561110231d Add SparkFun Thing Plus RP2040 (#540) 2022-04-17 14:39:28 -07:00
Earle F. Philhower, III eff908385e Add Seeed XAIO RP2040 support (#538)
Fixes #537
2022-04-16 10:52:06 -07:00
Jean-Luc Béchennec 784a6ed5ad Added the ability to set pad current via pinMode: OUTPUT_2MA, OUTPUT_… (#532) 2022-04-10 13:33:19 -07:00
Earle F. Philhower, III da2d08a27b Update keywords.txt 2022-04-09 11:27:01 -07:00
Earle F. Philhower, III 061b38f0b8 Add RTS/CTS to Arduino Nano Connect Serial2(NINA) (#525)
Fixes #524
2022-03-30 14:12:24 -07:00
Arya11111 9971bda9cb Modify DFRobot pins_arduino.h file. (#521) 2022-03-29 07:17:00 -07:00
Earle F. Philhower, III 6cb2bf7009 Update version 2022-03-17 08:42:29 -07:00
randomllama 50befc42c3 udisks v2.9.0 dropped the trailing period for "udisksctl mount". (#516) 2022-03-16 14:25:44 -07:00
Earle F. Philhower, III 53043830e7 Avoid "chunkiness" of UART FIFO availability (#511)
* Avoid "chunkiness" of UART FIFO availability

The UART FIFO will generate an IRQ to transfer data into the SerialUART
FIFOs either every 4 received bytes, or every 4 idle byte times.  This
causes the ::available count to report "0" until either of those two
cases happen, causing a potentially delay in data becoming available to
the app.

Change the code to pull data from the HW FIFO on a read/available/peek.
Use a non-blocking mutex and IRQ disabling to safely empty the FIFO from
user space.  The mutex added to the IRQ is non-blocking and will be
a single CAS the vast majority of the time, so it should not impact the
Serial performance.

Fixes #464 and others where `setPollingMode()` was needed as a workaround.

Make sure we have all mutexes locked before we disable the port and free
the queue to avoid evil cases.

Only init the mutexes once, on object creation.

In polled mode, don't bother acquiring/releasing the fifo mutex.

When begin() is called on an already running port, call end() to clean
up the old data/etc. before making a new queue/config.  This avoids a
memory leak and potential write-after-free case.
2022-03-16 08:46:20 -07:00
Maximilian Gerhardt 8deb47f2c3 Auto-ignore Adafruit TinyUSB Library if not active (#513)
Originally from https://github.com/episource/platform-raspberrypi/blob/my-platform-pico/builder/frameworks/arduino-pico/arduino-pico.py#L104-L115
2022-03-14 09:01:41 -07:00
Earle F. Philhower, III 01093482e7 Add script to update version number, new header (#506)
Define ARDUINO_PICO_MAJOR/_MINOR/_REVISION for app use and update the
Platform.IO and Arduino files for a new release version.

Fixes #309
Fixes #487
2022-03-06 11:04:19 -08:00
Earle F. Philhower, III da215aecd4 Fix QT Py serial (Serial2 pinned out, not Serial1) (#505) 2022-03-05 14:28:45 -08:00
Earle F. Philhower, III be21c31eab Update package_pico_index.template.json 2022-02-24 04:58:27 -08:00
Earle F. Philhower, III 6457cb7dd0 Update README.md 2022-02-24 04:57:35 -08:00
Arya11111 e444a7329c Add DFRobot Beetle RP2040 boards V1.0.0 (#502) 2022-02-24 04:56:59 -08:00
Earle F. Philhower, III 28d7818c2b Merge branch 'master' of https://github.com/earlephilhower/arduino-pico 2022-02-23 08:48:41 -08:00
Earle F. Philhower, III 6561310d25 Minor OOM check in PIO support routines 2022-02-23 08:48:16 -08:00
Pontus Oldberg f8e8a7b72e Updating LoRa board pins and UART hw flow. (#499)
* Added missing SERIAL2 and LoRa module GIO pins.
* Added support for enabling UART CTS and RTS pins.
2022-02-22 01:45:24 -08:00
Bodmer 5a0d67f1ac PDM library: Update PIO and interrupt use (#496)
The PIO and state machine were hard wired, so this caused problems if they were not free. The approach used by the Servo library has been adopted so a free PIO and SM are searched.

The DMA_IRQ_0 was grabbed exclusively, but this conflicts with SPI DMA use. The interrupt is now shared, but has been allocated the highest possible priority.

Since the PDM PIO use is receive only, the PIO state machine RX FIFO's can be joined to reduce DMA interrupt load.
2022-02-20 13:20:43 -08:00
Earle F. Philhower, III 03626e615b Alphabetize the boards menu (#495)
Reorder the boards menu to make it easier to find any specific
board.  Add a note to the script asking manufacturers to keep it
that way.
2022-02-20 09:21:01 -08:00
Earle F. Philhower, III bac6dae0ad Document new restartCore1 call 2022-02-20 08:46:25 -08:00
Magnus Nordlander 5c186d8467 Added ability to reset and relaunch core 1 (#493) 2022-02-20 08:43:30 -08:00
Earle F. Philhower, III 25b34913c6 Update package_pico_index.template.json 2022-02-19 09:57:56 -08:00
Pontus Oldberg 8dabe44693 Add support for Challenger RP2040 LoRa (#494) 2022-02-19 06:56:32 -08:00
Earle F. Philhower, III 8a7977fcb3 Fix SerialPIO receive interrupt on PIO1 (#490)
Fixes #489

Only the PIO0 IRQ handler was ever installed due to a hardcoding bug.
Attach the IRQ handler to the PIO being started instead.
2022-02-15 12:10:12 -08:00
167 changed files with 10915 additions and 2920 deletions
+46 -20
View File
@@ -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/ESP8266SdFat,./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
@@ -46,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:
@@ -59,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
@@ -81,8 +81,6 @@ jobs:
run: |
cd pico-sdk
git submodule update --init
cd ../pico-extras
git submodule update --init
cd ..
bash ./tests/build.sh
@@ -91,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
@@ -111,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
@@ -121,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
@@ -147,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
@@ -158,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
@@ -181,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
+20
View File
@@ -12,16 +12,36 @@ 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 }}
BUILD_TYPE: package
CI_GITHUB_API_KEY: ${{ secrets.GITHUB_TOKEN }}
PLATFORMIO_AUTH_TOKEN: ${{ secrets.PLATFORMIO_AUTH_TOKEN }}
run: |
pip3 install PyGithub
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
@@ -29,7 +29,6 @@ jobs:
CI_GITHUB_API_KEY: ${{ secrets.GITHUB_TOKEN }}
run: |
(cd pico-sdk && git submodule update --init)
(cd pico-extras && 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.
+3 -3
View File
@@ -13,9 +13,6 @@
[submodule "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
+17 -9
View File
@@ -18,19 +18,26 @@ See https://arduino-pico.readthedocs.io/en/latest/ along with the examples for m
* Adafruit QTPy RP2040
* Adafruit STEMMA Friend RP2040
* Adafruit Trinkey RP2040 QT
* Arduino Nano RP2040 Connect (preliminary)
* 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
@@ -66,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
`````
@@ -81,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.
@@ -93,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
@@ -139,14 +144,16 @@ The installed tools include a version of OpenOCD (in the pqt-openocd directory)
* Generic Arduino USB Serial, Keyboard, and Mouse emulation
* Filesystems (LittleFS and SD/SDFS)
* Multicore support (setup1() and loop1())
* FreeRTOS SMP support
* Overclocking and underclocking from the menus
* digitalWrite/Read, shiftIn/Out, tone, analogWrite(PWM)/Read, temperature
* Peripherals: SPI master, Wire(I2C) master/slave, dual UART, emulated EEPROM, I2S audio 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)
@@ -164,11 +171,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
+6058 -1717
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -25,6 +25,7 @@
#include <stdlib.h>
#include <string.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>
@@ -78,6 +79,9 @@ void analogWriteFreq(uint32_t freq);
void analogWriteRange(uint32_t range);
void analogWriteResolution(int res);
// FreeRTOS potential calls
extern bool __isFreeRTOS;
#ifdef __cplusplus
} // extern "C"
#endif
+1 -1
View File
@@ -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;
}
-14
View File
@@ -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
+129 -82
View File
@@ -25,7 +25,11 @@
#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:
@@ -45,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) {
@@ -79,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() {
@@ -89,97 +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;
extern RP2040 rp2040;
class RP2040 {
public:
RP2040() {
_epoch = 0;
// Enable SYSTICK exception
exception_set_exclusive_handler(SYSTICK_EXCEPTION, _SystickHandler);
systick_hw->csr = 0x7;
systick_hw->rvr = 0x00FFFFFF;
}
~RP2040() { /* noop */ }
// 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() {
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 */
}
inline uint64_t getCycleCount64() {
uint64_t epoch;
uint64_t ctr;
do {
epoch = _epoch;
ctr = systick_hw->cvr;
} while (epoch != _epoch);
return epoch + (1LL << 24) - ctr;
}
void idleOtherCore() {
fifo.idleOtherCore();
}
void resumeOtherCore() {
fifo.resumeOtherCore();
}
// Multicore comms FIFO
_MFIFO fifo;
private:
static void _SystickHandler() {
rp2040._epoch += 1LL << 24;
}
};
extern "C" void main1();
class PIOProgram;
// Wrapper class for PIO programs, abstracting common operations out
// TODO - Add unload/destructor
@@ -235,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;
};
+33 -27
View File
@@ -120,49 +120,49 @@ int __USBGetMouseReportID() {
return __USBInstallKeyboard ? 2 : 1;
}
static int __hid_report_len = 0;
static uint8_t *__hid_report = nullptr;
static uint8_t *GetDescHIDReport(int *len) {
static uint8_t *report = nullptr;
int report_len = 0;
if (report) {
free(report);
report = nullptr;
if (len) {
*len = __hid_report_len;
}
return __hid_report;
}
void __SetupDescHIDReport() {
if (__USBInstallKeyboard && __USBInstallMouse) {
uint8_t desc_hid_report[] = {
TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(1)),
TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(2))
};
report_len = sizeof(desc_hid_report);
report = (uint8_t *)malloc(report_len);
if (report) {
memcpy(report, desc_hid_report, report_len);
__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);
}
} else if (__USBInstallKeyboard && ! __USBInstallMouse) {
uint8_t desc_hid_report[] = {
TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(1))
};
report_len = sizeof(desc_hid_report);
report = (uint8_t *)malloc(report_len);
if (report) {
memcpy(report, desc_hid_report, report_len);
__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);
}
} else { // if (!__USBInstallKeyboard && __USBInstallMouse) {
} else if (! __USBInstallKeyboard && __USBInstallMouse) {
uint8_t desc_hid_report[] = {
TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(1))
};
report_len = sizeof(desc_hid_report);
report = (uint8_t *)malloc(report_len);
if (report) {
memcpy(report, desc_hid_report, report_len);
__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);
}
} else {
__hid_report = nullptr;
__hid_report_len = 0;
}
if (len) {
*len = report_len;
}
return report;
}
// Invoked when received GET HID REPORT DESCRIPTOR
@@ -173,11 +173,13 @@ uint8_t const * tud_hid_descriptor_report_cb(uint8_t 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 = nullptr;
return usbd_desc_cfg;
}
void __SetupUSBDescriptor() {
if (!usbd_desc_cfg) {
bool hasHID = __USBInstallKeyboard || __USBInstallMouse;
@@ -229,7 +231,6 @@ const uint8_t *tud_descriptor_configuration_cb(uint8_t index) {
}
}
}
return usbd_desc_cfg;
}
const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
@@ -287,12 +288,17 @@ 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();
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#define ARDUINO_PICO_MAJOR 2
#define ARDUINO_PICO_MINOR 2
#define ARDUINO_PICO_REVISION 1
#define ARDUINO_PICO_VERSION_STR "2.2.1"
+39 -7
View File
@@ -36,6 +36,10 @@ static pio_program_t *pio_make_uart_prog(int repl, const pio_program_t *pg) {
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;
@@ -93,7 +97,7 @@ void __not_in_flash_func(SerialPIO::_handleIRQ)() {
}
while (!pio_sm_is_rx_fifo_empty(_rxPIO, _rxSM)) {
uint32_t decode = _rxPIO->rxf[_rxSM];
decode >>= 32 - _rxBits;
decode >>= 33 - _rxBits;
uint32_t val = 0;
for (int b = 0; b < _bits + 1; b++) {
val |= (decode & (1 << (b * 2))) ? 1 << b : 0;
@@ -123,7 +127,7 @@ void __not_in_flash_func(SerialPIO::_handleIRQ)() {
asm volatile("" ::: "memory"); // Ensure the queue is written before the written count advances
_writer = next_writer;
} else {
// TODO: Overflow
_overflow = true;
}
}
}
@@ -142,6 +146,7 @@ SerialPIO::~SerialPIO() {
}
void SerialPIO::begin(unsigned long baud, uint16_t config) {
_overflow = false;
_baud = baud;
switch (config & SERIAL_PARITY_MASK) {
case SERIAL_PARITY_EVEN:
@@ -225,7 +230,7 @@ void SerialPIO::begin(unsigned long baud, uint16_t config) {
pio_sm_clear_fifos(_rxPIO, _rxSM); // Remove any existing data
// Put phase divider into OSR w/o using add'l program memory
pio_sm_put_blocking(_rxPIO, _rxSM, clock_get_hz(clk_sys) / (_baud * 2) - 2);
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
@@ -238,8 +243,9 @@ void SerialPIO::begin(unsigned long baud, uint16_t config) {
case 2: pio_set_irq0_source_enabled(_rxPIO, pis_sm2_rx_fifo_not_empty, true); break;
case 3: pio_set_irq0_source_enabled(_rxPIO, pis_sm3_rx_fifo_not_empty, true); break;
}
irq_set_exclusive_handler(PIO0_IRQ_0, _fifoIRQ);
irq_set_enabled(PIO0_IRQ_0, true);
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);
}
@@ -251,8 +257,23 @@ void SerialPIO::end() {
if (!_running) {
return;
}
// TODO: Deallocate PIO resources, stop them
_pioSP[pio_get_index(_rxPIO)][_rxSM] = nullptr;
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;
}
@@ -284,6 +305,17 @@ int SerialPIO::read() {
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)) {
+3 -1
View File
@@ -47,19 +47,21 @@ public:
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();
private:
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;
+135 -16
View File
@@ -65,6 +65,39 @@ bool SerialUART::setTX(pin_size_t pin) {
return false;
}
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;
@@ -81,17 +114,24 @@ bool SerialUART::setFIFOSize(size_t size) {
return true;
}
SerialUART::SerialUART(uart_inst_t *uart, pin_size_t tx, pin_size_t rx) {
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);
@@ -133,6 +173,13 @@ void SerialUART::begin(unsigned long baud, uint16_t config) {
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;
@@ -156,6 +203,7 @@ void SerialUART::end() {
if (!_running) {
return;
}
_running = false;
if (!_polling) {
if (_uart == uart0) {
irq_set_enabled(UART0_IRQ, false);
@@ -163,9 +211,28 @@ void SerialUART::end() {
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;
_running = false;
// 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() {
@@ -174,7 +241,9 @@ int SerialUART::peek() {
return -1;
}
if (_polling) {
_handleIRQ();
_handleIRQ(false);
} else {
_pumpFIFO();
}
if (_writer != _reader) {
return _queue[_reader];
@@ -188,7 +257,9 @@ int SerialUART::read() {
return -1;
}
if (_polling) {
_handleIRQ();
_handleIRQ(false);
} else {
_pumpFIFO();
}
if (_writer != _reader) {
auto ret = _queue[_reader];
@@ -201,13 +272,25 @@ int SerialUART::read() {
return -1;
}
bool SerialUART::overflow() {
CoreMutex m(&_mutex);
if (!_running || !m) {
return false;
}
bool hold = _overflow;
_overflow = false;
return hold;
}
int SerialUART::available() {
CoreMutex m(&_mutex);
if (!_running || !m) {
return 0;
}
if (_polling) {
_handleIRQ();
_handleIRQ(false);
} else {
_pumpFIFO();
}
return (_writer - _reader) % _fifoSize;
}
@@ -218,7 +301,7 @@ int SerialUART::availableForWrite() {
return 0;
}
if (_polling) {
_handleIRQ();
_handleIRQ(false);
}
return (uart_is_writable(_uart)) ? 1 : 0;
}
@@ -229,7 +312,7 @@ void SerialUART::flush() {
return;
}
if (_polling) {
_handleIRQ();
_handleIRQ(false);
}
uart_tx_wait_blocking(_uart);
}
@@ -240,7 +323,7 @@ size_t SerialUART::write(uint8_t c) {
return 0;
}
if (_polling) {
_handleIRQ();
_handleIRQ(false);
}
uart_putc_raw(_uart, c);
return 1;
@@ -252,7 +335,7 @@ size_t SerialUART::write(const uint8_t *p, size_t len) {
return 0;
}
if (_polling) {
_handleIRQ();
_handleIRQ(false);
}
size_t cnt = len;
while (cnt) {
@@ -267,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();
@@ -283,7 +363,15 @@ void arduino::serialEvent2Run(void) {
}
// 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)() {
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)) {
@@ -298,15 +386,46 @@ void __not_in_flash_func(SerialUART::_handleIRQ)() {
// Avoid using division or mod because the HW divider could be in use
_writer = next_writer;
} else {
// TODO: Overflow
_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)() {
Serial1._handleIRQ();
if (__SERIAL1_DEVICE == uart0) {
Serial1._handleIRQ();
} else {
Serial2._handleIRQ();
}
}
static void __not_in_flash_func(_uart1IRQ)() {
Serial2._handleIRQ();
if (__SERIAL2_DEVICE == uart1) {
Serial2._handleIRQ();
} else {
Serial1._handleIRQ();
}
}
+11 -3
View File
@@ -28,13 +28,16 @@
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);
@@ -57,24 +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();
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;
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;
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
+59
View File
@@ -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;
};
+4
View File
@@ -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 {
+62
View File
@@ -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;
}
%}
+71
View File
@@ -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
+3
View File
@@ -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"
{
+83
View File
@@ -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);
}
+88 -29
View File
@@ -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,47 @@ 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);
@@ -69,43 +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(115200);
if (!__isFreeRTOS) {
DEBUG_RP2040_PORT.begin(115200);
}
#endif
#ifndef NO_USB
if (setup1 || loop1) {
rp2040.fifo.begin(2);
multicore_launch_core1(main1);
} else {
rp2040.fifo.begin(1);
if (!__isFreeRTOS) {
if (setup1 || loop1) {
rp2040.fifo.begin(2);
multicore_launch_core1(main1);
} else {
rp2040.fifo.begin(1);
}
rp2040.fifo.registerCore();
}
rp2040.fifo.registerCore();
#endif
setup();
while (true) {
loop();
#ifdef USE_TINYUSB
yield();
#endif
if (arduino::serialEventRun) {
arduino::serialEventRun();
}
if (arduino::serialEvent1Run) {
arduino::serialEvent1Run();
}
if (arduino::serialEvent2Run) {
arduino::serialEvent2Run();
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;
}
}
+3 -3
View File
@@ -27,7 +27,7 @@
#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;
@@ -42,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;
}
+17
View File
@@ -43,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:
+53 -15
View File
@@ -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();
}
+17 -1
View File
@@ -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
View File
@@ -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
+49
View File
@@ -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
View File
@@ -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.
+37
View File
@@ -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

+2
View File
@@ -37,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>
-2
View File
@@ -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
+10 -4
View File
@@ -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.
+6
View File
@@ -26,6 +26,8 @@ Serial1 (UART0), Serial2 (UART1)
::setRX(pin)
::setTX(pin)
::setRTS(pin)
::setCTS(pin)
SPI (SPI0), SPI1 (SPI1)
-----------------------
@@ -59,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`.
+15 -2
View File
@@ -3,8 +3,8 @@
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 8 additional serial ports to be run from the RP2040 without
requiring additional CPU resources.
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
@@ -21,3 +21,16 @@ For example, to make a transmit-only port on GP16
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``
+100 -14
View File
@@ -19,14 +19,14 @@ The PlatformIO experience:
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 [platformio.ini options](https://docs.platformio.org/en/latest/projectconf/index.html)>`__ page.
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 soon.
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``
@@ -38,8 +38,7 @@ This will give you a project with the ``platformio.ini``
board = pico
framework = arduino
Here, you need to change the `platform` to take advantage of the features described hereunder.
You *also* need to inject two PlatformIO packages, one for the compiler toolchain and one for the Arduino core package.
Here, you need to change the `platform` to take advantage of the features described hereunder and switch to the new core.
.. code:: ini
@@ -47,12 +46,32 @@ You *also* need to inject two PlatformIO packages, one for the compiler toolchai
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
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.
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
----------------------
@@ -61,8 +80,8 @@ 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 (e.g. the standard
``board = pico``), the directive
for which this is not the default core (which are only
``board = pico`` and ``board = nanorp2040connect``), the directive
.. code:: ini
@@ -72,6 +91,8 @@ 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
----------
@@ -154,6 +175,37 @@ either a sing line or a newline-separated expression.
-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
---------
@@ -188,8 +240,8 @@ directive to do so. Simply specify that the framework package
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://`` pseudo-protocol can also be used instead of ``https://`` to point to a
local copy of the core (with e.g. some modifications) on disk.
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.
@@ -206,14 +258,48 @@ and 0.5MByte filesystem.
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
; 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
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``.
+3 -4
View File
@@ -32,15 +32,14 @@ using the ``setFIFOSize`` call prior to calling ``begin()``
Serial1.begin(baud);
The FIFO is normally handled via an interrupt, which reduced CPU load and
makes it less likely to lose characters. However, the FIFO introduces up
to 32 bit-times of delay before serial data is available to the application.
makes it less likely to lose characters.
For applications where this is an issue (i.e. very low baud), use
For applications where an IRQ driven serial port is not appropriate, use
``setPollingMode(true)`` before calling ``begin()``
.. code:: cpp
Serial1.setPollingMode(true);
Serial1.begin(110)
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/>`_ .
+6 -1
View File
@@ -11,7 +11,7 @@
#######################################
setup1 KEYWORD2
loop2 KEYWORD2
loop1 KEYWORD2
analogWriteFreq KEYWORD2
analogWriteRange KEYWORD2
@@ -36,3 +36,8 @@ prepare KEYWORD2
SerialPIO KEYWORD2
setFIFOSize KEYWORD2
setPollingMode KEYWORD2
OUTPUT_2MA LITERAL1
OUTPUT_4MA LITERAL1
OUTPUT_8MA LITERAL1
OUTPUT_12MA LITERAL1
BIN
View File
Binary file not shown.
+2 -2
View File
@@ -13,7 +13,7 @@
#define PICO_SDK_VERSION_MAJOR 1
#define PICO_SDK_VERSION_MINOR 3
#define PICO_SDK_VERSION_REVISION 0
#define PICO_SDK_VERSION_STRING "1.3.0"
#define PICO_SDK_VERSION_REVISION 2
#define PICO_SDK_VERSION_STRING "1.3.2-develop"
#endif
-3
View File
@@ -1,8 +1,5 @@
-iwithprefixbefore/cores/rp2040/api/deprecated-avr-comp/
-iwithprefixbefore/lib/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
+1 -3
View File
@@ -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
@@ -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);
}
+60
View File
@@ -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
+10
View File
@@ -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
+39
View File
@@ -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.
+1
View File
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/include/FreeRTOS.h"
+129
View File
@@ -0,0 +1,129 @@
/* 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
#define configNEWLIB_REENTRANT_IS_DYNAMIC 0 /* Note that we have a different config option, portSET_IMPURE_PTR */
#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"
+1
View File
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/include/StackMacros.h"
+1
View File
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/include/atomic.h"
+1
View File
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/croutine.c"
+1
View File
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/include/croutine.h"
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/include/deprecated_definitions.h"
+1
View File
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/event_groups.c"
+1
View File
@@ -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"
+1
View File
@@ -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"
+1
View File
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/list.c"
+1
View File
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/include/list.h"
+1
View File
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/include/message_buffer.h"
+1
View File
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/include/mpu_prototypes.h"
+1
View File
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/include/mpu_wrappers.h"
+1
View File
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/portable/ThirdParty/GCC/RP2040/port.c"
+1
View File
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/include/portable.h"
+1
View File
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/portable/ThirdParty/GCC/RP2040/include/portmacro.h"
+1
View File
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/include/projdefs.h"
+1
View File
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/queue.c"
+1
View File
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/include/queue.h"
+1
View File
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/portable/ThirdParty/GCC/RP2040/include/rp2040_config.h"
+1
View File
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/include/semphr.h"
+1
View File
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/include/stack_macros.h"
+1
View File
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/stream_buffer.c"
+1
View File
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/include/stream_buffer.h"
+1
View File
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/include/task.h"
+1
View File
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/tasks.c"
+1
View File
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/timers.c"
+1
View File
@@ -0,0 +1 @@
#include "../lib/FreeRTOS-Kernel/include/timers.h"
+425
View File
@@ -0,0 +1,425 @@
/*
* Copyright (C) 2021 Phillip Stevens All Rights Reserved.
* Modifications by Earle F. Philhower, III, for Arduino-Pico
*
* 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.
*
*
* This file is NOT part of the FreeRTOS distribution.
*
*/
#include <stdlib.h>
/* Arduino Core includes */
#include <Arduino.h>
#include <RP2040USB.h>
#include "tusb.h"
#define USB_TASK_IRQ 31
/* Raspberry PI Pico includes */
#include <pico.h>
#include <pico/time.h>
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
/*-----------------------------------------------------------*/
void initFreeRTOS(void)
{
}
extern void setup() __attribute__((weak));
extern void loop() __attribute__((weak));
extern void setup1() __attribute__((weak));
extern void loop1() __attribute__((weak));
// Idle functions (USB, events, ...) from the core
extern void __loop();
volatile bool __usbInitted = false;
static void __core0(void *params)
{
(void) params;
#ifndef NO_USB
while (!__usbInitted) {
delay(1);
}
#endif
if (setup) {
setup();
}
if (loop) {
while (1) {
loop();
__loop();
}
} else {
while (1) {
__loop();
}
}
}
static void __core1(void *params)
{
(void) params;
#ifndef NO_USB
while (!__usbInitted) {
delay(1);
}
#endif
if (setup1) {
setup1();
}
if (loop1) {
while (1) {
loop1();
}
} else {
while (1) {
vTaskDelay(1000);
}
}
}
extern "C" void delay(unsigned long ms) {
vTaskDelay(ms / portTICK_PERIOD_MS);
}
extern "C" void yield() {
taskYIELD();
}
extern mutex_t __usb_mutex;
static TaskHandle_t __usbTask;
static void __usb(void *param);
void startFreeRTOS(void)
{
TaskHandle_t c0;
xTaskCreate(__core0, "CORE0", 1024, 0, configMAX_PRIORITIES / 2, &c0);
vTaskCoreAffinitySet( c0, 1 << 0 );
if (setup1 || loop1) {
TaskHandle_t c1;
xTaskCreate(__core1, "CORE1", 1024, 0, configMAX_PRIORITIES / 2, &c1);
vTaskCoreAffinitySet( c1, 1 << 1 );
}
// Initialise and run the freeRTOS scheduler. Execution should never return here.
vTaskStartScheduler();
while (true) {
/* noop */
}
}
/*-----------------------------------------------------------*/
void prvDisableInterrupts()
{
portDISABLE_INTERRUPTS();
}
void prvEnableInterrupts()
{
portENABLE_INTERRUPTS();
}
/*-----------------------------------------------------------*/
#if ( configUSE_IDLE_HOOK == 1 )
/*
* Call the user defined loop() function from within the idle task.
* This allows the application designer to add background functionality
* without the overhead of a separate task.
*
* NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES, CALL A FUNCTION THAT MIGHT BLOCK.
*
*/
extern "C"
void vApplicationIdleHook( void ) __attribute__((weak));
void vApplicationIdleHook( void )
{
__wfe(); // Low power idle if nothing to do...
}
#endif /* configUSE_IDLE_HOOK == 1 */
/*-----------------------------------------------------------*/
#if ( configUSE_MINIMAL_IDLE_HOOK == 1 )
/*
* Call the user defined minimalIdle() function from within the idle task.
* This allows the application designer to add background functionality
* without the overhead of a separate task.
*
* NOTE: vApplicationMinimalIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES, CALL A FUNCTION THAT MIGHT BLOCK.
*
*/
void minimalIdle( void ) __attribute__((weak));
void minimalIdle() {} //Empty minimalIdle function
extern "C"
void vApplicationMinimalIdleHook( void ) __attribute__((weak));
void vApplicationMinimalIdleHook( void )
{
minimalIdle();
}
#endif /* configUSE_MINIMAL_IDLE_HOOK == 1 */
/*-----------------------------------------------------------*/
#if ( configUSE_TICK_HOOK == 1 )
/*
* Call the user defined minimalIdle() function from within the idle task.
* This allows the application designer to add background functionality
* without the overhead of a separate task.
*
* NOTE: vApplicationMinimalIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES, CALL A FUNCTION THAT MIGHT BLOCK.
*
*/
void tick( void ) __attribute__((weak));
void tick() {} //Empty minimalIdle function
extern "C"
void vApplicationTickHook( void ) __attribute__((weak));
void vApplicationTickHook( void )
{
tick();
}
#endif /* configUSE_TICK_HOOK == 1 */
/*-----------------------------------------------------------*/
#if ( configUSE_MALLOC_FAILED_HOOK == 1 || configCHECK_FOR_STACK_OVERFLOW >= 1 || configDEFAULT_ASSERT == 1 )
/**
* Private function to enable board led to use it in application hooks
*/
void prvSetMainLedOn( void )
{
gpio_init(LED_BUILTIN);
gpio_set_dir(LED_BUILTIN, true);
gpio_put(LED_BUILTIN, true);
}
/**
* Private function to blink board led to use it in application hooks
*/
void prvBlinkMainLed( void )
{
gpio_put(LED_BUILTIN, !gpio_get(LED_BUILTIN));
}
#endif
/*---------------------------------------------------------------------------*\
Usage:
called on fatal error (interrupts disabled already)
\*---------------------------------------------------------------------------*/
extern "C"
void rtosFatalError(void)
{
prvSetMainLedOn(); // Main LED on.
for(;;)
{
// Main LED slow flash
sleep_ms(100);
prvBlinkMainLed();
sleep_ms(2000);
prvBlinkMainLed();
}
}
#if ( configUSE_MALLOC_FAILED_HOOK == 1 )
/*---------------------------------------------------------------------------*\
Usage:
called by task system when a malloc failure is noticed
Description:
Malloc failure handler -- Shut down all interrupts, send serious complaint
to command port. FAST Blink on main LED.
Arguments:
pxTask - pointer to task handle
pcTaskName - pointer to task name
Results:
<none>
Notes:
This routine will never return.
This routine is referenced in the task.c file of FreeRTOS as an extern.
\*---------------------------------------------------------------------------*/
extern "C"
void vApplicationMallocFailedHook( void ) __attribute__((weak));
void vApplicationMallocFailedHook( void )
{
prvSetMainLedOn(); // Main LED on.
for(;;)
{
sleep_ms(50);
prvBlinkMainLed(); // Main LED fast blink.
}
}
#endif /* configUSE_MALLOC_FAILED_HOOK == 1 */
/*-----------------------------------------------------------*/
#if ( configCHECK_FOR_STACK_OVERFLOW >= 1 )
extern "C"
void vApplicationStackOverflowHook( TaskHandle_t xTask,
char * pcTaskName ) __attribute__((weak));
void vApplicationStackOverflowHook( TaskHandle_t xTask __attribute__((unused)),
char * pcTaskName __attribute__((unused)) )
{
prvSetMainLedOn(); // Main LED on.
for(;;)
{
sleep_ms(2000);
prvBlinkMainLed(); // Main LED slow blink.
}
}
#endif /* configCHECK_FOR_STACK_OVERFLOW >= 1 */
/*-----------------------------------------------------------*/
#if ( configSUPPORT_STATIC_ALLOCATION >= 1 )
extern "C"
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
configSTACK_DEPTH_TYPE * pulIdleTaskStackSize ) __attribute__((weak));
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
configSTACK_DEPTH_TYPE * pulIdleTaskStackSize )
{
static StaticTask_t xIdleTaskTCB;
static StackType_t uxIdleTaskStack[ configMINIMAL_STACK_SIZE ];
*ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
*ppxIdleTaskStackBuffer = uxIdleTaskStack;
*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}
#if ( configUSE_TIMERS >= 1 )
extern "C"
void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer,
StackType_t ** ppxTimerTaskStackBuffer,
configSTACK_DEPTH_TYPE * pulTimerTaskStackSize ) __attribute__((weak));
void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer,
StackType_t ** ppxTimerTaskStackBuffer,
configSTACK_DEPTH_TYPE * pulTimerTaskStackSize )
{
static StaticTask_t xTimerTaskTCB;
static StackType_t uxTimerTaskStack[ configTIMER_TASK_STACK_DEPTH ];
*ppxTimerTaskTCBBuffer = &xTimerTaskTCB;
*ppxTimerTaskStackBuffer = uxTimerTaskStack;
*pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
}
#endif /* configUSE_TIMERS >= 1 */
#endif /* configSUPPORT_STATIC_ALLOCATION >= 1 */
/**
* configASSERT default implementation
*/
#if configDEFAULT_ASSERT == 1
extern "C"
void vApplicationAssertHook() {
taskDISABLE_INTERRUPTS(); // Disable task interrupts
prvSetMainLedOn(); // Main LED on.
for(;;)
{
sleep_ms(100);
prvBlinkMainLed(); // Led off.
sleep_ms(2000);
prvBlinkMainLed(); // Led on.
sleep_ms(100);
prvBlinkMainLed(); // Led off
sleep_ms(100);
prvBlinkMainLed(); // Led on.
}
}
#endif
static void __usb_irq() {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR( __usbTask, &xHigherPriorityTaskWoken );
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
static void __usb(void *param)
{
(void) param;
tusb_init();
irq_set_exclusive_handler(USB_TASK_IRQ, __usb_irq);
irq_set_enabled(USB_TASK_IRQ, true);
Serial.begin(115200);
__usbInitted = true;
while (true) {
if (mutex_try_enter(&__usb_mutex, NULL)) {
tud_task();
mutex_exit(&__usb_mutex);
}
vTaskDelay(1 / portTICK_PERIOD_MS);
}
}
extern void __SetupDescHIDReport();
extern void __SetupUSBDescriptor();
void __USBStart()
{
mutex_init(&__usb_mutex);
__SetupDescHIDReport();
__SetupUSBDescriptor();
// Make highest prio and locked to core 0
xTaskCreate(__usb, "USB", 256, 0, configMAX_PRIORITIES - 1, &__usbTask);
vTaskCoreAffinitySet( __usbTask, 1 << 0 );
}
@@ -0,0 +1,37 @@
/*
I2S stereo microphone (input) example
Run using the Arduino Serial Plotter to see waveform.
Released to the Public Domain by Earle F. Philhower, III
For the Google AIY Voice Hat Microphone daughterboard, part
of the Raspberry Pi AIY cardboard box, the I2S stereo pinout
looking at the board top with the RPI logo on the left hand
side:
+-- ------------------------------------ --+
left RPI | (1) GND (2) DIN (3) BCLK (4) LRCLK (5) 3.3V | AIY right
logo +---------------------------------------------+ logo
*/
#include <I2S.h>
I2S i2s(INPUT);
void setup() {
Serial.begin(115200);
i2s.setDATA(0);
i2s.setBCLK(1); // LRCLK = GP2
i2s.setBitsPerSample(16);
i2s.setFrequency(22050);
i2s.begin();
while (1) {
int16_t l, r;
i2s.read16(&l, &r);
Serial.printf("%d %d\n", l, r);
}
}
void loop() {
/* Nothing here */
}
@@ -5,29 +5,54 @@
created 17 November 2016
by Sandeep Mistry
modified for ESP8266 by Earle F. Philhower, III <earlephilhower@yahoo.com>
modified for RP2040 by Earle F. Philhower, III <earlephilhower@yahoo.com>
bool setBCLK(pin_size_t pin);
- This assigns two adjacent pins - the pin after this one (one greater)
is the WS (word select) signal, which toggles before the sample for
each channel is sent
bool setDATA(pin_size_t pin);
- Sets the DOUT pin, can be any valid GPIO pin
*/
#include <I2S.h>
// Create the I2S port using a PIO state machine
I2S i2s(OUTPUT);
// GPIO pin numbers
#define pBCLK 20
#define pWS (pBCLK+1)
#define pDOUT 22
const int frequency = 440; // frequency of square wave in Hz
const int amplitude = 500; // amplitude of square wave
const int sampleRate = 8000; // sample rate in Hz
const int sampleRate = 16000; // minimum for UDA1334A
const int halfWavelength = (sampleRate / frequency); // half wavelength of square wave
short sample = amplitude; // current sample value
int16_t sample = amplitude; // current sample value
int count = 0;
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, 1);
Serial.begin(115200);
Serial.println("I2S simple tone");
i2s.setBCLK(pBCLK);
i2s.setDATA(pDOUT);
i2s.setBitsPerSample(16);
// start I2S at the sample rate with 16-bits per sample
if (!I2S.begin(sampleRate)) {
if (!i2s.begin(sampleRate)) {
Serial.println("Failed to initialize I2S!");
while (1); // do nothing
}
}
void loop() {
@@ -37,10 +62,9 @@ void loop() {
}
// write the same sample twice, once for left and once for the right channel
I2S.write(sample);
I2S.write(sample);
i2s.write(sample);
i2s.write(sample);
// increment the counter for the next sample
count++;
}
+15 -1
View File
@@ -14,10 +14,24 @@ I2S KEYWORD1
begin KEYWORD2
end KEYWORD2
setBCLK KEYWORD2
setDATA KEYWORD2
setBitsPerSample KEYWORD2
setFrequency KEYWORD2
read8 KEYWORD2
read16 KEYWORD2
read24 KEYWORD2
read32 KEYWORD2
write8 KEYWORD2
write16 KEYWORD2
write24 KEYWORD2
write32 KEYWORD2
onReceive KEYWORD2
onTransmit KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
I2S_PHILIPS_MODE LITERAL1
+3 -2
View File
@@ -1,9 +1,10 @@
name=I2S
version=1.0
version=2.0
author=Earle F. Philhower, III <earlephilhower@yahoo.com>
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
sentence=Enables the communication with devices that use the Inter-IC Sound (I2S) Bus. Specific implementation for RP2040, based off of pico-extras samples.
sentence=Enables the communication with devices that use the Inter-IC Sound (I2S) Bus.
paragraph=
category=Communication
url=http://www.arduino.cc/en/Reference/I2S
architectures=rp2040
+256
View File
@@ -0,0 +1,256 @@
/*
AudioRingBuffer for Raspnerry Pi Pico RP2040
Implements a ring buffer for PIO DMA for I2S read or write
Copyright (c) 2022 Earle F. Philhower, III <earlephilhower@yahoo.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <Arduino.h>
#include <vector>
#include "hardware/dma.h"
#include "hardware/irq.h"
#include "hardware/pio.h"
#include "pio_i2s.pio.h"
#include "AudioRingBuffer.h"
static int __channelCount = 0; // # of channels left. When we hit 0, then remove our handler
static AudioRingBuffer* __channelMap[12]; // Lets the IRQ handler figure out where to dispatch to
AudioRingBuffer::AudioRingBuffer(size_t bufferCount, size_t bufferWords, int32_t silenceSample, PinMode direction) {
_running = false;
_silenceSample = silenceSample;
_bufferCount = bufferCount;
_wordsPerBuffer = bufferWords;
_isOutput = direction == OUTPUT;
_overunderflow = false;
_callback = nullptr;
_userBuffer = -1;
_userOff = 0;
for (size_t i = 0; i < bufferCount; i++) {
auto ab = new AudioBuffer;
ab->buff = new uint32_t[_wordsPerBuffer];
ab->empty = true;
_buffers.push_back(ab);
}
}
AudioRingBuffer::~AudioRingBuffer() {
if (_running) {
for (auto i = 0; i < 2; i++) {
dma_channel_set_irq0_enabled(_channelDMA[i], false);
dma_channel_unclaim(_channelDMA[i]);
__channelMap[_channelDMA[i]] = nullptr;
}
while (_buffers.size()) {
auto ab = _buffers.back();
_buffers.pop_back();
delete[] ab->buff;
delete ab;
}
__channelCount--;
if (!__channelCount) {
irq_set_enabled(DMA_IRQ_0, false);
// TODO - how can we know if there are no other parts of the core using DMA0 IRQ??
irq_remove_handler(DMA_IRQ_0, _irq);
}
}
}
void AudioRingBuffer::setCallback(void (*fn)()) {
_callback = fn;
}
bool AudioRingBuffer::begin(int dreq, volatile void *pioFIFOAddr) {
_running = true;
// Set all buffers to silence, empty
for (auto buff : _buffers) {
buff->empty = true;
if (_isOutput) {
for (uint32_t x = 0; x < _wordsPerBuffer; x++) {
buff->buff[x] = _silenceSample;
}
}
}
// Get ping and pong DMA channels
for (auto i = 0; i < 2; i++) {
_channelDMA[i] = dma_claim_unused_channel(true);
if (_channelDMA[i] == -1) {
if (i == 1) {
dma_channel_unclaim(_channelDMA[0]);
}
return false;
}
}
bool needSetIRQ = __channelCount == 0;
// Need to know both channels to set up ping-pong, so do in 2 stages
for (auto i = 0; i < 2; i++) {
dma_channel_config c = dma_channel_get_default_config(_channelDMA[i]);
channel_config_set_transfer_data_size(&c, DMA_SIZE_32); // 32b transfers into PIO FIFO
if (_isOutput) {
channel_config_set_read_increment(&c, true); // Reading incrementing addresses
channel_config_set_write_increment(&c, false); // Writing to the same FIFO address
} else {
channel_config_set_read_increment(&c, false); // Reading same FIFO address
channel_config_set_write_increment(&c, true); // Writing to incrememting buffers
}
channel_config_set_dreq(&c, dreq); // Wait for the PIO TX FIFO specified
channel_config_set_chain_to(&c, _channelDMA[i ^ 1]); // Start other channel when done
channel_config_set_irq_quiet(&c, false); // Need IRQs
if (_isOutput) {
dma_channel_configure(_channelDMA[i], &c, pioFIFOAddr, _buffers[i]->buff, _wordsPerBuffer, false);
} else {
dma_channel_configure(_channelDMA[i], &c, _buffers[i]->buff, pioFIFOAddr, _wordsPerBuffer, false);
}
dma_channel_set_irq0_enabled(_channelDMA[i], true);
__channelMap[_channelDMA[i]] = this;
__channelCount++;
}
if (needSetIRQ) {
irq_add_shared_handler(DMA_IRQ_0, _irq, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
irq_set_enabled(DMA_IRQ_0, true);
}
_curBuffer = 0;
_nextBuffer = 2 % _bufferCount;
dma_channel_start(_channelDMA[0]);
return true;
}
bool AudioRingBuffer::write(uint32_t v, bool sync) {
if (!_running || !_isOutput) {
return false;
}
if (_userBuffer == -1) {
// First write or overflow, pick spot 2 buffers out
_userBuffer = (_nextBuffer + 2) % _bufferCount;
_userOff = 0;
}
if (!_buffers[_userBuffer]->empty) {
if (!sync) {
return false;
} else {
while (!_buffers[_userBuffer]->empty) {
/* noop busy wait */
}
}
}
if (_userBuffer == _curBuffer) {
if (!sync) {
return false;
} else {
while (_userBuffer == _curBuffer) {
/* noop busy wait */
}
}
}
_buffers[_userBuffer]->buff[_userOff++] = v;
if (_userOff == _wordsPerBuffer) {
_buffers[_userBuffer]->empty = false;
_userBuffer = (_userBuffer + 1) % _bufferCount;
_userOff = 0;
}
return true;
}
bool AudioRingBuffer::read(uint32_t *v, bool sync) {
if (!_running || _isOutput) {
return false;
}
if (_userBuffer == -1) {
// First write or overflow, pick last filled buffer
_userBuffer = (_curBuffer - 1 + _bufferCount) % _bufferCount;
_userOff = 0;
}
if (_buffers[_userBuffer]->empty) {
if (!sync) {
return false;
} else {
while (_buffers[_userBuffer]->empty) {
/* noop busy wait */
}
}
}
if (_userBuffer == _curBuffer) {
if (!sync) {
return false;
} else {
while (_userBuffer == _curBuffer) {
/* noop busy wait */
}
}
}
auto ret = _buffers[_userBuffer]->buff[_userOff++];
if (_userOff == _wordsPerBuffer) {
_buffers[_userBuffer]->empty = true;
_userBuffer = (_userBuffer + 1) % _bufferCount;
_userOff = 0;
}
*v = ret;
return true;
}
bool AudioRingBuffer::getOverUnderflow() {
bool hold = _overunderflow;
_overunderflow = false;
return hold;
}
int AudioRingBuffer::available() {
if (!_running) {
return 0;
}
int avail;
avail = _wordsPerBuffer - _userOff;
avail += ((_bufferCount + _curBuffer - _userBuffer) % _bufferCount) * _wordsPerBuffer;
return avail;
}
void AudioRingBuffer::flush() {
while (_curBuffer != _userBuffer) {
// busy wait
}
}
void __not_in_flash_func(AudioRingBuffer::_dmaIRQ)(int channel) {
if (_isOutput) {
for (uint32_t x = 0; x < _wordsPerBuffer; x++) {
_buffers[_curBuffer]->buff[x] = _silenceSample;
}
_buffers[_curBuffer]-> empty = true;
_overunderflow = _overunderflow | _buffers[_nextBuffer]->empty;
dma_channel_set_read_addr(channel, _buffers[_nextBuffer]->buff, false);
} else {
_buffers[_curBuffer]-> empty = false;
_overunderflow = _overunderflow | !_buffers[_nextBuffer]->empty;
dma_channel_set_write_addr(channel, _buffers[_nextBuffer]->buff, false);
}
dma_channel_set_trans_count(channel, _wordsPerBuffer, false);
_curBuffer = (_curBuffer + 1) % _bufferCount;
_nextBuffer = (_nextBuffer + 1) % _bufferCount;
dma_channel_acknowledge_irq0(channel);
if (_callback) {
_callback();
}
}
void __not_in_flash_func(AudioRingBuffer::_irq)() {
for (size_t i = 0; i < sizeof(__channelMap); i++) {
if (dma_channel_get_irq0_status(i) && __channelMap[i]) {
__channelMap[i]->_dmaIRQ(i);
}
}
}
+69
View File
@@ -0,0 +1,69 @@
/*
AudioRingBuffer for Rasperry Pi Pico
Implements a ring buffer for PIO DMA for I2S read or write
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 <Arduino.h>
#include <vector>
class AudioRingBuffer {
public:
AudioRingBuffer(size_t bufferCount, size_t bufferWords, int32_t silenceSample, PinMode direction = OUTPUT);
~AudioRingBuffer();
void setCallback(void (*fn)());
bool begin(int dreq, volatile void *pioFIFOAddr);
bool write(uint32_t v, bool sync = true);
bool read(uint32_t *v, bool sync = true);
void flush();
bool getOverUnderflow();
int available();
private:
void _dmaIRQ(int channel);
static void _irq();
typedef struct {
uint32_t *buff;
volatile bool empty;
} AudioBuffer;
bool _running = false;
std::vector<AudioBuffer*> _buffers;
volatile int _curBuffer;
volatile int _nextBuffer;
size_t _chunkSampleCount;
int _bitsPerSample;
size_t _wordsPerBuffer;
size_t _bufferCount;
bool _isOutput;
int32_t _silenceSample;
int _channelDMA[2];
void (*_callback)();
bool _overunderflow;
// User buffer pointer
int _userBuffer = -1;
size_t _userOff = 0;
};
+280 -156
View File
@@ -1,7 +1,8 @@
/*
I2S Master library for the Raspberry Pi Pico RP2040
I2SIn and I2SOut for Raspberry Pi Pico
Implements one or more I2S interfaces using DMA
Copyright (c) 2021 Earle F. Philhower, III <earlephilhower@yahoo.com>
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
@@ -17,30 +18,30 @@
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <Arduino.h>
#include "I2S.h"
#include "pio_i2s.pio.h"
#ifdef USE_TINYUSB
// For Serial when selecting TinyUSB. Can't include in the core because Arduino IDE
// will not link in libraries called from the core. Instead, add the header to all
// the standard libraries in the hope it will still catch some user cases where they
// use these libraries.
// See https://github.com/earlephilhower/arduino-pico/issues/167#issuecomment-848622174
#include <Adafruit_TinyUSB.h>
#endif
I2SClass::I2SClass() {
I2S::I2S(PinMode direction) {
_running = false;
_pool = nullptr;
_curBuff = nullptr;
_bps = 0;
_bps = 16;
_writtenHalf = false;
_pinBCLK = 26;
_pinDOUT = 28;
_freq = 48000;
_arb = nullptr;
_isOutput = direction == OUTPUT;
_cb = nullptr;
_buffers = 8;
_bufferWords = 16;
_silenceSample = 0;
}
bool I2SClass::setBCLK(pin_size_t pin) {
I2S::~I2S() {
}
bool I2S::setBCLK(pin_size_t pin) {
if (_running || (pin > 28)) {
return false;
}
@@ -48,7 +49,7 @@ bool I2SClass::setBCLK(pin_size_t pin) {
return true;
}
bool I2SClass::setDOUT(pin_size_t pin) {
bool I2S::setDATA(pin_size_t pin) {
if (_running || (pin > 29)) {
return false;
}
@@ -56,173 +57,296 @@ bool I2SClass::setDOUT(pin_size_t pin) {
return true;
}
bool I2SClass::begin(long sampleRate) {
bool I2S::setBitsPerSample(int bps) {
if (_running || ((bps != 8) && (bps != 16) && (bps != 24) && (bps != 32))) {
return false;
}
_bps = bps;
return true;
}
bool I2S::setBuffers(size_t buffers, size_t bufferWords, int32_t silenceSample) {
if (_running || (buffers < 3) || (bufferWords < 8)) {
return false;
}
_buffers = buffers;
_bufferWords = bufferWords;
_silenceSample = silenceSample;
return true;
}
bool I2S::setFrequency(int newFreq) {
_freq = newFreq;
if (_running) {
return false;
float bitClk = _freq * _bps * 2.0;
pio_sm_set_clkdiv(_pio, _sm, (float)clock_get_hz(clk_sys) / bitClk);
}
return true;
}
bzero(&_audio_format, sizeof(_audio_format));
_audio_format.sample_freq = (uint32_t)sampleRate;
_audio_format.format = AUDIO_BUFFER_FORMAT_PCM_S16;
_audio_format.channel_count = 2;
bzero(&_producer_format, sizeof(_producer_format));
_producer_format.format = &_audio_format;
_producer_format.sample_stride = 4;
if (!_pool) {
_pool = audio_new_producer_pool(&_producer_format, 3, 256);
void I2S::onTransmit(void(*fn)(void)) {
if (_isOutput) {
_cb = fn;
if (_running) {
_arb->setCallback(_cb);
}
}
}
bzero(&_config, sizeof(_config));
_config.data_pin = _pinDOUT;
_config.clock_pin_base = _pinBCLK;
_config.dma_channel = 0;
_config.pio_sm = 1;
if (!audio_i2s_setup(&_audio_format, &_config)) {
return false;
void I2S::onReceive(void(*fn)(void)) {
if (!_isOutput) {
_cb = fn;
if (_running) {
_arb->setCallback(_cb);
}
}
}
if (!audio_i2s_connect(_pool)) {
return false;
}
audio_i2s_set_enabled(true);
_curBuff = take_audio_buffer(_pool, true);
_curBuff->sample_count = 0;
_bps = 16;
_freq = sampleRate;
bool I2S::begin() {
_running = true;
_hasPeeked = false;
int off = 0;
_i2s = new PIOProgram(_isOutput ? &pio_i2s_out_program : &pio_i2s_in_program);
_i2s->prepare(&_pio, &_sm, &off);
if (_isOutput) {
pio_i2s_out_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps);
} else {
pio_i2s_in_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps);
}
setFrequency(_freq);
if (_bps == 8) {
uint8_t a = _silenceSample & 0xff;
_silenceSample = (a << 24) | (a << 16) | (a << 8) | a;
} else if (_bps == 16) {
uint16_t a = _silenceSample & 0xffff;
_silenceSample = (a << 16) | a;
}
_arb = new AudioRingBuffer(_buffers, _bufferWords, _silenceSample, _isOutput ? OUTPUT : INPUT);
_arb->begin(pio_get_dreq(_pio, _sm, _isOutput), _isOutput ? &_pio->txf[_sm] : (volatile void*)&_pio->rxf[_sm]);
_arb->setCallback(_cb);
pio_sm_set_enabled(_pio, _sm, true);
return true;
}
void I2SClass::end() {
if (_running) {
audio_i2s_set_enabled(false);
if (_curBuff) {
release_audio_buffer(_pool, _curBuff);
_curBuff = nullptr;
}
}
void I2S::end() {
_running = false;
_bps = 0;
_freq = 0;
_writtenHalf = false;
delete _arb;
_arb = nullptr;
delete _i2s;
_i2s = nullptr;
}
int I2SClass::availableForWrite() {
if (!_running) {
int I2S::available() {
if (!_running || _isOutput) {
return 0;
}
// Can we get a whole new buffer to work with?
if (!_curBuff) {
_curBuff = take_audio_buffer(_pool, false);
_curBuff->sample_count = 0;
}
if (!_curBuff) {
return false;
}
return _curBuff->max_sample_count - _curBuff->sample_count;
return _arb->available();
}
void I2SClass::flush() {
if (!_curBuff || !_curBuff->sample_count) {
return;
}
_audio_format.sample_freq = _freq;
give_audio_buffer(_pool, _curBuff);
_curBuff = nullptr;
}
bool I2SClass::setFrequency(int newFreq) {
if (newFreq != _freq) {
flush();
_freq = newFreq;
}
return true;
}
size_t I2SClass::write(uint8_t s) {
return write((int16_t)s);
}
size_t I2SClass::write(const uint8_t *buffer, size_t size) {
return write((const void *)buffer, size);
}
size_t I2SClass::write(int16_t s) {
if (!_running) {
int I2S::read() {
if (!_running || _isOutput) {
return 0;
}
// Because our HW really wants 32b writes, store any 16b writes until another
// 16b write comes in and then send the combined write down.
if (_bps == 16) {
if (_writtenHalf) {
_writtenData <<= 16;
_writtenData |= 0xffff & s;
_writtenHalf = false;
if (!_curBuff) {
_curBuff = take_audio_buffer(_pool, true);
_curBuff->sample_count = 0;
}
int32_t *samples = (int32_t *)_curBuff->buffer->bytes;
samples[_curBuff->sample_count++] = _writtenData;
if (_curBuff->sample_count == _curBuff->max_sample_count) {
_audio_format.sample_freq = _freq;
give_audio_buffer(_pool, _curBuff);
_curBuff = nullptr;
}
if (_hasPeeked) {
_hasPeeked = false;
return _peekSaved;
}
if (_wasHolding <= 0) {
read(&_holdWord, true);
_wasHolding = 32;
}
int ret;
switch (_bps) {
case 8:
ret = _holdWord >> 24;
_holdWord <<= 8;
_wasHolding -= 8;
return ret;
case 16:
ret = _holdWord >> 16;
_holdWord <<= 16;
_wasHolding -= 32;
return ret;
case 24:
case 32:
default:
ret = _holdWord;
_wasHolding = 0;
return ret;
}
}
int I2S::peek() {
if (!_running || _isOutput) {
return 0;
}
if (!_hasPeeked) {
_peekSaved = read();
_hasPeeked = true;
}
return _peekSaved;
}
void I2S::flush() {
if (_running) {
_arb->flush();
}
}
size_t I2S::_writeNatural(int32_t s) {
if (!_running || !_isOutput) {
return 0;
}
switch (_bps) {
case 8:
_holdWord |= s & 0xff;
if (_wasHolding >= 24) {
auto ret = write(_holdWord, true);
_holdWord = 0;
_wasHolding = 0;
return ret;
} else {
_writtenHalf = true;
_writtenData = s & 0xffff;
_holdWord <<= 8;
_wasHolding += 8;
return 1;
}
case 16:
_holdWord |= s & 0xffff;
if (_wasHolding) {
auto ret = write(_holdWord, true);
_holdWord = 0;
_wasHolding = 0;
return ret;
} else {
_holdWord <<= 16;
_wasHolding = 16;
return 1;
}
case 24:
case 32:
default:
return write(s, true);
}
}
size_t I2S::write(int32_t val, bool sync) {
if (!_running || !_isOutput) {
return 0;
}
return _arb->write(val, sync);
}
size_t I2S::write8(int8_t l, int8_t r) {
if (!_running || !_isOutput) {
return 0;
}
int16_t o = (l << 8) | (r & 0xff);
return write((int16_t) o);
}
size_t I2S::write16(int16_t l, int16_t r) {
if (!_running || !_isOutput) {
return 0;
}
int32_t o = (l << 16) | (r & 0xffff);
return write((int32_t)o, true);
}
size_t I2S::write24(int32_t l, int32_t r) {
return write32(l, r);
}
size_t I2S::write32(int32_t l, int32_t r) {
if (!_running || !_isOutput) {
return 0;
}
write((int32_t)l);
write((int32_t)r);
return 1;
}
// Mostly non-blocking
size_t I2SClass::write(const void *buffer, size_t size) {
if (!_running) {
size_t I2S::read(int32_t *val, bool sync) {
if (!_running || _isOutput) {
return 0;
}
// We have no choice here because we need to write at least 1 byte...
if (!_curBuff) {
_curBuff = take_audio_buffer(_pool, true);
_curBuff->sample_count = 0;
}
int32_t *inSamples = (int32_t *)buffer;
int written = 0;
int wantToWrite = size / 4;
while (wantToWrite) {
if (!_curBuff) {
_curBuff = take_audio_buffer(_pool, false);
if (_curBuff) {
_curBuff->sample_count = 0;
} else {
break;
}
}
int avail = _curBuff->max_sample_count - _curBuff->sample_count;
int writeSize = (avail > wantToWrite) ? wantToWrite : avail;
int32_t *samples = (int32_t *)_curBuff->buffer->bytes;
memcpy(samples + _curBuff->sample_count, inSamples, writeSize * 4);
_curBuff->sample_count += writeSize;
inSamples += writeSize;
written += writeSize;
wantToWrite -= writeSize;
if (_curBuff->sample_count == _curBuff->max_sample_count) {
_audio_format.sample_freq = _freq;
give_audio_buffer(_pool, _curBuff);
_curBuff = nullptr;
}
}
return written;
return _arb->read((uint32_t *)val, sync);
}
I2SClass I2S;
bool I2S::read8(int8_t *l, int8_t *r) {
if (!_running || _isOutput) {
return false;
}
if (_wasHolding) {
*l = (_holdWord >> 8) & 0xff;
*r = (_holdWord >> 0) & 0xff;
_wasHolding = 0;
} else {
read(&_holdWord, true);
_wasHolding = 16;
*l = (_holdWord >> 24) & 0xff;
*r = (_holdWord >> 16) & 0xff;
}
return true;
}
bool I2S::read16(int16_t *l, int16_t *r) {
if (!_running || _isOutput) {
return false;
}
int32_t o;
read(&o, true);
*l = (o >> 16) & 0xffff;
*r = (o >> 0) & 0xffff;
return true;
}
bool I2S::read24(int32_t *l, int32_t *r) {
if (!_running || _isOutput) {
return false;
}
read32(l, r);
// 24-bit samples are read right-aligned, so left-align them to keep the binary point between 33.32
*l <<= 8;
*r <<= 8;
return true;
}
bool I2S::read32(int32_t *l, int32_t *r) {
if (!_running || _isOutput) {
return false;
}
read(l, true);
read(r, true);
return true;
}
size_t I2S::write(const uint8_t *buffer, size_t size) {
// We can only write 32-bit chunks here
if (size & 0x3) {
return 0;
}
size_t writtenSize = 0;
int32_t *p = (int32_t *)buffer;
while (size) {
if (!write((int32_t)*p)) {
// Blocked, stop write here
return writtenSize;
} else {
p++;
size -= 4;
writtenSize += 4;
}
}
return writtenSize;
}
int I2S::availableForWrite() {
if (!_running || !_isOutput) {
return 0;
}
return _arb->available();
}
+80 -42
View File
@@ -1,7 +1,8 @@
/*
I2S Master library for the Raspberry Pi Pico RP2040
I2SIn and I2SOut for Raspberry Pi Pico
Implements one or more I2S interfaces using DMA
Copyright (c) 2021 Earle F. Philhower, III <earlephilhower@yahoo.com>
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
@@ -19,72 +20,109 @@
*/
#pragma once
#include <Arduino.h>
#include "pico/audio_i2s.h"
#include <vector>
#include "AudioRingBuffer.h"
class I2SClass : public Stream {
class I2S : public Stream {
public:
I2SClass();
I2S(PinMode direction = OUTPUT);
virtual ~I2S();
// Only 16 bitsPerSample are allowed by the PIO code. Only write, no read.
bool setBCLK(pin_size_t pin);
bool setDOUT(pin_size_t pin);
bool begin(long sampleRate);
bool setDATA(pin_size_t pin);
bool setBitsPerSample(int bps);
bool setBuffers(size_t buffers, size_t bufferWords, int32_t silenceSample = 0);
bool setFrequency(int newFreq);
bool begin(long sampleRate) {
setFrequency(sampleRate);
return begin();
}
bool begin();
void end();
// from Stream
virtual int available() override {
return -1;
}
virtual int read() override {
return -1;
}
virtual int peek() override {
return -1;
}
virtual int available() override;
virtual int read() override;
virtual int peek() override;
virtual void flush() override;
// from Print (see notes on write() methods below)
virtual size_t write(uint8_t) override;
virtual size_t write(const uint8_t *buffer, size_t size) override;
virtual int availableForWrite() override;
bool setFrequency(int newFreq);
// Try and make I2S::write() do what makes sense, namely write
// one sample (L or R) at the I2S configured bit width
virtual size_t write(uint8_t s) override {
return _writeNatural(s & 0xff);
}
size_t write(int8_t s) {
return write((uint8_t)s);
}
size_t write(uint16_t s) {
return _writeNatural(s & 0xffff);
}
size_t write(int16_t s) {
return write((uint16_t)s);
}
size_t write(uint32_t s) {
return _writeNatural(s);
}
size_t write(int32_t s) {
return write((uint32_t)s);
}
// Write a single L:R sample to the I2S device. Blocking until write succeeds
size_t write(int16_t);
// Write up to size samples to the I2S device. Non-blocking, will write
// from 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.
size_t write(const void *buffer, size_t lrsamples);
// Write 32 bit value to port, user responsbile for packing/alignment, etc.
size_t write(int32_t val, bool sync);
// Write sample to I2S port, will block until completed
size_t write8(int8_t l, int8_t r);
size_t write16(int16_t l, int16_t r);
size_t write24(int32_t l, int32_t r); // Note that 24b must have values left-aligned (i.e. 0xABCDEF00)
size_t write32(int32_t l, int32_t r);
// Read 32 bit value to port, user responsbile for packing/alignment, etc.
size_t read(int32_t *val, bool sync);
// Read samples from I2S port, will block until data available
bool read8(int8_t *l, int8_t *r);
bool read16(int16_t *l, int16_t *r);
bool read24(int32_t *l, int32_t *r); // Note that 24b reads will be left-aligned (see above)
bool read32(int32_t *l, int32_t *r);
// Note that these callback are called from **INTERRUPT CONTEXT** and hence
// must be both stored in IRAM and not perform anything that's not legal in
// an interrupt
//void onTransmit(void(*)(void)); -- Not yet implemented, need to edit pico-extra to get callback
//void onReceive(void(*)(void)); -- no I2S input yet
// should be in RAM, not FLASH, and should be quick to execute.
void onTransmit(void(*)(void));
void onReceive(void(*)(void));
private:
pin_size_t _pinBCLK;
pin_size_t _pinDOUT;
int _bps;
int _freq;
size_t _buffers;
size_t _bufferWords;
int32_t _silenceSample;
bool _isOutput;
bool _running;
// Audio format/pool config
audio_format_t _audio_format;
audio_buffer_format_t _producer_format;
audio_i2s_config_t _config;
bool _hasPeeked;
int32_t _peekSaved;
// We manage our own buffer pool here...
audio_buffer_pool_t *_pool;
audio_buffer_t *_curBuff;
// Support for ::write(x) on 16b quantities
size_t _writeNatural(int32_t s);
uint32_t _writtenData;
bool _writtenHalf;
};
extern I2SClass I2S;
int32_t _holdWord = 0;
int _wasHolding = 0;
void (*_cb)();
AudioRingBuffer *_arb;
PIOProgram *_i2s;
PIO _pio;
int _sm;
};
+112
View File
@@ -0,0 +1,112 @@
; pio_i2s for the Raspberry Pi Pico RP2040
;
; Based loosely off of the MicroPython I2S code in
; https://github.com/micropython/micropython/blob/master/ports/rp2/machine_i2s.c
;
; 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
.program pio_i2s_out
.side_set 2 ; 0 = bclk, 1=wclk
; The C code should place (number of bits/sample - 2) in Y and
; also update the SHIFTCTRL to be 24 or 32 as appropriate
; +----- WCLK
; |+---- BCLK
mov x, y side 0b01
left:
out pins, 1 side 0b00
jmp x--, left side 0b01
out pins, 1 side 0b10 ; Last bit of left has WCLK change per I2S spec
mov x, y side 0b11
right:
out pins, 1 side 0b10
jmp x--, right side 0b11
out pins, 1 side 0b00 ; Last bit of right also has WCLK change
; Loop back to beginning...
.program pio_i2s_in ; Note this is the same as _out, just "in" and not "out"
.side_set 2 ; 0 = bclk, 1=wclk
; The C code should place (number of bits/sample - 2) in Y and
; also update the SHIFTCTRL to be 24 or 32 as appropriate
; +----- WCLK
; |+---- BCLK
mov x, y side 0b00
left:
in pins, 1 side 0b01
jmp x--, left side 0b00
in pins, 1 side 0b11 ; Last bit of left has WCLK change per I2S spec
mov x, y side 0b10
right:
in pins, 1 side 0b11
jmp x--, right side 0b10
in pins, 1 side 0b01 ; Last bit of right also has WCLK change
; Loop back to beginning...
% c-sdk {
static inline void pio_i2s_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits) {
pio_gpio_init(pio, data_pin);
pio_gpio_init(pio, clock_pin_base);
pio_gpio_init(pio, clock_pin_base + 1);
pio_sm_config sm_config = pio_i2s_out_program_get_default_config(offset);
sm_config_set_out_pins(&sm_config, data_pin, 1);
sm_config_set_sideset_pins(&sm_config, clock_pin_base);
sm_config_set_out_shift(&sm_config, false, true, (bits <= 16) ? 2 * bits : bits);
sm_config_set_fifo_join(&sm_config, PIO_FIFO_JOIN_TX);
pio_sm_init(pio, sm, offset, &sm_config);
uint pin_mask = (1u << data_pin) | (3u << clock_pin_base);
pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask);
pio_sm_set_pins(pio, sm, 0); // clear pins
pio_sm_exec(pio, sm, pio_encode_set(pio_y, bits - 2));
}
static inline void pio_i2s_in_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits) {
pio_gpio_init(pio, data_pin);
pio_gpio_init(pio, clock_pin_base);
pio_gpio_init(pio, clock_pin_base + 1);
pio_sm_config sm_config = pio_i2s_in_program_get_default_config(offset);
sm_config_set_in_pins(&sm_config, data_pin);
sm_config_set_sideset_pins(&sm_config, clock_pin_base);
sm_config_set_in_shift(&sm_config, false, true, (bits <= 16) ? 2 * bits : bits);
sm_config_set_fifo_join(&sm_config, PIO_FIFO_JOIN_RX);
pio_sm_init(pio, sm, offset, &sm_config);
uint pin_mask = 3u << clock_pin_base;
pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask);
pio_sm_set_pins(pio, sm, 0); // clear pins
pio_sm_exec(pio, sm, pio_encode_set(pio_y, bits - 2));
}
%}
+112
View File
@@ -0,0 +1,112 @@
// -------------------------------------------------- //
// This file is autogenerated by pioasm; do not edit! //
// -------------------------------------------------- //
#pragma once
#if !PICO_NO_HARDWARE
#include "hardware/pio.h"
#endif
// ----------- //
// pio_i2s_out //
// ----------- //
#define pio_i2s_out_wrap_target 0
#define pio_i2s_out_wrap 7
static const uint16_t pio_i2s_out_program_instructions[] = {
// .wrap_target
0xa822, // 0: mov x, y side 1
0x6001, // 1: out pins, 1 side 0
0x0841, // 2: jmp x--, 1 side 1
0x7001, // 3: out pins, 1 side 2
0xb822, // 4: mov x, y side 3
0x7001, // 5: out pins, 1 side 2
0x1845, // 6: jmp x--, 5 side 3
0x6001, // 7: out pins, 1 side 0
// .wrap
};
#if !PICO_NO_HARDWARE
static const struct pio_program pio_i2s_out_program = {
.instructions = pio_i2s_out_program_instructions,
.length = 8,
.origin = -1,
};
static inline pio_sm_config pio_i2s_out_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + pio_i2s_out_wrap_target, offset + pio_i2s_out_wrap);
sm_config_set_sideset(&c, 2, false, false);
return c;
}
#endif
// ---------- //
// pio_i2s_in //
// ---------- //
#define pio_i2s_in_wrap_target 0
#define pio_i2s_in_wrap 7
static const uint16_t pio_i2s_in_program_instructions[] = {
// .wrap_target
0xa022, // 0: mov x, y side 0
0x4801, // 1: in pins, 1 side 1
0x0041, // 2: jmp x--, 1 side 0
0x5801, // 3: in pins, 1 side 3
0xb022, // 4: mov x, y side 2
0x5801, // 5: in pins, 1 side 3
0x1045, // 6: jmp x--, 5 side 2
0x4801, // 7: in pins, 1 side 1
// .wrap
};
#if !PICO_NO_HARDWARE
static const struct pio_program pio_i2s_in_program = {
.instructions = pio_i2s_in_program_instructions,
.length = 8,
.origin = -1,
};
static inline pio_sm_config pio_i2s_in_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + pio_i2s_in_wrap_target, offset + pio_i2s_in_wrap);
sm_config_set_sideset(&c, 2, false, false);
return c;
}
static inline void pio_i2s_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits) {
pio_gpio_init(pio, data_pin);
pio_gpio_init(pio, clock_pin_base);
pio_gpio_init(pio, clock_pin_base + 1);
pio_sm_config sm_config = pio_i2s_out_program_get_default_config(offset);
sm_config_set_out_pins(&sm_config, data_pin, 1);
sm_config_set_sideset_pins(&sm_config, clock_pin_base);
sm_config_set_out_shift(&sm_config, false, true, (bits <= 16) ? 2 * bits : bits);
sm_config_set_fifo_join(&sm_config, PIO_FIFO_JOIN_TX);
pio_sm_init(pio, sm, offset, &sm_config);
uint pin_mask = (1u << data_pin) | (3u << clock_pin_base);
pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask);
pio_sm_set_pins(pio, sm, 0); // clear pins
pio_sm_exec(pio, sm, pio_encode_set(pio_y, bits - 2));
}
static inline void pio_i2s_in_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits) {
pio_gpio_init(pio, data_pin);
pio_gpio_init(pio, clock_pin_base);
pio_gpio_init(pio, clock_pin_base + 1);
pio_sm_config sm_config = pio_i2s_in_program_get_default_config(offset);
sm_config_set_in_pins(&sm_config, data_pin);
sm_config_set_sideset_pins(&sm_config, clock_pin_base);
sm_config_set_in_shift(&sm_config, false, true, (bits <= 16) ? 2 * bits : bits);
sm_config_set_fifo_join(&sm_config, PIO_FIFO_JOIN_RX);
pio_sm_init(pio, sm, offset, &sm_config);
uint pin_mask = 3u << clock_pin_base;
pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask);
pio_sm_set_pins(pio, sm, 0); // clear pins
pio_sm_exec(pio, sm, pio_encode_set(pio_y, bits - 2));
}
#endif
+6
View File
@@ -58,6 +58,12 @@ private:
int _gain;
int _init;
// Hardware peripherals used
uint _dmaChannel;
PIO _pio;
int _smIdx;
int _pgmOffset;
PDMDoubleBuffer _doubleBuffer;
void (*_onReceive)(void);
+24 -18
View File
@@ -11,11 +11,7 @@ extern "C" {
}
#include "hardware/sync.h"
#include "pdm.pio.h"
// Hardware peripherals used
uint dmaChannel = 0;
PIO pio = pio0;
uint sm = 0;
static PIOProgram _pdmPgm(&pdm_pio_program);
// raw buffers contain PDM data
#define RAW_BUFFER_SIZE 512 // should be a multiple of (decimation / 8)
@@ -49,7 +45,11 @@ PDMClass::PDMClass(int dinPin, int clkPin, int pwrPin) :
_gain(-1),
_channels(-1),
_samplerate(-1),
_init(-1)
_init(-1),
_dmaChannel(0),
_pio(nullptr),
_smIdx(-1),
_pgmOffset(-1)
{
}
@@ -89,29 +89,35 @@ int PDMClass::begin(int channels, int sampleRate)
// Configure PIO state machine
float clkDiv = (float)clock_get_hz(clk_sys) / sampleRate / decimation / 2;
uint offset = pio_add_program(pio, &pdm_pio_program);
pdm_pio_program_init(pio, sm, offset, _clkPin, _dinPin, clkDiv);
if (!_pdmPgm.prepare(&_pio, &_smIdx, &_pgmOffset)) {
// ERROR, no free slots
return -1;
}
pdm_pio_program_init(_pio, _smIdx, _pgmOffset, _clkPin, _dinPin, clkDiv);
// Wait for microphone
delay(100);
// Configure DMA for transferring PIO rx buffer to raw buffers
dma_channel_config c = dma_channel_get_default_config(dmaChannel);
_dmaChannel = dma_claim_unused_channel(false);
dma_channel_config c = dma_channel_get_default_config(_dmaChannel);
channel_config_set_read_increment(&c, false);
channel_config_set_write_increment(&c, true);
channel_config_set_dreq(&c, pio_get_dreq(pio, sm, false));
channel_config_set_dreq(&c, pio_get_dreq(_pio, _smIdx, false));
channel_config_set_transfer_data_size(&c, DMA_SIZE_8);
// Clear DMA interrupts
dma_hw->ints0 = 1u << dmaChannel;
dma_hw->ints0 = 1u << _dmaChannel;
// Enable DMA interrupts
dma_channel_set_irq0_enabled(dmaChannel, true);
irq_set_exclusive_handler(DMA_IRQ_0, dmaHandler);
dma_channel_set_irq0_enabled(_dmaChannel, true);
// Share but allocate a high priority to the interrupt
irq_add_shared_handler(DMA_IRQ_0, dmaHandler, 0);
irq_set_enabled(DMA_IRQ_0, true);
dma_channel_configure(dmaChannel, &c,
dma_channel_configure(_dmaChannel, &c,
rawBuffer[rawBufferIndex], // Destinatinon pointer
&pio->rxf[sm], // Source pointer
&_pio->rxf[_smIdx], // Source pointer
RAW_BUFFER_SIZE, // Number of transfers
true // Start immediately
);
@@ -123,7 +129,7 @@ int PDMClass::begin(int channels, int sampleRate)
void PDMClass::end()
{
dma_channel_abort(dmaChannel);
dma_channel_abort(_dmaChannel);
pinMode(_clkPin, INPUT);
}
@@ -171,10 +177,10 @@ void PDMClass::IrqHandler(bool halftranfer)
static int cutSamples = 100;
// Clear the interrupt request.
dma_hw->ints0 = 1u << dmaChannel;
dma_hw->ints0 = 1u << _dmaChannel;
// Restart dma pointing to the other buffer
int shadowIndex = rawBufferIndex ^ 1;
dma_channel_set_write_addr(dmaChannel, rawBuffer[shadowIndex], true);
dma_channel_set_write_addr(_dmaChannel, rawBuffer[shadowIndex], true);
if (_doubleBuffer.available()) {
// buffer overflow, stop
+1
View File
@@ -26,6 +26,7 @@ static inline void pdm_pio_program_init(PIO pio, uint sm, uint offset, uint clkP
sm_config_set_in_pins(&c, dataPin);
sm_config_set_sideset_pins(&c, clkPin);
sm_config_set_clkdiv(&c, clkDiv);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);
pio_sm_set_consecutive_pindirs(pio, sm, dataPin, 1, false);
pio_sm_set_consecutive_pindirs(pio, sm, clkPin, 1, true);
+1
View File
@@ -43,6 +43,7 @@ static inline void pdm_pio_program_init(PIO pio, uint sm, uint offset, uint clkP
sm_config_set_in_pins(&c, dataPin);
sm_config_set_sideset_pins(&c, clkPin);
sm_config_set_clkdiv(&c, clkDiv);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);
pio_sm_set_consecutive_pindirs(pio, sm, dataPin, 1, false);
pio_sm_set_consecutive_pindirs(pio, sm, clkPin, 1, true);
pio_sm_set_pins_with_mask(pio, sm, 0, (1u << clkPin) );
@@ -307,5 +307,12 @@ void SPIClassRP2040::setClockDivider(uint8_t uc_div) {
(void) uc_div; // no-op
}
SPIClassRP2040 SPI(spi0, PIN_SPI0_MISO, PIN_SPI0_SS, PIN_SPI0_SCK, PIN_SPI0_MOSI);
SPIClassRP2040 SPI1(spi1, PIN_SPI1_MISO, PIN_SPI1_SS, PIN_SPI1_SCK, PIN_SPI1_MOSI);
#ifndef __SPI0_DEVICE
#define __SPI0_DEVICE spi0
#endif
#ifndef __SPI1_DEVICE
#define __SPI1_DEVICE spi1
#endif
SPIClassRP2040 SPI(__SPI0_DEVICE, PIN_SPI0_MISO, PIN_SPI0_SS, PIN_SPI0_SCK, PIN_SPI0_MOSI);
SPIClassRP2040 SPI1(__SPI1_DEVICE, PIN_SPI1_MISO, PIN_SPI1_SS, PIN_SPI1_SCK, PIN_SPI1_MOSI);

Some files were not shown because too many files have changed in this diff Show More