Compare commits

...
133 Commits
Author SHA1 Message Date
Earle F. Philhower, III 99b4aac48b Update version 2023-09-13 13:10:54 -07:00
Earle F. Philhower, III 2bd64b566c GDB/Binutils release-gdb-13.2, GCC still 12.3 (#1706)
Fix #1681 due to breakage of GDB 12.3 in Platform.IO
Remove new Binutils linker warning
2023-09-13 13:09:37 -07:00
wd5gnr 3c93d14b33 Update rp2040.rst (#1704)
isPicoW was missing the rp2040. qualifier.
2023-09-10 13:54:24 -07:00
Earle F. Philhower, III 5639edefee Update wifi.rst (#1702) 2023-09-09 08:09:15 -07:00
Andy2No 585c31ef39 Pin definitions for Pimoroni Tiny 2040 (#1699)
Fixes #1696

Additional pin definitions for Pimoroni Tiny 2040, including using the Green LED element of the RGB LED as the default LED.

There are only 12 external header pins for GPIO, including 4 ADC pins, so there are less options for assigning pins than on a generic Pico RP2040. In particular, there can only be one SPI, and it can't have an SS pin but I've defined GPIO17, as on the pico, because the definition is used in generic / common.h.

BOOTSEL on the Pimoroni Tiny 2040 is connected to GPIO23. I don't know if that has any consequences for implementing reading from BOOTSEL for this board. That may need to be revisited, but it doesn't appear to involve any changes to pins_arduino.h.
2023-09-08 13:16:04 -07:00
Earle F. Philhower, III a385a4c1e5 Update README.md 2023-09-05 15:44:34 -07:00
Earle F. Philhower, III ef257c32b4 Update version 2023-09-05 14:15:03 -07:00
Earle F. Philhower, III 88cd4b5288 Fix Print::print(0ULL) (#1692)
Move to a patched ArduinoCore-API revision.

Fixes #1691
2023-09-03 13:30:12 -07:00
Earle F. Philhower, III ca7ec56f0d Rebuild OTA using GCC 12.3 (#1690)
No functional changes, just for completeness.
2023-09-03 09:58:28 -07:00
Earle F. Philhower, III 74b1156d5e Fix PicoProbe CMSIS restart of second core (#1689)
For some reason `program ... reset" causes OpenOCD to leave the chip in a
state where the 2nd core does not come up properly, leading to problems in
FreeRTOS and others.

Use a separate reset sequence after programming to work around the issue.

Fixes #1687
2023-09-03 09:43:34 -07:00
Odd Stråbø 6ac7ee1a9d Fix gcc search path in make-ota.sh (#1688) 2023-09-03 09:23:50 -07:00
Earle F. Philhower, III 0b56452c35 Rebuild libpico/libbearssl using GCC 12.3 (#1686) 2023-09-01 08:17:31 -07:00
Earle F. Philhower, III b9c66ca0fd Ensure HID reports aren't dropped (#1685)
Fixes #1682

Make the HID report wait up to 500ms for an existing one to go out
before giving up sending a report.
2023-08-31 08:40:38 -07:00
Earle F. Philhower, III faf06a3b70 Avoid obsolete GCC 7.1 note on building WiFi (#1684)
Fixes #1683
2023-08-31 07:42:54 -07:00
Earle F. Philhower, III d0ac7f06b1 Update version 2023-08-30 08:29:26 -07:00
Earle F. Philhower, III e9daaa3589 Add TDM support to I2S (#1673)
Fixes #1066

Implements a simple TDM mode for the I2S output object.
2023-08-30 08:28:34 -07:00
João Vieira 1393811525 Add stdint.h to generic variant common.h (#1677)
Fixes #1676
2023-08-27 13:55:17 -07:00
Andriy Golovnya 2b640669a7 Added RP2040-ProMini board to the project. (#1674) 2023-08-27 11:53:19 -07:00
Earle F. Philhower, III 9d0f0a8d7a Add missing Tiny2040 JSON (#1675) 2023-08-27 10:03:12 -07:00
Earle F. Philhower, III 21d2fb4afa Add Pimoroni Tiny2040 (#1672)
Fixes #1604.  Supports 2MB and 8MB revs
2023-08-25 10:39:56 -07:00
Earle F. Philhower, III ade74986ee GCC 12.3, GDB 12, OpenOCD 0.12, Picotool 1.1.2 (#1670)
Major toolchain update including:
* GCC 12.3
* GDB 12
* OpenOCD 0.12
* Picotool 1.1.2

* Fix MDNS infinite recursion

* Remove legacy Picoprobe

Fixes #1313
Fixes #1650
2023-08-25 09:56:52 -07:00
LinusHeu 73722b5c87 Fix I2S stop/start race condition #1656 (#1659)
Fixes  #1656
2023-08-22 08:40:23 -07:00
Earle F. Philhower, III 456b474a48 Allow re-setting identical pins without panic() (#1655)
Setting a pin to the current value is a no-op, not fatal.

Fixes #1652
2023-08-21 08:52:49 -07:00
Jimmy Hedman f11cc4db69 Make MDNS compile with IPv6 enabled (#1651) 2023-08-20 14:47:23 -07:00
Matt 8c2901da13 Update license.rst (#1648)
fixed typo in license.rst
2023-08-17 09:33:31 -07:00
LinusHeu ca4637d14d SPI debugging: tiny fix & log actual baudrate (#1641) 2023-08-10 07:39:36 -07:00
Earle F. Philhower, III 313caf406e Update version 2023-08-04 17:09:07 -07:00
Rastloser 25052fedd6 Update platform.txt to require auto-discovery and serial-monitor (#1631)
Fixes #1619

Requiring the auto-discovery tool and serial monitor if they have not already been included by other boards (namely if AVR cores have been uninstalled in the IDE).
2023-08-04 16:31:47 -07:00
Kattni 678cd2c4b9 Add Adafruit Metro RP2040 (#1630) 2023-08-04 15:49:05 -07:00
Dryw Wade 1231317c9c Fix race condition between WiFi receive and consume (#1614)
If another packet comes in between freeing `_rx_buf` and setting `_rx_buf` to 0, that new packet could get put into the same memory address and get concatenated to itself, which leads to an infinite loop.
New solution assigns a temp pointer, sets `rx_buf` to 0, then frees the memory, which guarantees `_rx_buf` always points to valid data.
2023-08-03 20:10:24 -07:00
Earle F. Philhower, III cd76f030cb Update serial.rst 2023-08-03 11:10:01 -07:00
Earle F. Philhower, III 36839cb190 Update serial.rst, add ignoreFlowControl docs (#1626) 2023-08-02 17:06:42 -07:00
marklinmax 23e68973c0 Add SerialUSB::ignoreFlowControl() method (#1624)
Fixes #1620
2023-08-02 17:00:26 -07:00
Patrick Van Oosterwijck 70af32eead Add new board Silicognition RP2040-Shim (#1623)
Board documentation can be found here:
https://silicognition.com/Products/rp2040-shim/

Signed-off-by: Patrick Van Oosterwijck <patrick@silicognition.com>
2023-08-02 16:48:55 -07:00
Maximilian Gerhardt 3cc5ac14ff Fix PlatformIO intellisense (#1616)
The `_idedata` has been changed to `__idedata` in newer PlatformIO core versions per https://github.com/platformio/platformio-core/commit/158aabbdf23ed1ff8f5e3f01fb4ef1723a78d3bb. This change has broken the logic to expand out the `-iprefix PATH @INCLUDEFILE` argument into its individual include paths, causing Intellisense breakages on some VSCode systems and other IDEs that didn't handle these arguments correctly or in which the path was corrupted.
2023-07-29 13:48:29 -07:00
Earle F. Philhower, III 4fd8e41db1 Update version 2023-07-28 10:35:47 -07:00
Earle F. Philhower, III 5fd4736d8f Handle swapped Wire IRQs properly (#1608)
When we swap the Wire objects, we need the i2c0 IRQ shim to call
Wire1.onIRQ, not the usual Wire.onIRQ.  Same for i2c1 IRQ shim.

Fixes #1607
2023-07-26 15:40:07 -07:00
Earle F. Philhower, III 67a07edccc Fix FS upload crash (#1598)
Thanks to @pietglas for finding and fixing.  See #1590 for more info.
2023-07-17 15:17:31 -07:00
Earle F. Philhower, III 1dc0872818 Remove obsolete refs to ATOMIC_FS_UPDATE (#1597)
See #1590
2023-07-14 14:58:21 -07:00
Pontus Oldberg ead0728a57 Adds new Challenger WiFi6/BLE5 board to the mix. (#1595) 2023-07-14 08:58:06 -07:00
Jack Burgess 14eb8d3906 Update ota.rst (#1592)
Spelling mistakes and grammatical corrections
2023-07-14 08:10:13 -07:00
LinusHeu 4c90b295b2 Fix: 'I2S::operator=(const I2S&)' is implicitly (#1588) 2023-07-12 03:26:43 -07:00
Earle F. Philhower, III 43aa0427ea Reapply #1548 (#1582) 2023-07-07 13:38:04 -07:00
palmerr23 cc5d1779a3 Add MCLK support for I2S, optimize clocks for jitter-free playback (#1555)
Fixes #1065
2023-07-07 13:10:32 -07:00
Earle F. Philhower, III 0f437e4db2 Remove leftover debug printf in LWIP_Ethernet (#1572)
See #1161
2023-07-02 13:12:22 -07:00
Max 3778fbb833 Add ArtronShop RP2 Nano board (#1567) 2023-06-28 09:16:05 -07:00
Ha Thach 4b6f3d05e1 BREAKING: Change default debug_script to cmsis-dap (#1565)
The PicoProbe firmware has only supported  CMSIS-DAP for some time, make it the default debugging option to work around IDE 2.0 issues.
2023-06-27 09:31:58 -07:00
Earle F. Philhower, III 9e89dda900 Ensure 64bit math for SD card FSInfo (#1553)
Fixes #1552

Ensure that 64 bit multiplication is done when calculating the total size
available and used for SDFS.
2023-06-23 08:25:36 -07:00
Tristan Rowley 91e69e2a1a Add Pimoroni Plasma RP2040 support (#1556) 2023-06-22 12:58:57 -07:00
Mohammed Chamma 21d1a285dc Fix typo in fs.rst regarding info64 (#1551) 2023-06-20 13:00:36 -07:00
LinusHeu 0e579792d1 I2S: Don't consider _isHolding when it's an output (#1548) 2023-06-19 07:54:34 -07:00
Earle F. Philhower, III 20cabe824f Update version 2023-06-17 19:40:40 -07:00
Earle F. Philhower, III 0847d3dbb0 Update to Pico-SDK 1.5.1 (#1539)
This should reduce the `git submodule update` space required as well as
avoid the max-path-len errors under Windows in most cases.
2023-06-15 17:05:04 -07:00
Paint Your Dragon fa7c287f07 Linker tweaks for RAM-resident PicoDVI library functions (#1541) 2023-06-15 15:01:02 -07:00
hreintke 9b3032cd58 WebServer: Solve HTTP request delay by dropping idle connections (#1537)
Serve next wificlient (http_request) if current client does not have data
2023-06-15 07:39:02 -07:00
Earle F. Philhower, III 50646b9e70 Update httpclient.rst (#1538) 2023-06-15 07:20:15 -07:00
Earle F. Philhower, III 3d6a1c7b41 Enable proper reuse of PIO programs (#1526)
* Enable proper reuse of PIO programs

Rewrite the PIOProgram helper class to properly re-use loaded programs
and to try to re-use loaded instructions before allocating a new PIO
program.

Supersedes #1524

* Less copy-pasta
2023-06-13 04:42:37 -07:00
Earle F. Philhower, III 5b76b0668b Addition ABM checks in PWMAudio and ADCInput (#1530)
Handle the case where the DMA manager is unable to completely allocate
needed resources (DMA channels or memory) and return `false` in ::begin()
2023-06-12 15:20:52 -07:00
Earle F. Philhower, III d18f8dce2f I2S check for failure of ARB and PIO allocation (#1528)
Per https://github.com/earlephilhower/arduino-pico/pull/1524#issuecomment-1587885054
2023-06-12 12:24:08 -07:00
Earle F. Philhower, III c64cdc14b6 Call I2S::end() in I2S destructor (#1527)
Per https://github.com/earlephilhower/arduino-pico/pull/1524#issuecomment-1587562257
2023-06-12 12:12:50 -07:00
Earle F. Philhower, III cc800713bd Minor clean up includes (#1520) 2023-06-10 20:42:28 -07:00
Linar Yusupov c6a0d6ecfe Fix for invalid __channelCount in ~AudioBufferManager() (#1519) 2023-06-10 10:51:19 -07:00
Earle F. Philhower, III c6426ae461 Update RP2040Support.h (#1518) 2023-06-09 07:59:05 -07:00
Earle F. Philhower, III 7b04a033b3 Update rp2040.rst (#1515) 2023-06-08 09:49:57 -07:00
madias123 8e4008bf12 Add rebootToBootloader to reboot to bootloader from code (#1514) 2023-06-08 09:48:35 -07:00
Earle F. Philhower, III 7eb176c0b4 Update version 2023-06-07 18:06:55 -07:00
hreintke 3f475ac68c CoreMutex add portYieldFromISR for FreeRTOS (#1484) 2023-06-07 06:59:10 -07:00
Earle F. Philhower, III 5204dab99b Fix CoreMutex FreeRTOS ISR logic (#1510) 2023-06-07 00:15:41 -07:00
Earle F. Philhower, III 273fb84dc5 Update to Adafruit TinyUSB 2.2.1 (#1511)
Fixes #1509
2023-06-07 00:05:12 -07:00
LinusHeu fe3af4d98b Update i2s.rst + typo (#1504) 2023-06-05 13:10:04 -07:00
Earle F. Philhower, III 9aade5bb24 Update adc.rst (#1502) 2023-06-05 02:15:09 -07:00
Earle F. Philhower, III 3c408dab7c AudioBufferManager(I2s, PWMAudio, ADCInput) clicking fix (#1500)
The ABM had an off-by-one error in the DMA buffer swapover.  Instead of
setting the DMA address to the newly added buffer in active[], it set it
to the buffer that was currently running.

This would effectively disable the ping-pong and cause clicks/lost data.

Fixes #1491
2023-06-04 18:40:29 -07:00
Earle F. Philhower, III 2888f4d03d I2S::available/availableForWrite() returns bytes (#1499)
Per the Arduino documentation, I2s::available should return bytes free,
not samples.  Adjust accordingly.
2023-06-04 16:38:12 -07:00
Earle F. Philhower, III f57b5bc762 Add I2S::getOverUnderflow() (#1497)
See #1491.  Thanks @LinusHeu
2023-06-04 14:21:02 -07:00
Earle F. Philhower, III 35a4d57360 Fix I2s::available/availableForWrite() (#1496)
Return the actual number of samples that can be read/written, not the
number of 32-bit values there is space for.
2023-06-04 14:10:53 -07:00
Earle F. Philhower, III 579e366bcf Fix serial reset hang under FreeRTOS (#1495)
The serial port reset logic was calling `sleep_ms()` which ended up doing
a task switch...while the other core was frozen and everything was supposed
to be locked.

Use `busy_wait_ms()` which is a tight loop to delay in the reset portion.

Fixes #1486
2023-06-04 13:28:11 -07:00
hreintke db4f79448d CoreMutex freeRTOS Mutex acquire properly (#1481)
Since FreeRTOS has real tasks and mutexes with support for priority bumping, actually always try and take a `CoreMutex` instead of seeing if someone else already has it and aborting immediately.

This fix helps ensure things like Serial output in a multi-task system won't get lost.
2023-06-04 13:12:30 -07:00
LinusHeu 45bbcca207 AudioBufferManager: Make dma_claim_unused_channel() not panic (#1487) 2023-05-30 15:51:07 -07:00
Dominic PearmanandDominic Pearman 8e961a5667 Added setup overload to pass name. (#1483)
Co-authored-by: Dominic Pearman <dominic@phymorous.de>
2023-05-27 10:40:24 -07:00
Ivan Kravets 64ad69c247 Temporary disable publishing to the PIO registry (#1476)
See RPI's CEO comment https://github.com/platformio/platform-raspberrypi/pull/36#issuecomment-1560504425
2023-05-26 10:24:28 -07:00
Jan 4def2f219c Implement the BD_ADDR(char * address_string) constructor. (#1440)
* Implement the BD_ADDR(char * address_string) constructor.

* Updating implementation to use sscanf.

There is an extra step after the sscanf that checks that we got
six bytes back and if we did not, it will set all bytes in the
address to zero.

* Example using BD_ADDR(const char * address_string)

This example shows how BD_ADDR(const char * address_string) can
be used to create BD_ADDR objects to use for comparisons etc.

* Update LEDeviceScanner.ino formatting
2023-05-25 06:54:24 -07:00
Earle F. Philhower, III b2b4b2d71d Update version 2023-05-23 17:57:38 -07:00
Earle F. Philhower, III e93bd14ef5 Generate warnings if Pico STDIO init called (#1467)
Warn the user that the Pico SDK STDIO calls (stdio_init_all, stdio_usb_init,
stdio_uart_init) are not supported or needed at compile time.  See multiple
issues #1433 #1347 #1273 #1251 and others.
2023-05-23 17:28:57 -07:00
Earle F. Philhower, III 652f9f9eda Fix FreeRTOS CoreMutex shim to handle ISRs (#1442)
* Fix FreeRTOS CoreMutex shim to handle ISRs

Automatically check, when in FreeRTOS, if we're in an ISR and
if so call the correct mutex grab.

Thanks to @caveman99 for finding and proposing a solution!

Fixes #1441

* Fix the CoreMutex destructor, too
2023-05-23 10:12:52 -07:00
Earle F. Philhower, III cac9eb0cd7 BREAKING: Swap Wire1 and Wire in Adafruit Feather (#1468)
Fixes #1465

The Adafruit Feather came onboard before the Wire swapping was supported
in the core, so it's backwards from the real definition.  Now that swapping is
supported, fix it to match the rest of the boards.
2023-05-23 09:57:28 -07:00
LinusHeu 3b4fb295ea Fix I2S::write(const uint8_t *buffer, size_t size) (#1461)
According to this: https://github.com/earlephilhower/arduino-pico/discussions/1450
2023-05-20 11:33:50 -07:00
Jean-Luc Béchennec a1ae61b5b1 BT Serial connection docs for Mac (#1459) 2023-05-20 10:56:06 -07:00
355abd8c57 Add setInverted() options to SerialPIO (#1451)
Call `SerialPIO::setInverted(txinv, rxinv)` before `SerialPIO::begin()` to enable.

---------

Co-authored-by: Mykle Hansen <mykle@mykle.com>
Co-authored-by: Earle F. Philhower, III <earlephilhower@yahoo.com>
2023-05-19 16:30:26 -07:00
Earle F. Philhower, III b57ac66815 Cleanup legacy includes (#1452) 2023-05-16 23:47:17 -07:00
Earle F. Philhower, III a851a928d2 Merge latest FreeRTOS/SMP upstream branch (#1449)
Update to head of upstream FreeRTOS/SMP branch.  Very minor changes.

Remove the custom getreent implementation, use the one that was defined
in the upstream FreeRTOS/smp branch (callback related vs. static vars).
2023-05-16 12:13:51 -07:00
Earle F. Philhower, III 16c2e8e454 Clean up FreeRTOS, remove core freeze hacks (#1448) 2023-05-15 19:52:38 -07:00
Earle F. Philhower, III ae908c8e9b Use real FreeRTOS tasks to idle core during flash (#1444)
Fixes #1439

Use a real FreeRTOS task, at the highest priority, to idle the other core
while doing flash accesses.

The USB stack seems to have some timing dependent bits which get broken
if FreeRTOS switches out the current task when it's running.  Avoid any
issue by disabling preemption on the core for all tud_task calls.

The PendSV handler disable flag can live completely inside the FreeRTOS
variant port, so remove any reference to it in the main core.

Tested using Multicode-FreeRTOS, #1402 and #1441

The USB FIFO interrupts were still being serviced even when the core was
frozen, leading to crashes.  Explicitly shut off IRQs on both the victim
and the initiator core when freezing.

Removed the need for hack __holdUpPendSV flag
2023-05-15 18:10:30 -07:00
Earle F. Philhower, III 723c81470a Update contrib.rst 2023-05-15 10:09:30 -07:00
Alessandro Ranellucci a7905fba3e Add Arduino CLI installation instructions (#1447) 2023-05-15 09:29:12 -07:00
Earle F. Philhower, III 6e52b72523 Update macro for detecting this core in contributing docs (#1436) 2023-05-11 09:45:34 -07:00
git2212 ef4ec4185a Add dummy Serial implementation when NO_USB defined (#1438)
Fixes #1434
2023-05-11 09:45:01 -07:00
Earle F. Philhower, III 3dee0a276b Update version 2023-05-05 09:00:07 -07:00
Maximilian Gerhardt e95248a787 Support and Document pico-debug in PlatformIO (#1427) 2023-05-05 07:32:41 -07:00
Earle F. Philhower, III 414ff23141 Remove circular dependency on WiFi/LWIP_Ethernet (#1415)
Fixes #1408

The wl_* types are required in multiple libraries which in turn seem
to have a circular dependency that Platform.IO has issues with in certain
modes.

Avoid the issue by moving the common headers into the core directories
so they will be accessible by all libs.

Move StackThunk to core directory, too
2023-05-04 19:54:24 -07:00
Paint Your Dragon 2c0ce6f416 Add Feathers: CAN and Prop-Maker (#1421) 2023-05-02 15:41:05 -07:00
Earle F. Philhower, III 89fe754a9f Fix USB Mouse/Joystick HID report ID (#1418)
Fixes #1410

The USB Keyboard now has 2 reports (keyboard + consumer control), so when both
Keyboard and Mouse libraries are included, the Mouse must be report 3, not 2.

Update the Mouse and Joystick report IDs appropriately.
2023-05-01 21:14:11 -07:00
Earle F. Philhower, III 01ee673dc2 Protect the HW random generation from FreeRTOS (#1395)
Fixes #1394

The Pico_Rand SDK calls gather bits from the HW ROSC at precise intervals.
If there is jitter in the sleep_until() call then the ROSC bit collection
will always think it's failed to acquire the right bit and retry infintitely.

Avoid by wrapping the HW random number calls and the sleep_until() routine.
Only when in FreeRTOS set a flag to silently make sleep_until() into a
busy wait loop while in a random number generation step.  When not in the
random code, do the normal sleep_until call.
2023-05-01 19:42:21 -07:00
Earle F. Philhower, III 5a949443a5 Fix USB crashes in FreeRTOS (#1419)
Fixes #1402

The global USB mutex is auto-shadowed with a FreeRTOS semaphore while in
FreeRTOS mode.  Unfortunately, while the core was using the proper
FreeRTOS semaphore to lock access to the USB port, the actual FreeRTOS
USB task was using the naked Pico SDK mutex, leading to cases where it
could acquire the mutex even though some other FreeRTOS task actually
owned the shadowed mutex.

Properly lock the shadowed Semaphore, not mutex_t, in the FreeRTOS
USB periodic task.
2023-05-01 19:34:46 -07:00
Maximilian Gerhardt a916695155 Add PICO_FLASH_SIZE_BYTES to PIO (#1416) 2023-05-01 11:48:28 -07:00
Earle F. Philhower, III 611547ee97 Add PICO_FLASH_SIZE_BYTES define (#1414)
Matches the flash size defined in the menus.

Fixes #1399
2023-05-01 11:02:01 -07:00
Earle F. Philhower, III ff9f228e1b FreeRTOS compilation fix when no LED present (#1413)
Fixes #1412
2023-05-01 10:46:23 -07:00
Earle F. Philhower, III f33dfd2313 Don't delete first cert, taken from ESP8266 repo (#1392)
See https://github.com/esp8266/Arduino/pull/8907
2023-04-22 15:34:59 -07:00
Earle F. Philhower, III 7ef00c8761 Update to Adafruit_TinyUSB 2.1.0 (#1387)
Fixes #1382
2023-04-21 10:20:14 -07:00
Maximilian Gerhardt 437d03583a Sync macros used in PlatformIO and platform.txt (#1386) 2023-04-21 09:57:50 -07:00
Wvirgil123 0322a6871c Add board Seeed Indicator RP2040 (#1375) 2023-04-21 09:48:38 -07:00
Maximilian Gerhardt 4f643d27d9 Enable BlackMagicProbe for PlatformIO (#1378) 2023-04-15 12:35:45 -07:00
Earle F. Philhower, III 3dcb4bbf8b Clear compile errors if BT needed but not on (#1371)
Fixes #1370

Adds a simple helper assertion to tell the user how to enable BT if it's
not enabled, instead of some odd compilation warnings about undefined
functions.
2023-04-12 08:21:24 -07:00
Paint Your Dragon 59981aed7f Rename USB HOST pins (#1367)
Change “N” to “M” and add “HOST” in the USB pins. Only two pins are actually affected; diff appears large to maintain the pleasant aligned-columns format.
2023-04-10 10:53:28 -07:00
Earle F. Philhower, III 07781e9cf5 Rename BTstack lib to BTstackLib (#1360)
Fixes #1356

Mac and Windows have case-insensitive filesystems, so the will find
the internal (all-lowercase) "btstack.h" and not the library's "BTstack.h",
causing compilation errors.

Rename the library and header to avoid the issue.
2023-04-06 17:09:07 -07:00
Earle F. Philhower, III c276a36c9b Update version 2023-04-05 13:38:25 -07:00
Earle F. Philhower, III ff0d794c50 Add MacOS Picotool upload help (#1355)
Fixes #1344
2023-04-04 14:24:15 -07:00
Maximilian Gerhardt 369c878711 Add BTC and BLE docs to PlatformIO (#1354) 2023-04-04 14:20:39 -07:00
Pontus Oldberg 5bf570c27d Add support for Challenger UWB board, fix some defines (#1351) 2023-04-03 08:58:26 -07:00
Earle F. Philhower, III 0963611fc7 Add absolute mouse support (#1342)
Fixes #1338

To be revisited when TinyUSB native support is added and picked up in the SDK
2023-03-30 16:09:04 -07:00
Earle F. Philhower, III 00644db9b1 Update to fixed HID_Mouse submodule (#1340) 2023-03-30 12:24:13 -07:00
Paint Your Dragon f8ba72aa15 Add Feather ThinkINK and USB Host (#1334) 2023-03-29 11:18:45 -07:00
Paint Your Dragon 371b2c87b1 Add Feather RP2040 RFM, fix pins in SCORPIO, DVI board defs (#1333) 2023-03-28 12:58:49 -07:00
Earle F. Philhower, III 387655606e Update README.md 2023-03-21 07:17:40 -07:00
Earle F. Philhower, III e1b881a688 Add Win32 Long Path info to P.IO docs (#1321) 2023-03-20 15:31:39 -07:00
Mark Hildreth 869ea4c16b Add flatpak-specific IDE installation instructions (#1317) 2023-03-19 11:43:47 -07:00
Earle F. Philhower, III 71238ccd74 Clean up CoreMutex and __isFreeRTOS definition (#1312)
See #1311 for more info.  __isFreeRTOS is C++ linkage and only used
in the core proper (all C++).
2023-03-16 14:42:28 -07:00
Earle F. Philhower, III 1dfd9ebac7 Add HID consumer (aka media) keys for USB, BT (#1309)
Allow sending thigns like KEY_MUTE or KEY_SCAN_NEXT from the USB and
Bluetooth Classic keyboard libraries.

BLE requires some add'l magic, not yet discovered.

Pull in latest upstream Keyboard library changes
2023-03-15 19:10:31 -07:00
Earle F. Philhower, III 3dbe5cf930 Add I2S::swapClocks() for boards w/reversed pins (#1298)
Allow users of boards like the Pico-Audiom where the LRCLK comes
before the BCLK pin, to swap the BCLK/LRCLK of the I2S interface.

Fixes #1287
2023-03-15 15:23:56 -07:00
Earle F. Philhower, III 8e8fea4b7d Add (unsupported) BTstack Arduino library (#1305)
The BTstack driver includes a basic Arduino library as part of the ports
directory.
2023-03-15 13:02:24 -07:00
Earle F. Philhower, III ebe5bfbc07 Increase UDP PCBs to avoid DNS OOM error (#1304)
Fixes #1285 (or at least works well enough for now)
2023-03-14 07:28:24 -07:00
Limor "Ladyada" Fried d67930eeef Feather DVI peripheral fix (#1301)
* make the main SPI SPI (even tho its on SPI1)

* fix Wire to default pins
2023-03-13 17:07:55 -07:00
Earle F. Philhower, III 084d5b0b87 Add 4 and 8 MB VCC_GND boards via Flash Size menu (#1297)
Thanks to @e-tinkers for the initial PR!
2023-03-11 09:52:33 -08:00
Earle F. Philhower, III 8dc44b5e0d Astyle format variants format (#1295) 2023-03-10 13:00:04 -08:00
265 changed files with 11687 additions and 1848 deletions
+12 -12
View File
@@ -19,18 +19,18 @@ jobs:
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Cache PlatformIO
uses: actions/cache@v3
with:
path: ~/.platformio
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
- uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install PlatformIO
run: |
python -m pip install --upgrade pip
pip install --upgrade platformio
python-version: '3.x'
# - name: Cache PlatformIO
# uses: actions/cache@v3
# with:
# path: ~/.platformio
# key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
# - name: Install PlatformIO
# run: |
# python -m pip install --upgrade pip
# pip install --upgrade platformio
- name: Deploy updated JSON
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
@@ -43,5 +43,5 @@ jobs:
curl -L -o package_rp2040_index.json "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/releases/download/$TAG/package_rp2040_index.json"
./package/update_release.py --token ${CI_GITHUB_API_KEY} --repo "$GITHUB_REPOSITORY" --tag global package_rp2040_index.json
# Upload to Platform.IO
curl -LO $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/releases/download/$TAG/rp2040-$TAG.zip
pio package publish rp2040-$TAG.zip --non-interactive
# curl -LO $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/releases/download/$TAG/rp2040-$TAG.zip
# pio package publish rp2040-$TAG.zip --non-interactive
+37 -2
View File
@@ -4,7 +4,7 @@
Raspberry Pi Pico Arduino core, for all RP2040 boards
This is a port of the RP2040 (Raspberry Pi Pico processor) to the Arduino ecosystem. It uses the bare Raspberry Pi Pico SDK and a custom GCC 10.3/Newlib 4.0 toolchain.
This is a port of the RP2040 (Raspberry Pi Pico processor) to the Arduino ecosystem. It uses the bare Raspberry Pi Pico SDK and a custom GCC 12.3/Newlib 4.0 toolchain.
# Documentation
See https://arduino-pico.readthedocs.io/en/latest/ along with the examples for more detailed usage information.
@@ -21,10 +21,12 @@ Read the [Contributing Guide](https://github.com/earlephilhower/arduino-pico/blo
* Adafruit ItsyBitsy RP2040
* Adafruit KB2040
* Adafruit Macropad RP2040
* Adafruit Metro RP2040
* Adafruit QTPy RP2040
* Adafruit STEMMA Friend RP2040
* Adafruit Trinkey RP2040 QT
* Arduino Nano RP2040 Connect
* ArtronShop RP2 Nano
* BridgeTek IDM2040-7A
* Cytron Maker Pi RP2040
* Cytron Maker Nano RP2040
@@ -35,18 +37,24 @@ Read the [Contributing Guide](https://github.com/earlephilhower/arduino-pico/blo
* ExtremeElectronics RC2040
* Invector Labs Challenger RP2040 WiFi
* Invector Labs Challenger RP2040 WiFi/BLE
* Invector Labs Challenger RP2040 WiFi6/BLE
* Invector Labs Challenger NB RP2040 WiFi
* Invector Labs Challenger RP2040 LTE
* Invector Labs Challenger RP2040 LoRa
* Invector Labs Challenger RP2040 SubGHz
* Invector Labs Challenger RP2040 SD/RTC
* Invector Labs Challenger RP2040 UWB
* Invector Labs RPICO32
* Melopero Cookie RP2040
* Melopero Shake RP2040
* Neko Systems BL2040 Mini
* nullbits Bit-C PRO
* Pimoroni PGA2040
* Pimoroni Plasma2040
* Pimoroni Tiny2040
* Seeed Indicator RP2040
* Seeed XIAO RP2040
* Silicognition RP2040-Shim
* Solder Party RP2040 Stamp
* SparkFun ProMicro RP2040
* SparkFun Thing Plus RP2040
@@ -61,16 +69,37 @@ Read the [Contributing Guide](https://github.com/earlephilhower/arduino-pico/blo
* WIZnet W5100S-EVB-Pico
* WIZnet W5500-EVB-Pico
* WIZnet WizFi360-EVB-Pico
* Redscorp RP2040-ProMini
* Generic (configurable flash, I/O pins)
# Installing via Arduino Boards Manager
**Windows Users**: Please do not use the Windows Store version of the actual Arduino application
## Windows-specific Notes
Please do not use the Windows Store version of the actual Arduino application
because it has issues detecting attached Pico boards. Use the "Windows ZIP" or plain "Windows"
executable (EXE) download direct from https://arduino.cc. and allow it to install any device
drivers it suggests. Otherwise the Pico board may not be detected. Also, if trying out the
2.0 beta Arduino please install the release 1.8 version beforehand to ensure needed device drivers
are present. (See #20 for more details.)
## Linux-specific Notes
Installing Arduino using flatpak (often used by "App Stores" in various Linux
distributions) will mean it has restricted access to the host. This might cause uploads to fail
with error messages such as the following:
```
Scanning for RP2040 devices
...
No drive to deploy.
```
If you encounter this, you will need to either install Arduino in a different manner, or override
the flatpak sandboxing feature using the following command, then restarting Arduino.
```
flatpak override --user --filesystem=host:ro cc.arduino.IDE2
```
## Installation
Open up the Arduino IDE and go to File->Preferences.
In the dialog that pops up, enter the following URL in the "Additional Boards Manager URLs" field:
@@ -88,6 +117,12 @@ Type "pico" in the search box and select "Add":
![image](https://user-images.githubusercontent.com/11875/111917223-12063680-8a3c-11eb-8884-4f32b8f0feb1.png)
# Installing via GIT
**Windows Users:** Before installing via `git` on Windows, please read and follow the directions in
[this link](https://arduino-pico.readthedocs.io/en/latest/platformio.html#important-steps-for-windows-users-before-installing).
If Win32 long paths are not enabled, and `git` not configured to use them then there
may be errors when attempting to clone the submodules.
To install via GIT (for latest and greatest versions):
````
mkdir -p ~/Arduino/hardware/pico
+4071 -474
View File
File diff suppressed because it is too large Load Diff
+15 -4
View File
@@ -28,7 +28,7 @@
#include "api/ArduinoAPI.h"
#include "api/itoa.h" // ARM toolchain doesn't provide itoa etc, provide them
#include <pins_arduino.h>
#include "hardware/gpio.h" // Required for the port*Register macros
#include <hardware/gpio.h> // Required for the port*Register macros
#include "debug_internal.h"
#include <RP2040.h> // CMSIS
@@ -84,13 +84,13 @@ void analogWriteFreq(uint32_t freq);
void analogWriteRange(uint32_t range);
void analogWriteResolution(int res);
// FreeRTOS potential calls
extern bool __isFreeRTOS;
#ifdef __cplusplus
} // extern "C"
#endif
// FreeRTOS potential calls
extern bool __isFreeRTOS;
// Ancient AVR defines
#define HAVE_HWSERIAL0
#define HAVE_HWSERIAL1
@@ -129,3 +129,14 @@ constexpr uint32_t __bitset(const int (&a)[N], size_t i = 0U) {
return i < N ? (1L << a[i]) | __bitset(a, i + 1) : 0;
}
#endif
// Warn users trying to use Pico SDK's STDIO implementation
#include <pico/stdio.h> // Ensure it won't be re-included elsewhere
#undef stdio_uart_init
#define stdio_uart_init(...) static_assert(0, "stdio_uart_init is not supported or needed. Either use Serial.printf() or set the debug port in the IDE to Serial/1/2 and use printf(). See https://github.com/earlephilhower/arduino-pico/issues/1433#issuecomment-1540354673 and https://github.com/earlephilhower/arduino-pico/issues/1433#issuecomment-1546783109")
#undef stdio_init_all
#define stdio_init_all(...) static_assert(0, "stdio_init_all is not supported or needed. Either use Serial.printf() or set the debug port in the IDE to Serial/1/2 and use printf(). See https://github.com/earlephilhower/arduino-pico/issues/1433#issuecomment-1540354673 and https://github.com/earlephilhower/arduino-pico/issues/1433#issuecomment-1546783109")
#undef stdio_usb_init
#define stdio_usb_init(...) static_assert(0, "stdio_usb_init is not supported or needed. Either use Serial.printf() or set the debug port in the IDE to Serial/1/2 and use printf(). See https://github.com/earlephilhower/arduino-pico/issues/1433#issuecomment-1540354673 and https://github.com/earlephilhower/arduino-pico/issues/1433#issuecomment-1546783109")
+5 -5
View File
@@ -4,11 +4,11 @@
SPDX-License-Identifier: BSD-3-Clause
*/
#include <Arduino.h>
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/sync.h"
#include "hardware/structs/ioqspi.h"
#include "hardware/structs/sio.h"
#include <pico/stdlib.h>
#include <hardware/gpio.h>
#include <hardware/sync.h>
#include <hardware/structs/ioqspi.h>
#include <hardware/structs/sio.h>
// This example blinks the Pico LED when the BOOTSEL button is pressed.
//
+10 -4
View File
@@ -28,11 +28,17 @@ CoreMutex::CoreMutex(mutex_t *mutex, uint8_t option) {
_mutex = mutex;
_acquired = false;
_option = option;
_pxHigherPriorityTaskWoken = 0; // pdFALSE
if (__isFreeRTOS) {
auto m = __get_freertos_mutex_for_ptr(mutex);
if (_option & FromISR) {
__freertos_mutex_take_from_isr(m);
if (__freertos_check_if_in_isr()) {
if (!__freertos_mutex_take_from_isr(m, &_pxHigherPriorityTaskWoken)) {
return;
}
// At this point we have the mutex in ISR
} else {
// Grab the mutex normally, possibly waking other tasks to get it
__freertos_mutex_take(m);
}
} else {
@@ -54,8 +60,8 @@ CoreMutex::~CoreMutex() {
if (_acquired) {
if (__isFreeRTOS) {
auto m = __get_freertos_mutex_for_ptr(_mutex);
if (_option & FromISR) {
__freertos_mutex_give_from_isr(m);
if (__freertos_check_if_in_isr()) {
__freertos_mutex_give_from_isr(m, &_pxHigherPriorityTaskWoken);
} else {
__freertos_mutex_give(m);
}
+4 -4
View File
@@ -23,12 +23,11 @@
#pragma once
#include "pico/mutex.h"
#include <pico/mutex.h>
#include "_freertos.h"
enum {
DebugEnable = 1,
FromISR = 1 << 1,
DebugEnable = 1
};
class CoreMutex {
@@ -43,5 +42,6 @@ public:
private:
mutex_t *_mutex;
bool _acquired;
u_int8_t _option;
uint8_t _option;
BaseType_t _pxHigherPriorityTaskWoken;
};
+82
View File
@@ -0,0 +1,82 @@
/*
RP2040 PIO utility class
Copyright (c) 2023 Earle F. Philhower, III <earlephilhower@yahoo.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <Arduino.h>
#include "PIOProgram.h"
#include <map>
static std::map<const pio_program_t *, int> __pioMap[2];
auto_init_mutex(_pioMutex);
PIOProgram::PIOProgram(const pio_program_t *pgm) {
_pgm = pgm;
_pio = nullptr;
_sm = -1;
}
// We leave the INSN loaded in INSN RAM
PIOProgram::~PIOProgram() {
if (_pio) {
pio_sm_unclaim(_pio, _sm);
}
}
// Possibly load into a PIO and allocate a SM
bool PIOProgram::prepare(PIO *pio, int *sm, int *offset) {
CoreMutex m(&_pioMutex);
PIO pi[2] = { pio0, pio1 };
// If it's already loaded into PIO IRAM, try and allocate in that specific PIO
for (int o = 0; o < 2; o++) {
auto p = __pioMap[o].find(_pgm);
if (p != __pioMap[o].end()) {
int idx = pio_claim_unused_sm(pi[o], false);
if (idx >= 0) {
_pio = pi[o];
_sm = idx;
*pio = pi[o];
*sm = idx;
*offset = p->second;
return true;
}
}
}
// Not in any PIO IRAM, so try and add
for (int o = 0; o < 2; o++) {
if (pio_can_add_program(pi[o], _pgm)) {
int idx = pio_claim_unused_sm(pi[o], false);
if (idx >= 0) {
int off = pio_add_program(pi[o], _pgm);
__pioMap[o].insert({_pgm, off});
_pio = pi[o];
_sm = idx;
*pio = pi[o];
*sm = idx;
*offset = off;
return true;
}
}
}
// Nope, no room either for SMs or INSNs
return false;
}
+37
View File
@@ -0,0 +1,37 @@
/*
RP2040 PIO utility class
Copyright (c) 2023 Earle F. Philhower, III <earlephilhower@yahoo.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include <hardware/pio.h>
// Wrapper class for PIO programs, abstracting common operations out
class PIOProgram {
public:
PIOProgram(const pio_program_t *pgm);
~PIOProgram();
// Possibly load into a PIO and allocate a SM
bool prepare(PIO *pio, int *sm, int *offset);
private:
const pio_program_t *_pgm;
PIO _pio;
int _sm;
};
+16 -66
View File
@@ -29,7 +29,9 @@
#include <pico/multicore.h>
#include <pico/rand.h>
#include <pico/util/queue.h>
#include <pico/bootrom.h>
#include "CoreMutex.h"
#include "PIOProgram.h"
#include "ccount.pio.h"
#include <malloc.h>
@@ -91,15 +93,14 @@ public:
if (!_multicore) {
return;
}
__holdUpPendSV = 1;
if (__isFreeRTOS) {
vTaskPreemptionDisable(nullptr);
vTaskSuspendAll();
__freertos_idle_other_core();
} else {
mutex_enter_blocking(&_idleMutex);
__otherCoreIdled = false;
multicore_fifo_push_blocking(_GOTOSLEEP);
while (!__otherCoreIdled) { /* noop */ }
}
mutex_enter_blocking(&_idleMutex);
__otherCoreIdled = false;
multicore_fifo_push_blocking(_GOTOSLEEP);
while (!__otherCoreIdled) { /* noop */ }
}
void resumeOtherCore() {
@@ -109,10 +110,8 @@ public:
mutex_exit(&_idleMutex);
__otherCoreIdled = false;
if (__isFreeRTOS) {
xTaskResumeAll();
vTaskPreemptionEnable(nullptr);
__freertos_resume_other_core();
}
__holdUpPendSV = 0;
// Other core will exit busy-loop and return to operation
// once __otherCoreIdled == false.
@@ -156,65 +155,9 @@ private:
class RP2040;
extern RP2040 rp2040;
extern "C" void main1();
class PIOProgram;
extern "C" char __StackLimit;
extern "C" char __bss_end__;
// Wrapper class for PIO programs, abstracting common operations out
// TODO - Add unload/destructor
class PIOProgram {
public:
PIOProgram(const pio_program_t *pgm) {
_pgm = pgm;
}
// Possibly load into a PIO and allocate a SM
bool prepare(PIO *pio, int *sm, int *offset) {
extern mutex_t _pioMutex;
CoreMutex m(&_pioMutex);
// Is there an open slot to run in, first?
if (!_findFreeSM(pio, sm)) {
return false;
}
// Is it loaded on that PIO?
if (_offset[pio_get_index(*pio)] < 0) {
// Nope, need to load it
if (!pio_can_add_program(*pio, _pgm)) {
return false;
}
_offset[pio_get_index(*pio)] = pio_add_program(*pio, _pgm);
}
// Here it's guaranteed loaded, return values
// PIO and SM already set
*offset = _offset[pio_get_index(*pio)];
return true;
}
private:
// Find an unused PIO state machine to grab, returns false when none available
static bool _findFreeSM(PIO *pio, int *sm) {
int idx = pio_claim_unused_sm(pio0, false);
if (idx >= 0) {
*pio = pio0;
*sm = idx;
return true;
}
idx = pio_claim_unused_sm(pio1, false);
if (idx >= 0) {
*pio = pio1;
*sm = idx;
return true;
}
return false;
}
private:
int _offset[2] = { -1, -1 };
const pio_program_t *_pgm;
};
class RP2040 {
public:
RP2040() { /* noop */ }
@@ -320,6 +263,13 @@ public:
reboot();
}
inline void rebootToBootloader() {
reset_usb_boot(0, 0);
while (1) {
continue;
}
}
static void enableDoubleResetBootloader();
void wdt_begin(uint32_t delay_ms) {
+99 -20
View File
@@ -25,17 +25,17 @@
#include "CoreMutex.h"
#include "RP2040USB.h"
#include "tusb.h"
#include "class/hid/hid_device.h"
#include "class/audio/audio.h"
#include "class/midi/midi.h"
#include "pico/time.h"
#include "hardware/irq.h"
#include "pico/mutex.h"
#include "pico/unique_id.h"
#include "pico/usb_reset_interface.h"
#include "hardware/watchdog.h"
#include "pico/bootrom.h"
#include <tusb.h>
#include <class/hid/hid_device.h>
#include <class/audio/audio.h>
#include <pico/time.h>
#include <hardware/irq.h>
#include <pico/mutex.h>
#include <pico/unique_id.h>
#include <pico/usb_reset_interface.h>
#include <hardware/watchdog.h>
#include <pico/bootrom.h>
#include "sdkoverride/tusb_absmouse.h"
#include <device/usbd_pvt.h>
// Big, global USB mutex, shared with all USB devices to make sure we don't
@@ -100,7 +100,7 @@ const uint8_t *tud_descriptor_device_cb(void) {
.iSerialNumber = USBD_STR_SERIAL,
.bNumConfigurations = 1
};
if (__USBInstallSerial && !__USBInstallKeyboard && !__USBInstallMouse && !__USBInstallJoystick && !__USBInstallMassStorage) {
if (__USBInstallSerial && !__USBInstallKeyboard && !__USBInstallMouse && !__USBInstallAbsoluteMouse && !__USBInstallJoystick && !__USBInstallMassStorage) {
// Can use as-is, this is the default USB case
return (const uint8_t *)&usbd_desc_device;
}
@@ -108,7 +108,7 @@ const uint8_t *tud_descriptor_device_cb(void) {
if (__USBInstallKeyboard) {
usbd_desc_device.idProduct |= 0x8000;
}
if (__USBInstallMouse) {
if (__USBInstallMouse || __USBInstallAbsoluteMouse) {
usbd_desc_device.idProduct |= 0x4000;
}
if (__USBInstallJoystick) {
@@ -129,15 +129,15 @@ int __USBGetKeyboardReportID() {
}
int __USBGetMouseReportID() {
return __USBInstallKeyboard ? 2 : 1;
return __USBInstallKeyboard ? 3 : 1;
}
int __USBGetJoystickReportID() {
int i = 1;
if (__USBInstallKeyboard) {
i++;
i += 2;
}
if (__USBInstallMouse) {
if (__USBInstallMouse || __USBInstallAbsoluteMouse) {
i++;
}
return i;
@@ -156,8 +156,9 @@ static uint8_t *GetDescHIDReport(int *len) {
void __SetupDescHIDReport() {
//allocate memory for the HID report descriptors. We don't use them, but need the size here.
uint8_t desc_hid_report_mouse[] = { TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(1)) };
uint8_t desc_hid_report_absmouse[] = { TUD_HID_REPORT_DESC_ABSMOUSE(HID_REPORT_ID(1)) };
uint8_t desc_hid_report_joystick[] = { TUD_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(1)) };
uint8_t desc_hid_report_keyboard[] = { TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(1)) };
uint8_t desc_hid_report_keyboard[] = { TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(1)), TUD_HID_REPORT_DESC_CONSUMER(HID_REPORT_ID(2)) };
int size = 0;
//accumulate the size of all used HID report descriptors
@@ -166,6 +167,8 @@ void __SetupDescHIDReport() {
}
if (__USBInstallMouse) {
size += sizeof(desc_hid_report_mouse);
} else if (__USBInstallAbsoluteMouse) {
size += sizeof(desc_hid_report_absmouse);
}
if (__USBInstallJoystick) {
size += sizeof(desc_hid_report_joystick);
@@ -194,11 +197,19 @@ void __SetupDescHIDReport() {
if (__USBInstallMouse) {
//determine if we need an offset (USB keyboard is installed)
if (__USBInstallKeyboard) {
uint8_t desc_local[] = { TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(2)) };
uint8_t desc_local[] = { TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(3)) };
memcpy(__hid_report + sizeof(desc_hid_report_keyboard), desc_local, sizeof(desc_local));
} else {
memcpy(__hid_report, desc_hid_report_mouse, sizeof(desc_hid_report_mouse));
}
} else if (__USBInstallAbsoluteMouse) {
//determine if we need an offset (USB keyboard is installed)
if (__USBInstallKeyboard) {
uint8_t desc_local[] = { TUD_HID_REPORT_DESC_ABSMOUSE(HID_REPORT_ID(3)) };
memcpy(__hid_report + sizeof(desc_hid_report_keyboard), desc_local, sizeof(desc_local));
} else {
memcpy(__hid_report, desc_hid_report_absmouse, sizeof(desc_hid_report_absmouse));
}
}
//3.) joystick descriptor. 2 additional checks are necessary for mouse and/or keyboard
@@ -206,12 +217,15 @@ void __SetupDescHIDReport() {
uint8_t reportid = 1;
int offset = 0;
if (__USBInstallKeyboard) {
reportid++;
reportid += 2;
offset += sizeof(desc_hid_report_keyboard);
}
if (__USBInstallMouse) {
reportid++;
offset += sizeof(desc_hid_report_mouse);
} else if (__USBInstallAbsoluteMouse) {
reportid++;
offset += sizeof(desc_hid_report_absmouse);
}
uint8_t desc_local[] = { TUD_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(reportid)) };
memcpy(__hid_report + offset, desc_local, sizeof(desc_local));
@@ -235,7 +249,7 @@ const uint8_t *tud_descriptor_configuration_cb(uint8_t index) {
void __SetupUSBDescriptor() {
if (!usbd_desc_cfg) {
bool hasHID = __USBInstallKeyboard || __USBInstallMouse || __USBInstallJoystick;
bool hasHID = __USBInstallKeyboard || __USBInstallMouse || __USBInstallAbsoluteMouse || __USBInstallJoystick;
uint8_t interface_count = (__USBInstallSerial ? 2 : 0) + (hasHID ? 1 : 0) + (__USBInstallMassStorage ? 1 : 0);
@@ -380,6 +394,18 @@ void __USBStart() {
}
bool __USBHIDReady() {
uint32_t start = millis();
const uint32_t timeout = 500;
while (((millis() - start) < timeout) && tud_ready() && !tud_hid_ready()) {
tud_task();
delay(1);
}
return tud_hid_ready();
}
// Invoked when received GET_REPORT control request
// Application must fill buffer report's content and return its length.
// Return zero will cause the stack to STALL request
@@ -535,6 +561,59 @@ usbd_class_driver_t const *usbd_app_driver_get_cb(uint8_t *driver_count) {
*driver_count = 1;
return &_resetd_driver;
}
#elif defined NO_USB
// will ensure backward compatibility with existing code when using pico-debug
#warning "NO_USB selected. No output to Serial will occur!"
#include <Arduino.h>
void SerialUSB::begin(unsigned long baud) {
}
void SerialUSB::end() {
}
int SerialUSB::peek() {
return 0;
}
int SerialUSB::read() {
return -1;
}
int SerialUSB::available() {
return 0;
}
int SerialUSB::availableForWrite() {
return 0;
}
void SerialUSB::flush() {
}
size_t SerialUSB::write(uint8_t c) {
(void) c;
return 0;
}
size_t SerialUSB::write(const uint8_t *buf, size_t length) {
(void) buf;
(void) length;
return 0;
}
SerialUSB::operator bool() {
return false;
}
SerialUSB Serial;
#endif
+10 -1
View File
@@ -19,13 +19,19 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "pico/mutex.h"
#include <pico/mutex.h>
// Weak function definitions for each type of endpoint
extern void __USBInstallSerial() __attribute__((weak));
extern void __USBInstallKeyboard() __attribute__((weak));
extern void __USBInstallJoystick() __attribute__((weak));
// One or the other allowed, not both
extern void __USBInstallMouse() __attribute__((weak));
extern void __USBInstallAbsoluteMouse() __attribute__((weak));
extern void __USBInstallMassStorage() __attribute__((weak));
// Big, global USB mutex, shared with all USB devices to make sure we don't
@@ -39,3 +45,6 @@ int __USBGetJoystickReportID();
// Called by main() to init the USB HW/SW.
void __USBStart();
// Helper class for HID report sending with wait and timeout
bool __USBHIDReady();
+2 -2
View File
@@ -1,5 +1,5 @@
#pragma once
#define ARDUINO_PICO_MAJOR 3
#define ARDUINO_PICO_MINOR 1
#define ARDUINO_PICO_MINOR 5
#define ARDUINO_PICO_REVISION 0
#define ARDUINO_PICO_VERSION_STR "3.1.0"
#define ARDUINO_PICO_VERSION_STR "3.5.0"
+21 -14
View File
@@ -46,22 +46,24 @@ static pio_program_t *pio_make_uart_prog(int repl, const pio_program_t *pg) {
return p;
}
static PIOProgram *_getTxProgram(int bits) {
auto f = _txMap.find(bits);
static PIOProgram *_getTxProgram(int bits, bool inverted) {
int key = inverted ? -bits : bits;
auto f = _txMap.find(key);
if (f == _txMap.end()) {
pio_program_t * p = pio_make_uart_prog(bits, &pio_tx_program);
_txMap.insert({bits, new PIOProgram(p)});
f = _txMap.find(bits);
pio_program_t * p = pio_make_uart_prog(bits, inverted ? &pio_tx_inv_program : &pio_tx_program);
_txMap.insert({key, new PIOProgram(p)});
f = _txMap.find(key);
}
return f->second;
}
static PIOProgram *_getRxProgram(int bits) {
auto f = _rxMap.find(bits);
static PIOProgram *_getRxProgram(int bits, bool inverted) {
int key = inverted ? -bits : bits;
auto f = _rxMap.find(key);
if (f == _rxMap.end()) {
pio_program_t * p = pio_make_uart_prog(bits, &pio_rx_program);
_rxMap.insert({bits, new PIOProgram(p)});
f = _rxMap.find(bits);
pio_program_t * p = pio_make_uart_prog(bits, inverted ? &pio_rx_inv_program : &pio_rx_program);
_rxMap.insert({key, new PIOProgram(p)});
f = _rxMap.find(key);
}
return f->second;
}
@@ -96,7 +98,7 @@ void __not_in_flash_func(SerialPIO::_handleIRQ)() {
return;
}
while (!pio_sm_is_rx_fifo_empty(_rxPIO, _rxSM)) {
uint32_t decode = _rxPIO->rxf[_rxSM];
uint32_t decode = _rxPIO->rxf[_rxSM] ^ (_rxInverted ? 0xffffffff : 0);
decode >>= 33 - _rxBits;
uint32_t val = 0;
for (int b = 0; b < _bits + 1; b++) {
@@ -189,7 +191,7 @@ void SerialPIO::begin(unsigned long baud, uint16_t config) {
if (_tx != NOPIN) {
_txBits = _bits + _stop + (_parity != UART_PARITY_NONE ? 1 : 0) + 1/*start bit*/;
_txPgm = _getTxProgram(_txBits);
_txPgm = _getTxProgram(_txBits, _txInverted);
int off;
if (!_txPgm->prepare(&_txPIO, &_txSM, &off)) {
DEBUGCORE("ERROR: Unable to allocate PIO TX UART, out of PIO resources\n");
@@ -216,7 +218,7 @@ void SerialPIO::begin(unsigned long baud, uint16_t config) {
_reader = 0;
_rxBits = 2 * (_bits + _stop + (_parity != UART_PARITY_NONE ? 1 : 0) + 1) - 1;
_rxPgm = _getRxProgram(_rxBits);
_rxPgm = _getRxProgram(_rxBits, _rxInverted);
int off;
if (!_rxPgm->prepare(&_rxPIO, &_rxSM, &off)) {
DEBUGCORE("ERROR: Unable to allocate PIO RX UART, out of PIO resources\n");
@@ -346,6 +348,11 @@ void SerialPIO::flush() {
delay((1000 * (_txBits + 1)) / _baud);
}
void SerialPIO::setInverted(bool invTx, bool invRx) {
_txInverted = invTx;
_rxInverted = invRx;
}
size_t SerialPIO::write(uint8_t c) {
CoreMutex m(&_mutex);
if (!_running || !m || (_tx == NOPIN)) {
@@ -364,7 +371,7 @@ size_t SerialPIO::write(uint8_t c) {
}
val <<= 1; // Start bit = low
pio_sm_put_blocking(_txPIO, _txSM, val);
pio_sm_put_blocking(_txPIO, _txSM, _txInverted ? ~val : val);
return 1;
}
+4
View File
@@ -41,6 +41,8 @@ public:
void begin(unsigned long baud, uint16_t config) override;
void end() override;
void setInverted(bool invTx = true, bool invRx = true);
virtual int peek() override;
virtual int read() override;
virtual int available() override;
@@ -63,6 +65,8 @@ protected:
int _stop;
bool _overflow;
mutex_t _mutex;
bool _txInverted = false;
bool _rxInverted = false;
PIOProgram *_txPgm;
PIO _txPIO;
+16
View File
@@ -40,6 +40,10 @@ bool SerialUART::setRX(pin_size_t pin) {
return true;
}
if (_rx == pin) {
return true;
}
if (_running) {
panic("FATAL: Attempting to set Serial%d.RX while running", uart_get_index(_uart) + 1);
} else {
@@ -57,6 +61,10 @@ bool SerialUART::setTX(pin_size_t pin) {
return true;
}
if (_tx == pin) {
return true;
}
if (_running) {
panic("FATAL: Attempting to set Serial%d.TX while running", uart_get_index(_uart) + 1);
} else {
@@ -74,6 +82,10 @@ bool SerialUART::setRTS(pin_size_t pin) {
return true;
}
if (_rts == pin) {
return true;
}
if (_running) {
panic("FATAL: Attempting to set Serial%d.RTS while running", uart_get_index(_uart) + 1);
} else {
@@ -91,6 +103,10 @@ bool SerialUART::setCTS(pin_size_t pin) {
return true;
}
if (_cts == pin) {
return true;
}
if (_running) {
panic("FATAL: Attempting to set Serial%d.CTS while running", uart_get_index(_uart) + 1);
} else {
+17 -13
View File
@@ -25,15 +25,15 @@
#include <Arduino.h>
#include "CoreMutex.h"
#include "tusb.h"
#include "pico/time.h"
#include "pico/binary_info.h"
#include "pico/bootrom.h"
#include "hardware/irq.h"
#include "pico/mutex.h"
#include "hardware/watchdog.h"
#include "pico/unique_id.h"
#include "hardware/resets.h"
#include <tusb.h>
#include <pico/time.h>
#include <pico/binary_info.h>
#include <pico/bootrom.h>
#include <hardware/irq.h>
#include <pico/mutex.h>
#include <hardware/watchdog.h>
#include <pico/unique_id.h>
#include <hardware/resets.h>
#ifndef DISABLE_USB_SERIAL
// Ensure we are installed in the USB chain
@@ -130,7 +130,7 @@ size_t SerialUSB::write(const uint8_t *buf, size_t length) {
static uint64_t last_avail_time;
int written = 0;
if (tud_cdc_connected()) {
if (tud_cdc_connected() || _ignoreFlowControl) {
for (size_t i = 0; i < length;) {
int n = length - i;
int avail = tud_cdc_write_available();
@@ -171,14 +171,19 @@ SerialUSB::operator bool() {
return tud_cdc_connected();
}
void SerialUSB::ignoreFlowControl(bool ignore) {
_ignoreFlowControl = ignore;
}
static bool _dtr = false;
static bool _rts = false;
static int _bps = 115200;
static bool _rebooting = false;
static void CheckSerialReset() {
__holdUpPendSV = 1; // Ensure we don't get swapped out by FreeRTOS
if (!_rebooting && (_bps == 1200) && (!_dtr)) {
if (__isFreeRTOS) {
__freertos_idle_other_core();
}
_rebooting = true;
// Disable NVIC IRQ, so that we don't get bothered anymore
irq_set_enabled(USBCTRL_IRQ, false);
@@ -186,11 +191,10 @@ static void CheckSerialReset() {
reset_block(RESETS_RESET_USBCTRL_BITS);
unreset_block(RESETS_RESET_USBCTRL_BITS);
// Delay a bit, so the PC can figure out that we have disconnected.
sleep_ms(3);
busy_wait_ms(3);
reset_usb_boot(0, 0);
while (1); // WDT will fire here
}
__holdUpPendSV = 0;
}
extern "C" void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
+3
View File
@@ -44,6 +44,8 @@ public:
using Print::write;
operator bool() override;
void ignoreFlowControl(bool ignore = true);
// ESP8266 compat
void setDebugOutput(bool unused) {
(void) unused;
@@ -51,6 +53,7 @@ public:
private:
bool _running = false;
bool _ignoreFlowControl = false;
};
extern SerialUSB Serial;
+2 -2
View File
@@ -16,8 +16,8 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "stdlib.h"
#include "stdint.h"
#include <stdlib.h>
#include <stdint.h>
void randomSeed(uint32_t dwSeed) {
if (dwSeed != 0) {
+44 -1
View File
@@ -19,8 +19,9 @@
*/
#include "_freertos.h"
#include "pico/mutex.h"
#include <pico/mutex.h>
#include <stdlib.h>
#include "Arduino.h"
typedef struct {
mutex_t *src;
@@ -59,3 +60,45 @@ SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m, bool recursive) {
}
return nullptr; // Need to make space for more mutex maps!
}
// The HW Random code needs a precise sleep_until() in order to assure it
// grabs the ROSC bit when it wants. Unfortunately, under FreeRTOS the
// sleep_until() becomes imprecise and the "did I get the bit when I wanted"
// check in the pico_rand code always fails and you get an infinite loop.
// This block wraps the 2 get_rand calls to set a flag to convert
// sleep_until() (which can do a task swap and cause bad timing) into a
// busy wait.
extern "C" {
static bool __inRand = false;
extern uint64_t __real_get_rand_64();
uint64_t __wrap_get_rand_64() {
if (__isFreeRTOS) {
rp2040.idleOtherCore();
__inRand = true;
auto r = __real_get_rand_64();
__inRand = false;
rp2040.resumeOtherCore();
return r;
} else {
return __real_get_rand_64();
}
}
uint32_t __wrap_get_rand_32() {
return (uint32_t) __wrap_get_rand_64();
}
extern void __real_sleep_until(absolute_time_t t);
void __wrap_sleep_until(absolute_time_t t) {
if (__inRand) {
busy_wait_until(t);
} else {
__real_sleep_until(t);
}
}
}
+9 -15
View File
@@ -19,7 +19,7 @@
*/
#pragma once
#include "pico/mutex.h"
#include <pico/mutex.h>
// Cannot include refs to FreeRTOS's actual semaphore calls because they
// are implemented as macros, so we have a wrapper in our variant hook
@@ -35,32 +35,26 @@ extern "C" {
struct QueueDefinition; /* Using old naming convention so as not to break kernel aware debuggers. */
typedef struct QueueDefinition * QueueHandle_t;
typedef QueueHandle_t SemaphoreHandle_t;
typedef int32_t BaseType_t;
#endif
extern bool __freertos_check_if_in_isr() __attribute__((weak));
extern SemaphoreHandle_t __freertos_mutex_create() __attribute__((weak));
extern SemaphoreHandle_t _freertos_recursive_mutex_create() __attribute__((weak));
extern void __freertos_mutex_take(SemaphoreHandle_t mtx) __attribute__((weak));
extern void __freertos_mutex_take_from_isr(SemaphoreHandle_t mtx) __attribute__((weak));
extern int __freertos_mutex_take_from_isr(SemaphoreHandle_t mtx, BaseType_t* pxHigherPriorityTaskWoken) __attribute__((weak));
extern int __freertos_mutex_try_take(SemaphoreHandle_t mtx) __attribute__((weak));
extern void __freertos_mutex_give(SemaphoreHandle_t mtx) __attribute__((weak));
extern void __freertos_mutex_give_from_isr(SemaphoreHandle_t mtx) __attribute__((weak));
extern void __freertos_mutex_give_from_isr(SemaphoreHandle_t mtx, BaseType_t* pxHigherPriorityTaskWoken) __attribute__((weak));
extern void __freertos_recursive_mutex_take(SemaphoreHandle_t mtx) __attribute__((weak));
extern int __freertos_recursive_mutex_try_take(SemaphoreHandle_t mtx) __attribute__((weak));
extern void __freertos_recursive_mutex_give(SemaphoreHandle_t mtx) __attribute__((weak));
#ifndef INC_FREERTOS_H
extern void vTaskSuspendAll() __attribute__((weak));
extern int32_t xTaskResumeAll() __attribute__((weak));
typedef struct tskTaskControlBlock * TaskHandle_t;
extern void vTaskPreemptionDisable(TaskHandle_t p) __attribute__((weak));
extern void vTaskPreemptionEnable(TaskHandle_t p) __attribute__((weak));
#endif
extern void __freertos_idle_other_core() __attribute__((weak));
extern void __freertos_resume_other_core() __attribute__((weak));
}
extern SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m, bool recursive = false);
// Halt the FreeRTOS PendSV task switching magic
extern "C" int __holdUpPendSV;
+7
View File
@@ -0,0 +1,7 @@
// Simple helper header to ensure pico libs support ?BT
#ifndef ENABLE_CLASSIC
#define ENABLE_CLASSIC 0
#endif
static_assert(ENABLE_CLASSIC, "This library needs Bluetooth enabled. Use the 'Tools->IP/Bluetooth Stack' menu in the IDE to enable it.");
+4 -4
View File
@@ -19,12 +19,12 @@
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
#include "lwip/netif.h"
#include <lwip/netif.h>
extern "C" {
#include "cyw43.h"
#include "cyw43_stats.h"
#include <cyw43.h>
#include <cyw43_stats.h>
}
#include "pico/cyw43_arch.h"
#include <pico/cyw43_arch.h>
#include <Arduino.h>
// From cyw43_ctrl.c
+1 -1
View File
@@ -1,7 +1,7 @@
/*
LWIP wrappers to protect against timer-based re-entrancy
Copyright (c) 202s Earle F. Philhower, III <earlephilhower@yahoo.com>
Copyright (c) 2023 Earle F. Philhower, III <earlephilhower@yahoo.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
+1 -5
View File
@@ -27,11 +27,8 @@
RP2040 rp2040;
extern "C" {
volatile bool __otherCoreIdled = false;
int __holdUpPendSV = 0;
};
mutex_t _pioMutex;
extern void setup();
extern void loop();
@@ -92,8 +89,6 @@ extern "C" int main() {
_REENT_INIT_PTR(_impure_ptr1);
}
mutex_init(&_pioMutex);
rp2040.begin();
initVariant();
@@ -163,6 +158,7 @@ extern "C" void __register_impure_ptr(struct _reent *p) {
}
}
extern "C" struct _reent *__wrap___getreent() __attribute__((weak));
extern "C" struct _reent *__wrap___getreent() {
if (get_core_num() == 0) {
return _impure_ptr;
+43 -2
View File
@@ -29,7 +29,7 @@
; We shift out the start and stop bit as part of the FIFO
set x, 9
pull side 1 ; Force stop bit
pull side 1 ; Force stop bit high
; Send the bits
bitloop:
@@ -41,6 +41,26 @@ wait_bit:
; inverted-logic version (inverts the stop bit)
.program pio_tx_inv
.side_set 1 opt
; We shift out the start and stop bit as part of the FIFO
set x, 9
pull side 0 ; Force stop bit low
; Send the bits
bitloop:
out pins, 1
mov y, isr ; ISR is loaded by the setup routine with the period-1 count
wait_bit:
jmp y-- wait_bit
jmp x-- bitloop
% c-sdk {
static inline void pio_tx_program_init(PIO pio, uint sm, uint offset, uint pin_tx) {
@@ -68,7 +88,7 @@ static inline void pio_tx_program_init(PIO pio, uint sm, uint offset, uint pin_t
}
%}
.program pio_rx
@@ -91,6 +111,27 @@ wait_half:
push ; Stuff it and wait for next start
.program pio_rx_inv
; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX.
start:
set x, 18 ; Preload bit counter...we'll shift in the start bit and stop bit, and each bit will be double-recorded (to be fixed by RP2040 code)
wait 1 pin 0 ; Stall until start bit is asserted
bitloop:
; Delay until 1/2 way into the bit time
mov y, osr
wait_half:
jmp y-- wait_half
; Read in the bit
in pins, 1 ; Shift data bit into ISR
jmp x-- bitloop ; Loop all bits
push ; Stuff it and wait for next start
% c-sdk {
static inline void pio_rx_program_init(PIO pio, uint sm, uint offset, uint pin) {
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false);
+68
View File
@@ -2,6 +2,8 @@
// This file is autogenerated by pioasm; do not edit! //
// -------------------------------------------------- //
#pragma once
#if !PICO_NO_HARDWARE
#include "hardware/pio.h"
#endif
@@ -37,6 +39,39 @@ static inline pio_sm_config pio_tx_program_get_default_config(uint offset) {
sm_config_set_sideset(&c, 2, true, false);
return c;
}
#endif
// ---------- //
// pio_tx_inv //
// ---------- //
#define pio_tx_inv_wrap_target 0
#define pio_tx_inv_wrap 5
static const uint16_t pio_tx_inv_program_instructions[] = {
// .wrap_target
0xe029, // 0: set x, 9
0x90a0, // 1: pull block side 0
0x6001, // 2: out pins, 1
0xa046, // 3: mov y, isr
0x0084, // 4: jmp y--, 4
0x0042, // 5: jmp x--, 2
// .wrap
};
#if !PICO_NO_HARDWARE
static const struct pio_program pio_tx_inv_program = {
.instructions = pio_tx_inv_program_instructions,
.length = 6,
.origin = -1,
};
static inline pio_sm_config pio_tx_inv_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + pio_tx_inv_wrap_target, offset + pio_tx_inv_wrap);
sm_config_set_sideset(&c, 2, true, false);
return c;
}
static inline void pio_tx_program_init(PIO pio, uint sm, uint offset, uint pin_tx) {
// Tell PIO to initially drive output-high on the selected pin, then map PIO
@@ -90,6 +125,39 @@ static inline pio_sm_config pio_rx_program_get_default_config(uint offset) {
sm_config_set_wrap(&c, offset + pio_rx_wrap_target, offset + pio_rx_wrap);
return c;
}
#endif
// ---------- //
// pio_rx_inv //
// ---------- //
#define pio_rx_inv_wrap_target 0
#define pio_rx_inv_wrap 6
static const uint16_t pio_rx_inv_program_instructions[] = {
// .wrap_target
0xe032, // 0: set x, 18
0x20a0, // 1: wait 1 pin, 0
0xa047, // 2: mov y, osr
0x0083, // 3: jmp y--, 3
0x4001, // 4: in pins, 1
0x0042, // 5: jmp x--, 2
0x8020, // 6: push block
// .wrap
};
#if !PICO_NO_HARDWARE
static const struct pio_program pio_rx_inv_program = {
.instructions = pio_rx_inv_program_instructions,
.length = 7,
.origin = -1,
};
static inline pio_sm_config pio_rx_inv_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + pio_rx_inv_wrap_target, offset + pio_rx_inv_wrap);
return c;
}
static inline void pio_rx_program_init(PIO pio, uint sm, uint offset, uint pin) {
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false);
@@ -6,10 +6,9 @@
SPDX-License-Identifier: BSD-3-Clause
*/
#include "pico.h"
#include "pico/time.h"
#include "pico/bootrom.h"
#include "pico/binary_info.h"
#include <pico/time.h>
#include <pico/bootrom.h>
#include <pico/binary_info.h>
// PICO_CONFIG: PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS, Window of opportunity for a second press of a reset button to enter BOOTSEL mode (milliseconds), type=int, default=200, group=pico_bootsel_via_double_reset
#ifndef PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS
+101
View File
@@ -0,0 +1,101 @@
/*
Expost absolute mouse HID descriptor and report
Taken from @tobozo PR https://github.com/hathach/tinyusb/pull/1363
TODO - remove once that PR merged with TinyUSB
Copyright (c) 2023 Earle F. Philhower, III <earlephilhower@yahoo.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include "tusb.h"
#include "class/hid/hid_device.h"
// Absolute Mouse: same as the Standard (relative) Mouse Report but
// with int16_t instead of int8_t for X and Y coordinates.
typedef struct TU_ATTR_PACKED {
uint8_t buttons; /**< buttons mask for currently pressed buttons in the mouse. */
int16_t x; /**< Current x position of the mouse. */
int16_t y; /**< Current y position of the mouse. */
int8_t wheel; /**< Current delta wheel movement on the mouse. */
int8_t pan; // using AC Pan
} hid_abs_mouse_report_t;
// Absolute Mouse Report Descriptor Template
#define TUD_HID_REPORT_DESC_ABSMOUSE(...) \
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
HID_USAGE ( HID_USAGE_DESKTOP_MOUSE ) ,\
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
/* Report ID if any */\
__VA_ARGS__ \
HID_USAGE ( HID_USAGE_DESKTOP_POINTER ) ,\
HID_COLLECTION ( HID_COLLECTION_PHYSICAL ) ,\
HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\
HID_USAGE_MIN ( 1 ) ,\
HID_USAGE_MAX ( 5 ) ,\
HID_LOGICAL_MIN ( 0 ) ,\
HID_LOGICAL_MAX ( 1 ) ,\
/* Left, Right, Middle, Backward, Forward buttons */ \
HID_REPORT_COUNT( 5 ) ,\
HID_REPORT_SIZE ( 1 ) ,\
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
/* 3 bit padding */ \
HID_REPORT_COUNT( 1 ) ,\
HID_REPORT_SIZE ( 3 ) ,\
HID_INPUT ( HID_CONSTANT ) ,\
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
/* X, Y absolute position [0, 32767] */ \
HID_USAGE ( HID_USAGE_DESKTOP_X ) ,\
HID_USAGE ( HID_USAGE_DESKTOP_Y ) ,\
HID_LOGICAL_MIN ( 0x00 ) ,\
HID_LOGICAL_MAX_N( 0x7FFF, 2 ) ,\
HID_REPORT_SIZE ( 16 ) ,\
HID_REPORT_COUNT ( 2 ) ,\
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
/* Vertical wheel scroll [-127, 127] */ \
HID_USAGE ( HID_USAGE_DESKTOP_WHEEL ) ,\
HID_LOGICAL_MIN ( 0x81 ) ,\
HID_LOGICAL_MAX ( 0x7f ) ,\
HID_REPORT_COUNT( 1 ) ,\
HID_REPORT_SIZE ( 8 ) ,\
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\
HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ), \
/* Horizontal wheel scroll [-127, 127] */ \
HID_USAGE_N ( HID_USAGE_CONSUMER_AC_PAN, 2 ), \
HID_LOGICAL_MIN ( 0x81 ), \
HID_LOGICAL_MAX ( 0x7f ), \
HID_REPORT_COUNT( 1 ), \
HID_REPORT_SIZE ( 8 ), \
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ), \
HID_COLLECTION_END , \
HID_COLLECTION_END \
static inline bool tud_hid_abs_mouse_report(uint8_t report_id,
uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal) {
hid_abs_mouse_report_t report = {
.buttons = buttons,
.x = x,
.y = y,
.wheel = vertical,
.pan = horizontal
};
return tud_hid_n_report(0, report_id, &report, sizeof(report));
}
+1 -1
View File
@@ -77,7 +77,7 @@ static std::map<pin_size_t, CBInfo> _map;
void _gpioInterruptDispatcher(uint gpio, uint32_t events) {
(void) events;
// Only need to lock around the std::map check, not the whole IRQ callback
CoreMutex m(&_irqMutex, (FromISR | DebugEnable));
CoreMutex m(&_irqMutex);
if (m) {
auto irq = _map.find(gpio);
if (irq != _map.end()) {
+2 -2
View File
@@ -12,7 +12,7 @@ need to be periodically sampled to be read by applications, easily, such as:
* Light dependent resistors (LDR), etc.
Up to 4 analog samples can be recorded by the hardware (``A0`` ... ``A4``), and all
Up to 4 analog samples can be recorded by the hardware (``A0`` ... ``A3``), and all
recording is done at 16-bit levels (but be aware that the ADC in the Pico will only
ever return values between 0...4095).
@@ -29,7 +29,7 @@ ADC Input API
ADCInput(pin0 [, pin1, pin2, pin3])
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Creates an ADC input object which will record the pins specified in the code.
Only pins ``A0`` ... ``A4`` can be used, and they must be specified in increasing
Only pins ``A0`` ... ``A3`` can be used, and they must be specified in increasing
order (i.e. ``ADCInput(A0, A1);`` is valid, but ``ADCInput(A1, A0)`` is not.
bool setBuffers(size_t buffers, size_t bufferWords)
+2 -2
View File
@@ -54,9 +54,9 @@ author = u'Earle F. Philhower, III'
# built documents.
#
# The short X.Y version.
version = u'3.1.0'
version = u'3.5.0'
# The full version, including alpha/beta/rc tags.
release = u'3.1.0'
release = u'3.5.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
+3 -2
View File
@@ -10,7 +10,8 @@ Contributing to the Core (Pull Requests)
----------------------------------------
We use the standard GitHub Pull Request model. If you're unfamiliar with it,
this `guide <https://www.freecodecamp.org/news/how-to-make-your-first-pull-request-on-github-3/>` gives a simple overview of the process.
this `guide <https://www.freecodecamp.org/news/how-to-make-your-first-pull-request-on-github-3/>`__
gives a simple overview of the process.
All pull requests have to pass a set of Continuous Integration (CI) checks
which help make sure the code compiles under different configurations and has
@@ -84,7 +85,7 @@ code that only runs on this core, use the following define.
.. code:: cpp
#if defined(ARDUINO_ARCH_RP2040)
#if defined(ARDUINO_ARCH_RP2040) && !defined(__MBED__)
~~~ your changes ~~~
#endif
+2 -2
View File
@@ -325,8 +325,8 @@ info64
.. code:: cpp
FSInfo64 fsinfo;
SDFS.info(fsinfo);
or LittleFS(fsinfo);
SDFS.info64(fsinfo);
or LittleFS.info64(fsinfo);
Performs the same operation as ``info`` but allows for reporting greater than
4GB for filesystem size/used/etc. Should be used with the SD and SDFS
+1 -1
View File
@@ -1,7 +1,7 @@
HTTPClient Library
==================
A simple HTTP requestor that can handle both HTTP and HTTP requests is
A simple HTTP requester that can handle both HTTP and HTTPS requests is
included as the ``HTTPClient`` library.
Check the examples for use under HTTP and HTTPS configurations. In general,
+39 -1
View File
@@ -40,6 +40,16 @@ bool setDATA(pin_size_t pin)
Sets the DOUT or DIN pin of the I2S device. Any pin may be used.
Call before ``I2S::begin()``
bool setMCLK(pin_size_t pin)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sets the MCLK pin of the I2S device and enables MCLK output. Any pin may be used.
Call before ``I2S::begin()``
bool setMCLKmult(int mult)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sets the sample rate to MCLK multiplier value. Only multiples of 64 are valid.
Call before ``I2S::begin()``
bool setBitsPerSample(int bits)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Specify how many bits per audio sample to read or write. Note that
@@ -58,12 +68,35 @@ Sets the word clock frequency, but does not start the I2S device if not
already running. May be called after ``I2S::begin()`` to change the
sample rate on-the-fly.
bool setSysClk(int samplerate)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Changes the PICO system clock to optimise for the desired samplerate.
The clock changes to 147.6 MHz for samplerates that are a multiple of 8 kHz, and 135.6 MHz for multiples of 11.025 kHz.
Note that using ``setSysClk()`` may affect the timing of other sysclk-dependent functions.
Should be called before any I2S functions and any other sysclk dependent initialisations.
bool setLSBJFormat()
~~~~~~~~~~~~~~~~~~~~
Enables LSB-J format for I2S output. In this mode the MSB comes out at the
same time as the LRCLK changes, and not the normal 1-cycle delay. Useful for
DAC chips like the PT8211.
bool setTDMFormat()
~~~~~~~~~~~~~~~~~~~
Enabled TDM formatted multi-channel output. Be sure to set the number of channels to
the expected value (8 normally) and the bits per sample to 32.
bool setTDMChannels(int channels)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sets the number of TDM channels between frame syncs. Generally should be set to 8.
bool swapClocks()
~~~~~~~~~~~~~~~~~
Certain boards are hardwired with the WCLK before the BCLK, instead of the normal
way around. This call swaps the WCLK and BCLK pins. Note that you still call
``setBCLK(x)`` with ``x`` being the lowest pin ID (i.e. in swapClocks mode the
``setBCLK`` call actually sets LRCLK).
bool begin()/begin(long sampleRate)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Start the I2S device up with the given sample rate, or with the value set
@@ -77,6 +110,11 @@ void flush()
~~~~~~~~~~~~
Waits until all the I2S buffers have been output.
void getOverUnderflow()
~~~~~~~~~~~~~~~~~~~~~~~
Returns a flag indicating if the I2S system ran our of data to send on output,
or had to throw away data on input.
size_t write(uint8_t/int8_t/int16_t/int32_t)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Writes a single sample of ``bitsPerSample`` to the buffer. It is up to the
@@ -105,7 +143,7 @@ many bytes were actually written.
int availableForWrite()
~~~~~~~~~~~~~~~~~~~~~~~
Returns the number of L/R samples that can be written without
Returns the amount of bytes that can be written without
potentially blocking.
int read()
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

+23 -1
View File
@@ -38,6 +38,22 @@ prevent the Arduino IDE from accessing. For Arduino IDE V2, override the filesys
restriction using ``flatpak override --user --filesystem=host cc.arduino.IDE2`` . For
For Arduino IDE < V2, use ``flatpak override --user --filesystem=host cc.arduino.arduinoide``.
Installing via Arduino CLI
--------------------------
To install using the Arduino command line tool (arduino-cli):
.. code:: bash
arduino-cli config add board_manager.additional_urls https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
arduino-cli core update-index
arduino-cli core install rp2040:rp2040
To list the supported boards:
.. code:: bash
arduino-cli board listall | grep rp2040
Installing via GIT
------------------
To install via GIT (for latest and greatest versions):
@@ -143,7 +159,13 @@ So for the first sketch you will need to rebuild (with the ``Upload Method->Pico
After the initial upload, as long as the running binary was built using the ``Picotool`` upload method, then the normal upload process should work.
Note that for Ubuntu and other Linux operating systems you may need to add the following lines to a new `udev` config file(``99-picotool.rules``) to allow normal users to access the special USB device the Pico exports:
Under MacOS, it may be necessary to install the USB support libraries from a command terminal before the ``Picotool`` upload method can be used:
.. code::
brew install libusb
For Ubuntu and other Linux operating systems you may need to add the following lines to a new `udev` config file(``99-picotool.rules``) to allow normal users to access the special USB device the Pico exports:
.. code::
+1 -1
View File
@@ -10,5 +10,5 @@ In addition, it contains code from additional open source projects:
* The `Pico-SDK <https://github.com/raspberrypi/pico-sdk>`_ and `Pico-Extras <https://github.com/raspberrypi/pico-extras>`_ are by Raspberry Pi (Trading) Ltd. and licensed under the BSD 3-Clause license.
* `Arduino-Pico <https://github.com/earlephilhower/arduino-pico>`_ core files are licenses under the LGPL.
* `LittleFS <https://github.com/ARMmbed/littlefs>`_ library written by ARM Limited and released under the `BSD 3-clause license <https://github.com/ARMmbed/littlefs/blob/master/LICENSE.md>`_ .
* `UF2CONV.PY <https://github.com/microsoft/uf2>`_ is by Microsoft Corporatio and licensed under the MIT license.
* `UF2CONV.PY <https://github.com/microsoft/uf2>`_ is by Microsoft Corporation and licensed under the MIT license.
* Some filesystem code taken from the `ESP8266 Arduino Core <https://github.com/esp8266/Arduino>`_ and licensed under the LGPL.
+12 -12
View File
@@ -90,7 +90,7 @@ Signing requires the generation of an RSA-2048 key (other bit lengths are suppor
Automatic Signing
^^^^^^^^^^^^^^^^^
The simplest way of implementing signing is to use the automatic mode, which presently is only possible on Linux and Mac due to some of the tools not being available for Windows. This mode uses the IDE to configure the source code to enable sigining verification with a given public key, and signs binaries as part of the standard build process using a given public key.
The simplest way of implementing signing is to use the automatic mode, which presently is only possible on Linux and Mac due to some of the tools not being available for Windows. This mode uses the IDE to configure the source code to enable signing verification with a given public key, and signs binaries as part of the standard build process using a given public key.
To enable this mode, just include `private.key` and `public.key` in the sketch `.ino` directory. The IDE will call a helper script (`tools/signing.py`) before the build begins to create a header to enable key validation using the given public key, and to actually do the signing after the build process, generating a `sketch.bin.signed` file. When OTA is enabled (ArduinoOTA, Web, or HTTP), the binary will automatically only accept signed updates.
@@ -140,11 +140,11 @@ Compile the sketch normally and, once a `.bin` file is available, sign it using
Compression
-----------
The eboot bootloader incorporates a GZIP decompressor, built for very low code requirements. For applications, this optional decompression is completely transparent. For uploading compressed filesystems, the application must be built with `ATOMIC_FS_UPDATE` defined because, otherwise, eboot will not be involved in writing the filesystem.
The bootloader incorporates a GZIP decompressor, built for very low code requirements. For applications, this optional decompression is completely transparent.
No changes to the application are required. The `Updater` class and `eboot` bootloader (which performs actual application overwriting on update) automatically search for the `gzip` header in the uploaded binary, and if found, handle it.
No changes to the application are required. The `Updater` class and bootloader (which performs actual application overwriting on update) automatically search for the `gzip` header in the uploaded binary, and if found, handle it.
Compress an application `.bin` file or filesystem package using any `gzip` available, at any desired compression level (`gzip -9` is recommended because it provides the maximum compression and uncompresses as fast as any other compressino level). For example:
Compress an application `.bin` file or filesystem package using any `gzip` available, at any desired compression level (`gzip -9` is recommended because it provides the maximum compression and uncompresses as fast as any other compression level). For example:
.. code:: bash
@@ -185,11 +185,11 @@ Uploading modules wirelessly from Arduino IDE is intended for the following typi
- Only if modules are accessible on the same network as the computer with the Arduino IDE.
- For all IDE uploads,m the Pico W and the computer must be connected to the same network.
- For all IDE uploads, the Pico W and the computer must be connected to the same network.
To upload wirelessly from the IDE:
1. Build a sketch starts ``WiFi`` and includes the appropriare calls to ``ArduinoOTA`` (see the examples for reference). These include the ``ArduinoOTA.begin()`` call in ``setup()`` and periodically calling ``ArduinoOTA.handle();`` from the ``loop()``
1. Build a sketch starts ``WiFi`` and includes the appropriate calls to ``ArduinoOTA`` (see the examples for reference). These include the ``ArduinoOTA.begin()`` call in ``setup()`` and periodically calling ``ArduinoOTA.handle();`` from the ``loop()``
2. Upload using standard USB connection the first time.
@@ -200,7 +200,7 @@ To upload wirelessly from the IDE:
Password Protection
-------------------
Protecting your OTA uploads with password is really straightforward. All you need to do, is to include the following statement in your code:
Protecting your OTA uploads with a password is really straightforward. All you need to do, is to include the following statement in your code:
.. code:: cpp
@@ -208,11 +208,11 @@ Protecting your OTA uploads with password is really straightforward. All you nee
Where ``123`` is a sample password that you should replace with your own.
Before implementing it in your sketch, it is a good idea to check how it works using *BasicOTA.ino* sketch available under *File > Examples > ArduinoOTA*. Go ahead, open *BasicOTA.ino*, uncomment the above statement that is already there, and upload the sketch. To make troubleshooting easier, do not modify example sketch besides what is absolutely required. This is including original simple ``123`` OTA password. Then attempt to upload sketch again (using OTA). After compilation is complete, once upload is about to begin, you should see prompt for password.
Before implementing it in your sketch, it is a good idea to check how it works using *BasicOTA.ino* sketch available under *File > Examples > ArduinoOTA*. Go ahead, open *BasicOTA.ino*, uncomment the above statement that is already there, and upload the sketch. To make troubleshooting easier, do not modify example sketch besides what is absolutely required. This includes the original simple ``123`` OTA password. Then attempt to upload a sketch again (using OTA). After compilation is complete, once upload is about to begin, you should see a prompt for password.
Enter the password and upload should be initiated as usual with the only difference being ``Authenticating...OK`` message visible in upload log.
Enter the password and upload should be initiated as usual with the only difference being ``Authenticating...OK`` message visible in the upload log.
You will not be prompted for a reentering the same password next time. Arduino IDE will remember it for you. You will see prompt for password only after reopening IDE, or if you change it in your sketch, upload the sketch and then try to upload it again.
You will not be prompted for a reentering the same password next time. Arduino IDE will remember it for you. You will see a prompt for password only after reopening IDE, or if you change it in your sketch, upload the sketch and then try to upload it again.
Please note, it is possible to reveal password entered previously in Arduino IDE, if IDE has not been closed since last upload. This can be done by enabling *Show verbose output during: upload* in *File > Preferences* and attempting to upload the module.
@@ -289,7 +289,7 @@ Simple updater downloads the file every time the function is called.
Advanced updater
^^^^^^^^^^^^^^^^
Its possible to point the update function to a script on the server. If a version string argument is given, it will be sent to the server. The server side script can use this string to check whether an update should be performed.
It's possible to point the update function to a script on the server. If a version string argument is given, it will be sent to the server. The server side script can use this string to check whether an update should be performed.
The server-side script can respond as follows: - response code 200, and send the firmware image, - or response code 304 to notify Pico that no update is required.
@@ -305,7 +305,7 @@ The server-side script can respond as follows: - response code 200, and send the
Serial.println("[update] Update no Update.");
break;
case HTTP_UPDATE_OK:
Serial.println("[update] Update ok."); // may not be called since we reboot the RP2040
Serial.println("[update] Update ok.");
break;
}
+85 -1
View File
@@ -23,6 +23,46 @@ Especially useful is the `Getting started with VSCode + PlatformIO <https://docs
Hereafter it is assumed that you have a basic understanding of PlatformIO in regards to project creation, project file structure and building and uploading PlatformIO projects, through reading the above pages.
Important steps for Windows users, before installing
----------------------------------------------------
By default, Windows has a limited path length that is not long enough to fully clone the ``Pico-SDK``'s ``tinyusb`` repository, resulting in error messages like the one below while attempting to fetch the repository.
.. code::
error: unable to create file '.....' : Filename too long
To work around this requires performing two steps and rebooting Windows once. These steps will enable longer file paths at the Windows OS and the ``git`` level.
Step 1: Enabling long paths in git
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Open up a Windows ``cmd`` or ``terminal`` window and execute the following command
.. code::
git config --system core.longpaths true
Step 2: Enabling long paths in the Windows OS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(taken from https://www.microfocus.com/documentation/filr/filr-4/filr-desktop/t47bx2ogpfz7.html)
1. Click Window key and type gpedit.msc, then press the Enter key. This launches the Local Group Policy Editor.
2. Navigate to Local Computer Policy > Computer Configuration > Administrative Templates > System > Filesystem.
3. Double click Enable NTFS/Win32 long paths and close the dialog.
.. image:: images/longpath.png
Step 3: Reboot the computer
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Once the two prior stages are complete, please do a full reboot or power cycle so that the new settings will take effect.
Current state of development
----------------------------
@@ -234,6 +274,17 @@ The lwIP stack can be configured to support only IPv4 (default) or additionally
to the ``platformio.ini``.
Bluetooth Stack
---------------
The Bluetooth Classic (BTC) and Bluetooth Low Energy (BLE) stack can be activated by adding
.. code:: ini
; BTC and BLE
build_flags = -DPIO_FRAMEWORK_ARDUINO_ENABLE_BLUETOOTH
to the ``platformio.ini``.
Selecting a different core version
----------------------------------
@@ -290,10 +341,15 @@ To specify the debugging adapter, use ``debug_tool`` (`documentation <https://do
* ``cmsis-dap``
* ``jlink``
* ``raspberrypi-swd``
* ``blackmagic``
* ``pico-debug``
These values can also be used in ``upload_protocol`` if you want PlatformIO to upload the regular firmware through this method, which you likely want.
Especially the PicoProbe method is convenient when you have two Raspberry Pi Pico boards. One of them can be flashed with the PicoProbe firmware (`documentation <https://www.raspberrypi.com/documentation/microcontrollers/raspberry-pi-pico.html#debugging-using-another-raspberry-pi-pico>`_) and is then connected to the target Raspberry Pi Pico board (see `documentation <https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf>`_ chapter "Picoprobe Wiring"). Remember that on Windows, you have to use `Zadig <https://zadig.akeo.ie/>`_ to also load "WinUSB" drivers for the "Picoprobe (Interface 2)" device so that OpenOCD can speak to it.
Especially the PicoProbe method is convenient when you have two Raspberry Pi Pico boards. One of them can be flashed with the PicoProbe firmware (`documentation <https://www.raspberrypi.com/documentation/microcontrollers/raspberry-pi-pico.html#debugging-using-another-raspberry-pi-pico>`__) and is then connected to the target Raspberry Pi Pico board (see `documentation <https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf>`__ chapter "Picoprobe Wiring"). Remember that on Windows, you have to use `Zadig <https://zadig.akeo.ie/>`_ to also load "WinUSB" drivers for the "Picoprobe (Interface 2)" device so that OpenOCD can speak to it.
.. note::
Newer PicoProbe firmware versions have dropped the proprietary "PicoProbe" USB communication protocol and emulate a **CMSIS-DAP** instead. Meaning, you have to use ``debug_tool = cmsis-dap`` for these newer firmwares, such as those obtained from `raspberrypi/picoprobe <https://github.com/raspberrypi/picoprobe/releases>`__
With that set up, debugging can be started via the left debugging sidebar and works nicely: Setup breakpoints, inspect the value of variables in the code, step through the code line by line. When a breakpoint is hit or execution is halted, you can even see the execution state both Cortex-M0+ cores of the RP2040.
@@ -301,6 +357,30 @@ With that set up, debugging can be started via the left debugging sidebar and wo
For further information on customizing debug options, like the initial breakpoint or debugging / SWD speed, consult `the documentation <https://docs.platformio.org/en/latest/projectconf/section_env_debug.html>`_.
.. note::
For the BlackMagicProbe debugging probe (as can be e.g., created by simply flashing a STM32F103C8 "Bluepill" board), you currently have to use the branch ``fix/rp2040-flash-reliability`` (or at least commit ``1d001bc``) **and** use the `official ARM provided toolchain <https://github.com/blackmagic-debug/blackmagic/issues/1364#issuecomment-1503393266>`_.
You can obtain precompiled binaries from `here <https://github.com/blackmagic-debug/blackmagic/issues/1364#issuecomment-1503372723>`__. A flashing guide is available `here <https://primalcortex.wordpress.com/2017/06/13/building-a-black-magic-debug-probe/>`__. You then have to configure the target serial port ("GDB port") in your project per `documentation <https://docs.platformio.org/en/latest/plus/debug-tools/blackmagic.html#debugging-tool-blackmagic>`__.
.. note::
For the pico-debug (`download <https://github.com/majbthrd/pico-debug/releases>`__) debugging way, *which needs to no additional debug probe*, add this snippet to your ``platformio.ini`` and follow the given procedure:
.. code:: ini
upload_protocol = pico-debug
debug_tool = pico-debug
build_flags = -DPIO_FRAMEWORK_ARDUINO_NO_USB
1. Build your firmware normally
2. Plug in the Pico in BOOTSEL mode
3. Drag and drop your ``.pio/build/<env>/firmware.uf2`` onto the boot drive
4. Unplug and replug your Pico back into BOOTSEL mode for the second time
5. Drag and drop the downloaded ``pico-debug-gimmecache.uf2`` file onto the boot drive
6. A CMSIS-DAP device should now appear on your computer
7. Start debugging via the debug sidebar as normal
Note the restrictions: The second core cannot be used, the USB port cannot be used (no USB serial, only UART serial), 16KB less RAM is available.
Filesystem Uploading
--------------------
@@ -315,3 +395,7 @@ The files you want to upload should be placed in a folder called ``data`` inside
The task "Build Filesystem Image" will take all files in the data directory and create a ``littlefs.bin`` file from it using the ``mklittlefs`` tool.
The task "Upload Filesystem Image" will upload the filesystem image to the Pico via the specified ``upload_protocol``.
.. note::
Set the space available for the filesystem in the ``platformio.ini`` using e.g., ``board_build.filesystem_size = 0.5m``, or filesystem
creation will fail!
+6 -2
View File
@@ -81,8 +81,8 @@ overhead).
Hardware Identification
-----------------------
bool isPicoW()
~~~~~~~~~~~~~~
bool rp2040.isPicoW()
~~~~~~~~~~~~~~~~~~~~~
Returns the core's best guess if this code is running on a Raspberry Pi Pico W.
This would let you, possibly, use the same UF2 for Pico and PicoW by simply not
doing any WiFi calls.
@@ -95,3 +95,7 @@ void rp2040.enableDoubleResetBootloader()
Add a call anywhere in the sketch to ``rp2040.enableDoubleResetBootloader()`` and
the core will check for a double-tap on reset, and if found will start the USB
bootloader.
void rp2040.rebootToBootloader()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Will reboot the RP2040 into USB UF2 upload mode.
+10
View File
@@ -45,3 +45,13 @@ For applications where an IRQ driven serial port is not appropriate, use
For detailed information about the Serial ports, see the
Arduino `Serial Reference <https://www.arduino.cc/reference/en/language/functions/communication/serial/>`_ .
RP2040 Specific SerialUSB methods
---------------------------------
void Serial.ignoreFlowControl(bool ignore)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In some cases, the target application will not assert the DTR virtual line, thus preventing writing operations to succeed.
For this reason, the SerialUSB::ignoreFlowControl() method disables the connection's state verification, enabling the program to write on the port, even though the data might be lost.
+2 -2
View File
@@ -40,9 +40,9 @@ Please note that WiFi on the Pico W is a work-in-progress and there are some imp
* Combined STA/AP mode is not supported
* Multicore is supported, but only one core may run ``WiFi`` code.
* Multicore is supported, but only core 0 may run ``WiFi`` related code.
* FreeRTOS is not yet supported due to the requirement for a very different LWIP implementation. PRs always appreciated!
* FreeRTOS is supported only on core 0 and from within ``setup`` and ``loop`, not tasks, due to the requirement for a very different LWIP implementation. PRs always appreciated!
The WiFi library borrows much work from the `ESP8266 Arduino Core <https://github.com/esp8266/Arduino>`__ , especially the ``WiFiClient`` and ``WiFiServer`` classes.
+3
View File
@@ -56,6 +56,9 @@ extern unsigned long get_rand_32(void);
#define DHCP_DOES_ARP_CHECK 0
#define LWIP_DHCP_DOES_ACD_CHECK 0
// See #1285
#define MEMP_NUM_UDP_PCB 6
#if LWIP_IPV6
#define LWIP_IPV6_DHCP6 1
#endif
+2 -2
View File
@@ -13,7 +13,7 @@
#define PICO_SDK_VERSION_MAJOR 1
#define PICO_SDK_VERSION_MINOR 5
#define PICO_SDK_VERSION_REVISION 0
#define PICO_SDK_VERSION_STRING "1.5.0"
#define PICO_SDK_VERSION_REVISION 1
#define PICO_SDK_VERSION_STRING "1.5.1"
#endif
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+3 -1
View File
@@ -94,7 +94,9 @@ SECTIONS
/* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from
* FLASH ... we will include any thing excluded here in .data below by default */
*(.init)
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*)
/* Some of these excludes required for PicoDVI library, won't affect most code */
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a: *interp.c.obj *divider.S.obj *PicoDVI.cpp.o *dvi.c.o) .text*)
*(.fini)
/* Pull all c'tors into .text */
*crtbegin.o(.ctors)
BIN
View File
Binary file not shown.
-2
View File
@@ -1,2 +0,0 @@
source [find interface/picoprobe.cfg]
source [find target/rp2040.cfg]
+4
View File
@@ -207,3 +207,7 @@
-Wl,--wrap=cyw43_tcpip_link_status
-Wl,--wrap=cyw43_cb_tcpip_init
-Wl,--wrap=cyw43_cb_tcpip_deinit
-Wl,--wrap=get_rand_64
-Wl,--wrap=get_rand_32
-Wl,--wrap=sleep_until
+9 -1
View File
@@ -79,6 +79,10 @@ void ADCInput::onReceive(void(*fn)(void)) {
}
bool ADCInput::begin() {
if (_running) {
return false;
}
_running = true;
_isHolding = 0;
@@ -105,7 +109,11 @@ bool ADCInput::begin() {
setFrequency(_freq);
_arb = new AudioBufferManager(_buffers, _bufferWords, 0, INPUT, DMA_SIZE_16);
_arb->begin(DREQ_ADC, (volatile void*)&adc_hw->fifo);
if (!_arb->begin(DREQ_ADC, (volatile void*)&adc_hw->fifo)) {
delete _arb;
_arb = nullptr;
return false;
}
_arb->setCallback(_cb);
adc_fifo_drain();
@@ -20,8 +20,8 @@
*/
#include <Arduino.h>
#include "hardware/dma.h"
#include "hardware/irq.h"
#include <hardware/dma.h>
#include <hardware/irq.h>
#include "AudioBufferManager.h"
static int __channelCount = 0; // # of channels left. When we hit 0, then remove our handler
@@ -73,13 +73,11 @@ AudioBufferManager::~AudioBufferManager() {
if (_running) {
_running = false;
for (auto i = 0; i < 2; i++) {
dma_channel_set_irq0_enabled(_channelDMA[i], false);
dma_channel_cleanup(_channelDMA[i]);
__channelMap[_channelDMA[i]] = nullptr;
dma_channel_abort(_channelDMA[i]);
dma_channel_unclaim(_channelDMA[i]);
dma_channel_acknowledge_irq0(_channelDMA[i]);
__channelCount--;
}
__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??
@@ -114,7 +112,7 @@ bool AudioBufferManager::begin(int dreq, volatile void *pioFIFOAddr) {
// Get ping and pong DMA channels
for (auto i = 0; i < 2; i++) {
_channelDMA[i] = dma_claim_unused_channel(true);
_channelDMA[i] = dma_claim_unused_channel(false);
if (_channelDMA[i] == -1) {
if (i == 1) {
dma_channel_unclaim(_channelDMA[0]);
@@ -257,7 +255,7 @@ void __not_in_flash_func(AudioBufferManager::_dmaIRQ)(int channel) {
_active[1] = _takeFromList(&_filled);
}
_overunderflow = _overunderflow | (_active[1] == _silence);
dma_channel_set_read_addr(channel, _active[0]->buff, false);
dma_channel_set_read_addr(channel, _active[1]->buff, false);
} else {
if (_empty) {
_addToList(&_filled, _active[0]);
@@ -266,7 +264,7 @@ void __not_in_flash_func(AudioBufferManager::_dmaIRQ)(int channel) {
} else {
_overunderflow = true;
}
dma_channel_set_write_addr(channel, _active[0]->buff, false);
dma_channel_set_write_addr(channel, _active[1]->buff, false);
}
dma_channel_set_trans_count(channel, _wordsPerBuffer * (_dmaSize == DMA_SIZE_16 ? 2 : 1), false);
dma_channel_acknowledge_irq0(channel);
@@ -21,7 +21,7 @@
#pragma once
#include <Arduino.h>
#include "hardware/dma.h"
#include <hardware/dma.h>
class AudioBufferManager {
public:
+1
View File
@@ -0,0 +1 @@
This is the BTstack ports/arduino library, with compile errors fixed only.
+105
View File
@@ -0,0 +1,105 @@
#include <BTstackLib.h>
#include <stdio.h>
#include "ble/att_server.h"
#include "ble/gatt_client.h"
#include "ble/gatt-service/ancs_client.h"
#include "ble/sm.h"
#include "btstack_event.h"
#include <SPI.h>
/*
EXAMPLE_START(ANCS): ANCS Client
*/
/*
@section Advertisement
@text An ANCS Client needs to include the ANCS UUID in its advertisement to
get recognized by iOS
*/
/* LISTING_START(ANCSAdvertisement): ANCS Advertisement */
const uint8_t adv_data[] = {
// Flags general discoverable
0x02, 0x01, 0x02,
// Name
0x05, 0x09, 'A', 'N', 'C', 'S',
// Service Solicitation, 128-bit UUIDs - ANCS (little endian)
0x11, 0x15, 0xD0, 0x00, 0x2D, 0x12, 0x1E, 0x4B, 0x0F, 0xA4, 0x99, 0x4E, 0xCE, 0xB5, 0x31, 0xF4, 0x05, 0x79
};
/* LISTING_END(ANCSAdvertisement): ANCS Advertisement */
/*
@section Setup
@text In the setup, the LE Security Manager is configured to accept pairing requests.
Then, the ANCS Client library is initialized and and ancs_callback registered.
Finally, the Advertisement data is set and Advertisements are started.
*/
/* LISTING_START(ANCSSetup): ANCS Setup */
void setup(void) {
Serial.begin(9600);
Serial.println("BTstack ANCS Client starting up...");
// startup BTstack and configure log_info/log_error
BTstack.setup();
sm_set_io_capabilities(IO_CAPABILITY_DISPLAY_ONLY);
sm_set_authentication_requirements(SM_AUTHREQ_BONDING);
// setup ANCS Client
ancs_client_init();
ancs_client_register_callback(&ancs_callback);
// enable advertisements
BTstack.setAdvData(sizeof(adv_data), adv_data);
BTstack.startAdvertising();
}
/* LISTING_END(ANCSSetup): ANCS Setup */
void loop(void) {
BTstack.loop();
}
/*
@section ANCS Callback
@text In the ANCS Callback, connect and disconnect events are received.
For actual notifications, ancs_client_attribute_name_for_id allows to
look up the name. To get the notification body, e.g., the actual message,
the GATT Client needs to be used directly.
*/
/* LISTING_START(ANCSCallback): ANCS Callback */
void ancs_callback(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
(void) packet_type;
(void) channel;
(void) size;
const char * attribute_name;
if (hci_event_packet_get_type(packet) != HCI_EVENT_ANCS_META) {
return;
}
switch (hci_event_ancs_meta_get_subevent_code(packet)) {
case ANCS_SUBEVENT_CLIENT_CONNECTED:
Serial.println("ANCS Client: Connected");
break;
case ANCS_SUBEVENT_CLIENT_DISCONNECTED:
Serial.println("ANCS Client: Disconnected");
break;
case ANCS_SUBEVENT_CLIENT_NOTIFICATION:
attribute_name = ancs_client_attribute_name_for_id(ancs_subevent_client_notification_get_attribute_id(packet));
if (!attribute_name) {
break;
}
Serial.print("Notification: ");
Serial.print(attribute_name);
Serial.print(" - ");
Serial.println(ancs_subevent_client_notification_get_text(packet));
break;
default:
break;
}
}
/* LISTING_END(ANCSCallback): ANCS Callback */
@@ -0,0 +1,328 @@
#include <BTstackLib.h>
#include <SPI.h>
/*
EXAMPLE_START(LECentral): LE Central
@text Compared with the other examples, the LE Central is
a bit more complex. This is because it performs multiple
steps in sequence as it is common with GATT Client APIs.
It shows how to first scan for other
devices and then connect to one. When connected, a series of
GATT Client operations are performed: first the list of
GATT Services is queried. If a particular service is found,
the list of its GATT Characteristics is retrieved and a set
of known Characteristics are cached for later access.
*/
/*
@section Characteristic Summary
@text As multiple Characteristics need to be found, a custom
struct is used to collect all information about it. This allows
to define the list of necessary characteristics in the
characteristics[] array
*/
/* LISTING_START(LECentralSummary): Characteristic Summary */
// BLE Shield Service V2 incl. used Characteristics
UUID bleShieldServiceV2UUID("B8E06067-62AD-41BA-9231-206AE80AB550");
typedef struct characteristic_summary {
UUID uuid;
const char * name;
bool found;
BLECharacteristic characteristic;
} characteristic_summary_t;
typedef enum characteristicIDs {
charRX = 0,
charTX,
charBaud,
charBdAddr,
numCharacteristics /* last one */
} characteristicIDs_t;
characteristic_summary characteristics[] = {
{ UUID("f897177b-aee8-4767-8ecc-cc694fd5fcee"), "RX", false, BLECharacteristic() },
{ UUID("bf45e40a-de2a-4bc8-bba0-e5d6065f1b4b"), "TX", false, BLECharacteristic() },
{ UUID("2fbc0f31-726a-4014-b9fe-c8be0652e982"), "Baudrate", false, BLECharacteristic() },
{ UUID("65c228da-bad1-4f41-b55f-3d177f4e2196"), "BD ADDR", false, BLECharacteristic() }
};
/* LISTING_END(LECentralSummary): Characteristic Summary */
// Application state
BLEDevice myBLEDevice;
BLEService myBLEService;
bool serviceFound;
bool sendCounter = false;
int counter = 0;
char counterString[20];
// static btstack_timer_source_t heartbeat;
/*
@section Setup
@text In the setup, various callbacks are registered. After that
we start scanning for other devices
*/
/* LISTING_START(LECentralSetup): LE Central Setup */
void setup(void) {
Serial.begin(9600);
BTstack.setBLEAdvertisementCallback(advertisementCallback);
BTstack.setBLEDeviceConnectedCallback(deviceConnectedCallback);
BTstack.setBLEDeviceDisconnectedCallback(deviceDisconnectedCallback);
BTstack.setGATTServiceDiscoveredCallback(gattServiceDiscovered);
BTstack.setGATTCharacteristicDiscoveredCallback(gattCharacteristicDiscovered);
BTstack.setGATTCharacteristicNotificationCallback(gattCharacteristicNotification);
BTstack.setGATTCharacteristicReadCallback(gattReadCallback);
BTstack.setGATTCharacteristicWrittenCallback(gattWrittenCallback);
BTstack.setGATTCharacteristicSubscribedCallback(gattSubscribedCallback);
BTstack.setup();
BTstack.bleStartScanning();
}
/* LISTING_END(LECentralSetup): LE Central Setup */
/*
@section Loop
@text In the standard Arduino loop() function, BTstack's loop() is called first
If we're connected, we send the string "BTstack" plus a counter as fast as possible.
As the Bluetooth module might be busy, it's important to check the result of the
writeCharacteristicWithoutResponse() call. If it's not ok, we just try again in the
next loop iteration.
*/
/* LISTING_START(LECentralLoop): Loop */
void loop(void) {
BTstack.loop();
// send counter as fast as possible
if (sendCounter) {
sprintf(counterString, "BTstack %u\n", counter);
int result = myBLEDevice.writeCharacteristicWithoutResponse(&characteristics[charTX].characteristic, (uint8_t*) counterString, strlen(counterString));
if (result == 0) {
Serial.print("Wrote without response: ");
Serial.println(counterString);
counter++;
}
}
}
/* LISTING_END(LECentralLoop): Loop */
/*
@section Advertisement Callback
@text When an Advertisement is received, we check if it contains
the UUID of the service we're interested in. Only a single service
with a 128-bit UUID can be contained in and Advertisement and not
all BLE devices provides this. Other options are to match on the
reported device name or the BD ADDR prefix.
If we found an interesting device, we try to connect to it.
*/
/* LISTING_START(LECentralAdvertisementCallback): Advertisement Callback */
void advertisementCallback(BLEAdvertisement *bleAdvertisement) {
Serial.print("Device discovered: ");
Serial.print(bleAdvertisement->getBdAddr()->getAddressString());
Serial.print(", RSSI: ");
Serial.println(bleAdvertisement->getRssi());
if (bleAdvertisement->containsService(&bleShieldServiceV2UUID)) {
Serial.println("\nBLE ShieldService V2 found!\n");
BTstack.bleStopScanning();
BTstack.bleConnect(bleAdvertisement, 10000); // 10 s
}
}
/* LISTING_END(LECentralAdvertisementCallback): Advertisement Callback */
/*
@section Device Connected Callback
@text At the end of bleConnect(), the device connected callback is callec.
The status argument tells if the connection timed out, or if the connection
was established successfully.
On a successful connection, a GATT Service Discovery is started.
*/
/* LISTING_START(LECentralDeviceConnectedCallback): Device Connected Callback */
void deviceConnectedCallback(BLEStatus status, BLEDevice *device) {
switch (status) {
case BLE_STATUS_OK:
Serial.println("Device connected!");
myBLEDevice = *device;
counter = 0;
myBLEDevice.discoverGATTServices();
break;
case BLE_STATUS_CONNECTION_TIMEOUT:
Serial.println("Error while Connecting the Peripheral");
BTstack.bleStartScanning();
break;
default:
break;
}
}
/* LISTING_END(LECentralDeviceConnectedCallback): Device Connected Callback */
/*
@section Device Disconnected Callback
@text If the connection to a device breaks, the device disconnected callback
is called. Here, we start scanning for new devices again.
*/
/* LISTING_START(LECentralDeviceDisconnectedCallback): Device Disconnected Callback */
void deviceDisconnectedCallback(BLEDevice * device) {
(void) device;
Serial.println("Disconnected, starting over..");
sendCounter = false;
BTstack.bleStartScanning();
}
/* LISTING_END(LECentralDeviceDisconnectedCallback): Device Disconnected Callback */
/*
@section Service Discovered Callback
@text The service discovered callback is called for each service and after the
service discovery is complete. The status argument is provided for this.
The main information about a discovered Service is its UUID.
If we find our service, we store the reference to this service.
This allows to discover the Characteristics for our service after
the service discovery is complete.
*/
/* LISTING_START(LECentralServiceDiscoveredCallback): Service Discovered Callback */
void gattServiceDiscovered(BLEStatus status, BLEDevice *device, BLEService *bleService) {
switch (status) {
case BLE_STATUS_OK:
Serial.print("Service Discovered: :");
Serial.println(bleService->getUUID()->getUuidString());
if (bleService->matches(&bleShieldServiceV2UUID)) {
serviceFound = true;
Serial.println("Our service located!");
myBLEService = *bleService;
}
break;
case BLE_STATUS_DONE:
Serial.println("Service discovery finished");
if (serviceFound) {
device->discoverCharacteristicsForService(&myBLEService);
}
break;
default:
Serial.println("Service discovery error");
break;
}
}
/* LISTING_END(LECentralServiceDiscoveredCallback): Service Discovered Callback */
/*
@section Characteristic Discovered Callback
@text Similar to the Service Discovered callback, the Characteristic Discovered
callback is called for each Characteristic found and after the discovery is complete.
The main information is again its UUID. If we find a Characteristic that we're
interested in, it's name is printed and a reference stored for later.
On discovery complete, we subscribe to a particular Characteristic to receive
Characteristic Value updates in the Notificaation Callback.
*/
/* LISTING_START(LECentralCharacteristicDiscoveredCallback): Characteristic Discovered Callback */
void gattCharacteristicDiscovered(BLEStatus status, BLEDevice *device, BLECharacteristic *characteristic) {
switch (status) {
case BLE_STATUS_OK:
Serial.print("Characteristic Discovered: ");
Serial.print(characteristic->getUUID()->getUuidString());
Serial.print(", handle 0x");
Serial.println(characteristic->getCharacteristic()->value_handle, HEX);
int i;
for (i = 0; i < numCharacteristics; i++) {
if (characteristic->matches(&characteristics[i].uuid)) {
Serial.print("Characteristic found: ");
Serial.println(characteristics[i].name);
characteristics[i].found = 1;
characteristics[i].characteristic = *characteristic;
break;
}
}
break;
case BLE_STATUS_DONE:
Serial.print("Characteristic discovery finished, status ");
Serial.println(status, HEX);
if (characteristics[charRX].found) {
device->subscribeForNotifications(&characteristics[charRX].characteristic);
}
break;
default:
Serial.println("Characteristics discovery error");
break;
}
}
/* LISTING_END(LECentralCharacteristicDiscoveredCallback): Characteristic Discovered Callback */
/*
@section Subscribed Callback
@text After the subscribe operation is complete, we get notified if it was
successful. In this example, we read the Characteristic that contains the
BD ADDR of the other device. This isn't strictly necessary as we already
know the device address from the Advertisement, but it's a common pattern
with iOS as the device address is hidden from applications.
*/
/* LISTING_START(LECentralSubscribedCallback): Subscribed Callback */
void gattSubscribedCallback(BLEStatus status, BLEDevice * device) {
(void) status;
device->readCharacteristic(&characteristics[charBdAddr].characteristic);
}
/* LISTING_END(LECentralSubscribedCallback): Subscribed Callback */
/*
@section Read Callback
@text The Read callback is called with the result from a read operation.
Here, we write to the TX Characteristic next.
*/
/* LISTING_START(LECentralReadCallback): Read Callback */
void gattReadCallback(BLEStatus status, BLEDevice *device, uint8_t *value, uint16_t length) {
(void) status;
(void) length;
Serial.print("Read callback: ");
Serial.println((const char *)value);
device->writeCharacteristic(&characteristics[charTX].characteristic, (uint8_t*) "Hello!", 6);
}
/* LISTING_END(LECentralReadCallback): Read Callback */
/*
@section Written Callback
@text After the write operation is complete, the Written Callback is callbed with
the result in the status argument. As we're done with the initial setup of the remote
device, we set the flag to write the test string as fast as possible.
*/
/* LISTING_START(LECentralWrittenCallback): Written Callback */
void gattWrittenCallback(BLEStatus status, BLEDevice *device) {
(void) status;
(void) device;
sendCounter = true;
}
/* LISTING_END(LECentralWrittenCallback): Written Callback */
/*
@section Notification Callback
@text Notifications for Characteristic Value Updates are delivered via the
Notification Callback. When more than one Characteristic is subscribed,
the value handle can be used to distinguish between them. The
BLECharacteristic.isValueHandle(int handle) allows to test if a value handle
belongs to a particular Characteristic.
*/
/* LISTING_START(LECentralNotificationCallback): Notification Callback */
void gattCharacteristicNotification(BLEDevice *device, uint16_t value_handle, uint8_t *value, uint16_t length) {
(void) device;
(void) value_handle;
(void) length;
Serial.print("Notification: ");
Serial.println((const char *)value);
}
/* LISTING_END(LECentralNotificationCallback): Notification Callback */
@@ -0,0 +1,68 @@
#include <BTstackLib.h>
#include <SPI.h>
/*
EXAMPLE_START(LEDeviceScanner): LE Device Scanner
@text The LE Device Scanner monitors BLE device advertisements,
keeping track of one or more known devices as they appear.
*/
// Application state
int counter[2] = {0, 0};
BD_ADDR known_devices[2] = {BD_ADDR("DB:88:B6:70:9E:EB"),
BD_ADDR("C9:60:BD:F2:B4:9D")
};
/*
@section Setup
@text After BTstack.setup(), BTstack is configured to call
advertisementCallback whenever an Advertisement was received.
*/
/* LISTING_START(LEDeviceScannerSetup): LE Device Scanner Setup */
void setup(void) {
Serial.begin(9600);
BTstack.setBLEAdvertisementCallback(advertisementCallback);
BTstack.setup();
BTstack.bleStartScanning();
}
/* LISTING_END(LEDeviceScannerSetup): LE Device Scanner Setup */
/*
@section Loop
@text In the standard Arduino loop() function, BTstack's loop() is called.
*/
/* LISTING_START(LEDeviceScannerLoop): Loop */
void loop(void) {
BTstack.loop();
}
/* LISTING_END(LEDeviceScannerLoop): Loop */
/*
@section Advertisement Callback
@text Whenever an Advertisement is received, isIBeacon() checks if
it contains an iBeacon.
If it's not an iBeacon, the BD_ADDR is compared to the address we are
looking for and the counter is incremented.
*/
/* LISTING_START(LEDeviceScannerAdvertisementCallback): Advertisement Callback
*/
void advertisementCallback(BLEAdvertisement *bleAdvertisement) {
if (!(bleAdvertisement->isIBeacon())) {
Serial.print("Device discovered: ");
Serial.println(bleAdvertisement->getBdAddr()->getAddressString());
for (size_t i = 0; i < sizeof(counter) / sizeof(int); i++) {
if (memcmp(bleAdvertisement->getBdAddr()->getAddress(),
known_devices[i].getAddress(), sizeof(known_devices[i])) == 0) {
counter[i]++;
Serial.printf("Known device: %s, has been discovered %d times.\n",
known_devices[i].getAddressString(), counter[i]);
}
}
}
}
/* LISTING_END(LEDeviceScannerAdvertisementCallback): Advertisement Callback */
@@ -0,0 +1,120 @@
// LE Peripheral Example - not working yet
#include <BTstackLib.h>
#include <SPI.h>
/*
EXAMPLE_START(LEPeripheral): LE Peripheral
@text BTstack allows to setup a GATT Services and Characteristics directly
from the setup function without using other tools outside of the Arduino IDE.
@section Setup
@text First, a number of callbacks are set. Then, a Service with a Read-only
Characteristic and a dynamic Characteristic is added to the GATT database.
In BTstack, a dynamic Characteristic is a Characteristic where reads and writes
are forwarded to the Sketch. In this example, the dynamic Characteristic is
provided by the single byte variable characteristic_data.
*/
/* LISTING_START(LEPeripheralSetup): Setup */
static char characteristic_data = 'H';
void setup(void) {
Serial.begin(9600);
// set callbacks
BTstack.setBLEDeviceConnectedCallback(deviceConnectedCallback);
BTstack.setBLEDeviceDisconnectedCallback(deviceDisconnectedCallback);
BTstack.setGATTCharacteristicRead(gattReadCallback);
BTstack.setGATTCharacteristicWrite(gattWriteCallback);
// setup GATT Database
BTstack.addGATTService(new UUID("B8E06067-62AD-41BA-9231-206AE80AB551"));
BTstack.addGATTCharacteristic(new UUID("f897177b-aee8-4767-8ecc-cc694fd5fcef"), ATT_PROPERTY_READ, "This is a String!");
BTstack.addGATTCharacteristicDynamic(new UUID("f897177b-aee8-4767-8ecc-cc694fd5fce0"), ATT_PROPERTY_READ | ATT_PROPERTY_WRITE | ATT_PROPERTY_NOTIFY, 0);
// startup Bluetooth and activate advertisements
BTstack.setup();
BTstack.startAdvertising();
}
/* LISTING_END(LEPeripheralSetup): Setup */
void loop(void) {
BTstack.loop();
}
/*
@section Device Connected Callback
@text When a remove device connects, device connected callback is callec.
*/
/* LISTING_START(LEPeripheralDeviceConnectedCallback): Device Connected Callback */
void deviceConnectedCallback(BLEStatus status, BLEDevice *device) {
(void) device;
switch (status) {
case BLE_STATUS_OK:
Serial.println("Device connected!");
break;
default:
break;
}
}
/* LISTING_END(LEPeripheralDeviceConnectedCallback): Device Connected Callback */
/*
@section Device Disconnected Callback
@text If the connection to a device breaks, the device disconnected callback
is called.
*/
/* LISTING_START(LEPeripheralDeviceDisconnectedCallback): Device Disconnected Callback */
void deviceDisconnectedCallback(BLEDevice * device) {
(void) device;
Serial.println("Disconnected.");
}
/* LISTING_END(LEPeripheralDeviceDisconnectedCallback): Device Disconnected Callback */
/*
@section Read Callback
@text In BTstack, the Read Callback is first called to query the size of the
Characteristic Value, before it is called to provide the data.
Both times, the size has to be returned. The data is only stored in the provided
buffer, if the buffer argeument is not NULL.
If more than one dynamic Characteristics is used, the value handle is used
to distinguish them.
*/
/* LISTING_START(LEPeripheralReadCallback): Read Callback */
uint16_t gattReadCallback(uint16_t value_handle, uint8_t * buffer, uint16_t buffer_size) {
(void) value_handle;
(void) buffer_size;
if (buffer) {
Serial.print("gattReadCallback, value: ");
Serial.println(characteristic_data, HEX);
buffer[0] = characteristic_data;
}
return 1;
}
/* LISTING_END(LEPeripheralDeviceDisconnectedCallback): Read Callback */
/*
@section Write Callback
@text When the remove device writes a Characteristic Value, the Write callback
is called. The buffer arguments points to the data of size size/
If more than one dynamic Characteristics is used, the value handle is used
to distinguish them.
*/
/* LISTING_START(LEPeripheralWriteCallback): Write Callback */
int gattWriteCallback(uint16_t value_handle, uint8_t *buffer, uint16_t size) {
(void) value_handle;
(void) size;
characteristic_data = buffer[0];
Serial.print("gattWriteCallback , value ");
Serial.println(characteristic_data, HEX);
return 0;
}
/* LISTING_END(LEPeripheralWriteCallback): Write Callback */
@@ -0,0 +1,25 @@
#include <BTstackLib.h>
#include <stdio.h>
#include <SPI.h>
/* EXAMPLE_START(iBeacon): iBeacon Simulator
@section Setup
@text After BTstack.setup(), iBeaconConfigure() configures BTstack
to send out iBeacons Advertisements with the provided Major ID,
Minor ID and UUID.
*/
/* LISTING_START(iBeaconSetup): iBeacon Setup */
UUID uuid("E2C56DB5-DFFB-48D2-B060-D0F5A71096E0");
void setup(void) {
Serial.begin(9600);
BTstack.setup();
BTstack.iBeaconConfigure(&uuid, 4711, 2);
BTstack.startAdvertising();
}
/* LISTING_END(iBeaconSetup) */
void loop(void) {
BTstack.loop();
}
@@ -0,0 +1,58 @@
#include <BTstackLib.h>
#include <SPI.h>
/* EXAMPLE_START(iBeaconScanner): iBeacon Scanner
@section Setup
@text After BTstack.setup(), BTstack is configured to call
advertisementCallback whenever an Advertisement was received.
Then, a device discovery is started
*/
/* LISTING_START(iBeaconSetup): iBeacon Scanner Setup */
void setup(void) {
Serial.begin(9600);
BTstack.setup();
BTstack.setBLEAdvertisementCallback(advertisementCallback);
BTstack.bleStartScanning();
}
/* LISTING_END(iBeaconSetup): iBeacon Scanner Setup */
void loop(void) {
BTstack.loop();
}
/*
@section Advertisement Callback
@text Whenever an Advertisement is received, isIBeacon() checks if
it contains an iBeacon. If yes, the Major ID, Minor ID, and UUID
is printed.
If it's not an iBeacon, only the BD_ADDR and the received signal strength
(RSSI) is shown.
*/
/* LISTING_START(iBeaconCallback): iBeacon Scanner Callback */
void advertisementCallback(BLEAdvertisement *adv) {
if (adv->isIBeacon()) {
Serial.print("iBeacon found ");
Serial.print(adv->getBdAddr()->getAddressString());
Serial.print(", RSSI ");
Serial.print(adv->getRssi());
Serial.print(", UUID ");
Serial.print(adv->getIBeaconUUID()->getUuidString());
Serial.print(", MajorID ");
Serial.print(adv->getIBeaconMajorID());
Serial.print(", MinorID ");
Serial.print(adv->getIBecaonMinorID());
Serial.print(", Measured Power ");
Serial.println(adv->getiBeaconMeasuredPower());
} else {
Serial.print("Device discovered: ");
Serial.print(adv->getBdAddr()->getAddressString());
Serial.print(", RSSI ");
Serial.println(adv->getRssi());
}
}
/* LISTING_END(iBeaconCallback): iBeacon Scanner Callback */
+100
View File
@@ -0,0 +1,100 @@
#######################################
# Syntax Coloring Map
#######################################
#######################################
# Library (KEYWORD3)
#######################################
BTstack KEYWORD3 RESERVED_WORD
#######################################
# Datatypes (KEYWORD1)
#######################################
BLEStatus KEYWORD2
UUID KEYWORD2
BD_ADDR_TYPE KEYWORD2
BD_ADDR KEYWORD2
BLEAdvertisement KEYWORD2
BLECharacteristic KEYWORD2
BLEService KEYWORD2
BLEDevice KEYWORD2
BTstackManager KEYWORD2
#######################################
# Methods and Functions (KEYWORD2)
#######################################
getUuidString KEYWORD2
getUuid128String KEYWORD2
getUuid KEYWORD2
matches KEYWORD2
getAddress KEYWORD2
getAddressString KEYWORD2
getAddressType KEYWORD2
getBdAddr KEYWORD2
getBdAddrType KEYWORD2
getRssi KEYWORD2
containsService KEYWORD2
nameHasPrefix KEYWORD2
getAdvData KEYWORD2
isIBeacon KEYWORD2
getIBeaconUUID KEYWORD2
getIBeaconMajorID KEYWORD2
getIBecaonMinorID KEYWORD2
getiBeaconMeasuredPower KEYWORD2
isValueHandle KEYWORD2
getCharacteristic KEYWORD2
getUUID KEYWORD2
getService KEYWORD2
getHandle KEYWORD2
discoverGATTServices KEYWORD2
discoverCharacteristicsForService KEYWORD2
readCharacteristic KEYWORD2
writeCharacteristic KEYWORD2
writeCharacteristicWithoutResponse KEYWORD2
subscribeForNotifications KEYWORD2
unsubscribeFromNotifications KEYWORD2
subscribeForIndications KEYWORD2
unsubscribeFromIndications KEYWORD2
setPublicBdAddr KEYWORD2
enablePacketLogger KEYWORD2
enableDebugLogger KEYWORD2
setAdvData KEYWORD2
iBeaconConfigure KEYWORD2
startAdvertising KEYWORD2
stopAdvertising KEYWORD2
bleStartScanning KEYWORD2
bleStopScanning KEYWORD2
bleConnect KEYWORD2
bleDisconnect KEYWORD2
setBLEAdvertisementCallback KEYWORD2
setBLEDeviceConnectedCallback KEYWORD2
setBLEDeviceDisconnectedCallback KEYWORD2
setGATTServiceDiscoveredCallback KEYWORD2
setGATTCharacteristicDiscoveredCallback KEYWORD2
setGATTCharacteristicReadCallback KEYWORD2
setGATTCharacteristicNotificationCallback KEYWORD2
setGATTCharacteristicIndicationCallback KEYWORD2
setGATTDoneCallback KEYWORD2
setGATTCharacteristicWrittenCallback KEYWORD2
setGATTCharacteristicSubscribedCallback KEYWORD2
setGATTCharacteristicUnsubscribedCallback KEYWORD2
setGATTCharacteristicRead KEYWORD2
setGATTCharacteristicWrite KEYWORD2
addGATTService KEYWORD2
addGATTCharacteristic KEYWORD2
addGATTCharacteristicDynamic KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
BLE_STATUS_OK LITERAL1
BLE_STATUS_DONE LITERAL1
BLE_STATUS_CONNECTION_TIMEOUT LITERAL1
BLE_STATUS_CONNECTION_ERROR LITERAL1
BLE_STATUS_OTHER_ERROR LITERAL1
PUBLIC_ADDRESS LITERAL1
PRIVAT_ADDRESS LITERAL1
+10
View File
@@ -0,0 +1,10 @@
name=BTstack
version=1.0
author=bluekitchen-gmbh
maintainer=Earle Philhower
sentence=BTstack Arduino library distributed with BTstack
paragraph=
category=Data Processing
url=
architectures=rp2040
dot_a_linkage=true
+914
View File
@@ -0,0 +1,914 @@
/**
Arduino Wrapper for BTstack
*/
#include <Arduino.h>
#include <SPI.h>
#ifdef __AVR__
#include <avr/wdt.h>
#endif
#if __arm__
//#include <Reset.h> // also provides NVIC_SystemReset
#endif
#include "BTstackLib.h"
#include "btstack_memory.h"
#include "hal_tick.h"
#include "hal_cpu.h"
#include "hci_cmd.h"
#include "btstack_util.h"
#include "btstack_run_loop.h"
#include "btstack_event.h"
#include "btstack_run_loop_embedded.h"
#include "hci_transport.h"
#include "hci_transport_h4.h"
#include "ad_parser.h"
//#include "btstack_chipset_em9301.h"
#include "btstack_debug.h"
#include "gap.h"
#include "hci.h"
#include "hci_dump.h"
#include "hci_dump_embedded_stdout.h"
#include "l2cap.h"
#include "ble/att_db.h"
#include "ble/att_server.h"
#include "ble/att_db_util.h"
#include "ble/le_device_db.h"
#include "ble/sm.h"
// Pin 13 has an LED connected on most Arduino boards.
//#define PIN_LED 13
// prototypes
extern "C" void hal_uart_dma_process(void);
enum {
SET_ADVERTISEMENT_PARAMS = 1 << 0,
SET_ADVERTISEMENT_DATA = 1 << 1,
SET_ADVERTISEMENT_ENABLED = 1 << 2,
};
typedef enum gattAction {
gattActionWrite,
gattActionSubscribe,
gattActionUnsubscribe,
gattActionServiceQuery,
gattActionCharacteristicQuery,
gattActionRead,
} gattAction_t;
static gattAction_t gattAction;
// btstack state
static int btstack_state;
static const uint8_t iBeaconAdvertisement01[] = { 0x02, 0x01 };
static const uint8_t iBeaconAdvertisement38[] = { 0x1a, 0xff, 0x4c, 0x00, 0x02, 0x15 };
static uint8_t adv_data[31];
static uint16_t adv_data_len = 0;
//static int gatt_is_characteristics_query;
static uint16_t le_peripheral_todos = 0;
static bool have_custom_addr;
static bd_addr_t public_bd_addr;
static btstack_timer_source_t connection_timer;
static void (*bleAdvertismentCallback)(BLEAdvertisement * bleAdvertisement) = NULL;
static void (*bleDeviceConnectedCallback)(BLEStatus status, BLEDevice * device) = NULL;
static void (*bleDeviceDisconnectedCallback)(BLEDevice * device) = NULL;
static void (*gattServiceDiscoveredCallback)(BLEStatus status, BLEDevice * device, BLEService * bleService) = NULL;
static void (*gattCharacteristicDiscoveredCallback)(BLEStatus status, BLEDevice * device, BLECharacteristic * characteristic) = NULL;
static void (*gattCharacteristicNotificationCallback)(BLEDevice * device, uint16_t value_handle, uint8_t* value, uint16_t length) = NULL;
static void (*gattCharacteristicIndicationCallback)(BLEDevice * device, uint16_t value_handle, uint8_t* value, uint16_t length) = NULL;
static void (*gattCharacteristicReadCallback)(BLEStatus status, BLEDevice * device, uint8_t * value, uint16_t length) = NULL;
static void (*gattCharacteristicWrittenCallback)(BLEStatus status, BLEDevice * device) = NULL;
static void (*gattCharacteristicSubscribedCallback)(BLEStatus status, BLEDevice * device) = NULL;
static void (*gattCharacteristicUnsubscribedCallback)(BLEStatus status, BLEDevice * device) = NULL;
// retarget printf to Serial
#ifdef ENERGIA
extern "C" int putchar(int c) {
Serial.write((uint8_t)c);
return c;
}
#else
#ifdef __AVR__
static FILE uartout = {0} ;
static int uart_putchar(char c, FILE *stream) {
Serial.write(c);
return 0;
}
#endif
// added for Arduino Zero. Arduino Due already has tis own _write(..) implementation
// in /Users/mringwal/Library/Arduino15/packages/arduino/hardware/sam/1.6.4/cores/arduino/syscalls_sam3.c
#if defined(__SAMD21G18A__)
// #ifdef __arm__
extern "C" int _write(int file, char *ptr, int len) {
int i;
for (i = 0; i < len; i++) {
if (ptr[i] == '\n') {
Serial.write((uint8_t)'\r');
}
Serial.write((uint8_t)ptr[i]);
}
return i;
}
#endif
#endif
// HAL CPU Implementation
extern "C" void hal_cpu_disable_irqs(void) { }
extern "C" void hal_cpu_enable_irqs(void) { }
extern "C" void hal_cpu_enable_irqs_and_sleep(void) { }
//
static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
(void) channel;
(void) size;
hci_con_handle_t con_handle;
switch (packet_type) {
case HCI_EVENT_PACKET:
switch (packet[0]) {
case BTSTACK_EVENT_STATE:
btstack_state = packet[2];
// bt stack activated, get started
if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING) {
le_peripheral_todos |= SET_ADVERTISEMENT_PARAMS
| SET_ADVERTISEMENT_DATA
| SET_ADVERTISEMENT_ENABLED;
bd_addr_t addr;
gap_local_bd_addr(addr);
printf("BTstack up and running at %s\n", bd_addr_to_str(addr));
}
break;
case HCI_EVENT_DISCONNECTION_COMPLETE:
if (bleDeviceDisconnectedCallback) {
con_handle = little_endian_read_16(packet, 3);
BLEDevice device(con_handle);
(*bleDeviceDisconnectedCallback)(&device);
}
le_peripheral_todos |= SET_ADVERTISEMENT_ENABLED;
break;
case GAP_EVENT_ADVERTISING_REPORT: {
if (bleAdvertismentCallback) {
BLEAdvertisement advertisement(packet);
(*bleAdvertismentCallback)(&advertisement);
}
break;
}
case HCI_EVENT_LE_META:
switch (packet[2]) {
case HCI_SUBEVENT_LE_CONNECTION_COMPLETE:
con_handle = little_endian_read_16(packet, 4);
printf("Connection complete, con_handle 0x%04x\n", con_handle);
btstack_run_loop_remove_timer(&connection_timer);
if (!bleDeviceConnectedCallback) {
break;
}
if (packet[3]) {
(*bleDeviceConnectedCallback)(BLE_STATUS_CONNECTION_ERROR, NULL);
} else {
BLEDevice device(con_handle);
(*bleDeviceConnectedCallback)(BLE_STATUS_OK, &device);
}
break;
default:
break;
}
break;
}
}
}
static void extract_service(gatt_client_service_t * service, uint8_t * packet) {
service->start_group_handle = little_endian_read_16(packet, 4);
service->end_group_handle = little_endian_read_16(packet, 6);
service->uuid16 = 0;
reverse_128(&packet[8], service->uuid128);
if (uuid_has_bluetooth_prefix(service->uuid128)) {
service->uuid16 = big_endian_read_32(service->uuid128, 0);
}
}
static void extract_characteristic(gatt_client_characteristic_t * characteristic, uint8_t * packet) {
characteristic->start_handle = little_endian_read_16(packet, 4);
characteristic->value_handle = little_endian_read_16(packet, 6);
characteristic->end_handle = little_endian_read_16(packet, 8);
characteristic->properties = little_endian_read_16(packet, 10);
characteristic->uuid16 = 0;
reverse_128(&packet[12], characteristic->uuid128);
if (uuid_has_bluetooth_prefix(characteristic->uuid128)) {
characteristic->uuid16 = big_endian_read_32(characteristic->uuid128, 0);
}
}
static void gatt_client_callback(uint8_t packet_type, uint16_t channel, uint8_t * packet, uint16_t size) {
(void) channel;
(void) packet_type;
(void) size;
// if (hci) event is not 4-byte aligned, event->handle causes crash
// workaround: check event type, assuming GATT event types are contagious
if (packet[0] < GATT_EVENT_QUERY_COMPLETE) {
return;
}
if (packet[0] > GATT_EVENT_MTU) {
return;
}
hci_con_handle_t con_handle = little_endian_read_16(packet, 2);
uint8_t status;
uint8_t * value;
uint16_t value_handle;
uint16_t value_length;
BLEDevice device(con_handle);
switch (hci_event_packet_get_type(packet)) {
case GATT_EVENT_SERVICE_QUERY_RESULT:
if (gattServiceDiscoveredCallback) {
gatt_client_service_t service;
extract_service(&service, packet);
BLEService bleService(service);
(*gattServiceDiscoveredCallback)(BLE_STATUS_OK, &device, &bleService);
}
break;
case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
if (gattCharacteristicDiscoveredCallback) {
gatt_client_characteristic_t characteristic;
extract_characteristic(&characteristic, packet);
BLECharacteristic bleCharacteristic(characteristic);
(*gattCharacteristicDiscoveredCallback)(BLE_STATUS_OK, &device, &bleCharacteristic);
}
break;
case GATT_EVENT_QUERY_COMPLETE:
status = little_endian_read_16(packet, 4);
switch (gattAction) {
case gattActionWrite:
if (gattCharacteristicWrittenCallback) {
gattCharacteristicWrittenCallback(status ? BLE_STATUS_OTHER_ERROR : BLE_STATUS_OK, &device);
}
break;
case gattActionSubscribe:
if (gattCharacteristicSubscribedCallback) {
gattCharacteristicSubscribedCallback(status ? BLE_STATUS_OTHER_ERROR : BLE_STATUS_OK, &device);
}
break;
case gattActionUnsubscribe:
if (gattCharacteristicUnsubscribedCallback) {
gattCharacteristicUnsubscribedCallback(status ? BLE_STATUS_OTHER_ERROR : BLE_STATUS_OK, &device);
}
break;
case gattActionServiceQuery:
if (gattServiceDiscoveredCallback) {
gattServiceDiscoveredCallback(BLE_STATUS_DONE, &device, NULL);
}
break;
case gattActionCharacteristicQuery:
if (gattCharacteristicDiscoveredCallback) {
gattCharacteristicDiscoveredCallback(BLE_STATUS_DONE, &device, NULL);
}
break;
default:
break;
};
break;
case GATT_EVENT_NOTIFICATION:
if (gattCharacteristicNotificationCallback) {
value_handle = little_endian_read_16(packet, 4);
value_length = little_endian_read_16(packet, 6);
value = &packet[8];
(*gattCharacteristicNotificationCallback)(&device, value_handle, value, value_length);
}
break;
case GATT_EVENT_INDICATION:
if (gattCharacteristicIndicationCallback) {
value_handle = little_endian_read_16(packet, 4);
value_length = little_endian_read_16(packet, 6);
value = &packet[8];
(*gattCharacteristicIndicationCallback)(&device, value_handle, value, value_length);
}
break;
case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
if (gattCharacteristicReadCallback) {
value_handle = little_endian_read_16(packet, 4);
value_length = little_endian_read_16(packet, 6);
value = &packet[8];
(*gattCharacteristicReadCallback)(BLE_STATUS_OK, &device, value, value_length);
}
break;
default:
break;
}
}
static void connection_timeout_handler(btstack_timer_source_t * timer) {
(void) timer;
// log_info("Cancel outgoing connection");
gap_connect_cancel();
if (!bleDeviceConnectedCallback) {
return;
}
(*bleDeviceConnectedCallback)(BLE_STATUS_CONNECTION_TIMEOUT, NULL); // page timeout 0x04
}
//
/// UUID class
UUID::UUID(void) {
memset(uuid, 0, 16);
}
UUID::UUID(const uint8_t uuid[16]) {
memcpy(this->uuid, uuid, 16);
}
UUID::UUID(const char * uuidStr) {
memset(uuid, 0, 16);
int len = strlen(uuidStr);
if (len <= 4) {
// Handle 4 Bytes HEX
unsigned int uuid16;
int result = sscanf((char *) uuidStr, "%x", &uuid16);
if (result == 1) {
uuid_add_bluetooth_prefix(uuid, uuid16);
}
return;
}
// quick UUID parser, ignoring dashes
int i = 0;
int data = 0;
int have_nibble = 0;
while (*uuidStr && i < 16) {
const char c = *uuidStr++;
if (c == '-') {
continue;
}
data = data << 4 | nibble_for_char(c);
if (!have_nibble) {
have_nibble = 1;
continue;
}
uuid[i++] = data;
data = 0;
have_nibble = 0;
}
}
const uint8_t * UUID::getUuid(void) const {
return uuid;
}
static char uuid16_buffer[5];
const char * UUID::getUuidString() const {
// TODO: fix uuid_has_bluetooth_prefix call to use const
if (uuid_has_bluetooth_prefix((uint8_t*)uuid)) {
sprintf(uuid16_buffer, "%04x", (uint16_t) big_endian_read_32(uuid, 0));
return uuid16_buffer;
} else {
// TODO: fix uuid128_to_str
return uuid128_to_str((uint8_t*)uuid);
}
}
const char * UUID::getUuid128String() const {
return uuid128_to_str((uint8_t*)uuid);
}
bool UUID::matches(UUID *other) const {
return memcmp(this->uuid, other->uuid, 16) == 0;
}
// BD_ADDR class
BD_ADDR::BD_ADDR(void) {
}
BD_ADDR::BD_ADDR(const char * address_string, BD_ADDR_TYPE address_type) : address_type(address_type) {
int processed = sscanf(address_string, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", address, address + 1,
address + 2, address + 3, address + 4, address + 5);
if (processed != 6) { // Set address to zeroes if we did not get six bytes back.
for (int i = 0; i < 6; i++) {
address[i] = 0;
}
}
}
BD_ADDR::BD_ADDR(const uint8_t address[6], BD_ADDR_TYPE address_type) : address_type(address_type) {
memcpy(this->address, address, 6);
}
const uint8_t * BD_ADDR::getAddress(void) {
return address;
}
const char * BD_ADDR::getAddressString(void) {
return bd_addr_to_str(address);
}
BD_ADDR_TYPE BD_ADDR::getAddressType(void) {
return address_type;
}
BLEAdvertisement::BLEAdvertisement(uint8_t * event_packet) :
advertising_event_type(event_packet[2]),
rssi(event_packet[10]),
data_length(event_packet[11]),
iBeacon_UUID(NULL) {
bd_addr_t addr;
reverse_bd_addr(&event_packet[4], addr);
bd_addr = BD_ADDR(addr, (BD_ADDR_TYPE)event_packet[3]);
memcpy(data, &event_packet[12], LE_ADVERTISING_DATA_SIZE);
}
BLEAdvertisement::~BLEAdvertisement() {
if (iBeacon_UUID) {
delete (iBeacon_UUID);
}
}
const uint8_t * BLEAdvertisement::getAdvData(void) {
return data;
}
BD_ADDR * BLEAdvertisement::getBdAddr(void) {
return &bd_addr;
}
int BLEAdvertisement::getRssi(void) {
return rssi > 127 ? rssi - 256 : rssi;
}
bool BLEAdvertisement::containsService(UUID * service) {
return ad_data_contains_uuid128(data_length, data, (uint8_t*) service->getUuid());
}
bool BLEAdvertisement::nameHasPrefix(const char * name_prefix) {
ad_context_t context;
int name_prefix_len = strlen(name_prefix);
for (ad_iterator_init(&context, data_length, data) ; ad_iterator_has_more(&context) ; ad_iterator_next(&context)) {
uint8_t data_type = ad_iterator_get_data_type(&context);
uint8_t data_len = ad_iterator_get_data_len(&context);
const uint8_t * data = ad_iterator_get_data(&context);
int compare_len = name_prefix_len;
switch (data_type) {
case 8: // shortented local name
case 9: // complete local name
if (compare_len > data_len) {
compare_len = data_len;
}
if (strncmp(name_prefix, (const char*) data, compare_len) == 0) {
return true;
}
break;
default:
break;
}
}
return false;
};
bool BLEAdvertisement::isIBeacon(void) {
return ((memcmp(iBeaconAdvertisement01, data, sizeof(iBeaconAdvertisement01)) == 0)
&& (memcmp(iBeaconAdvertisement38, &data[3], sizeof(iBeaconAdvertisement38)) == 0));
}
const UUID * BLEAdvertisement::getIBeaconUUID(void) {
if (!iBeacon_UUID) {
iBeacon_UUID = new UUID(&data[9]);
}
return iBeacon_UUID;
};
uint16_t BLEAdvertisement::getIBeaconMajorID(void) {
return big_endian_read_16(data, 25);
};
uint16_t BLEAdvertisement::getIBecaonMinorID(void) {
return big_endian_read_16(data, 27);
};
uint8_t BLEAdvertisement::getiBeaconMeasuredPower(void) {
return data[29];
}
BLECharacteristic::BLECharacteristic(void) {
}
BLECharacteristic::BLECharacteristic(gatt_client_characteristic_t characteristic)
: characteristic(characteristic), uuid(characteristic.uuid128) {
}
const UUID * BLECharacteristic::getUUID(void) {
return &uuid;
}
bool BLECharacteristic::matches(UUID * uuid) {
return this->uuid.matches(uuid);
}
bool BLECharacteristic::isValueHandle(uint16_t value_handle) {
return characteristic.value_handle == value_handle;
}
const gatt_client_characteristic_t * BLECharacteristic::getCharacteristic(void) {
return &characteristic;
}
BLEService::BLEService(void) {
}
BLEService::BLEService(gatt_client_service_t service)
: service(service), uuid(service.uuid128) {
}
const UUID * BLEService::getUUID(void) {
return &uuid;
}
bool BLEService::matches(UUID * uuid) {
return this->uuid.matches(uuid);
}
const gatt_client_service_t * BLEService::getService(void) {
return &service;
}
// discovery of services and characteristics
BLEDevice::BLEDevice(void) {
}
BLEDevice::BLEDevice(hci_con_handle_t handle)
: handle(handle) {
}
uint16_t BLEDevice::getHandle(void) {
return handle;
}
int BLEDevice::discoverGATTServices(void) {
return BTstack.discoverGATTServices(this);
}
int BLEDevice::discoverCharacteristicsForService(BLEService * service) {
return BTstack.discoverCharacteristicsForService(this, service);
}
int BLEDevice::readCharacteristic(BLECharacteristic * characteristic) {
return BTstack.readCharacteristic(this, characteristic);
}
int BLEDevice::writeCharacteristic(BLECharacteristic * characteristic, uint8_t * data, uint16_t size) {
return BTstack.writeCharacteristic(this, characteristic, data, size);
}
int BLEDevice::writeCharacteristicWithoutResponse(BLECharacteristic * characteristic, uint8_t * data, uint16_t size) {
return BTstack.writeCharacteristicWithoutResponse(this, characteristic, data, size);
}
int BLEDevice::subscribeForNotifications(BLECharacteristic * characteristic) {
return BTstack.subscribeForNotifications(this, characteristic);
}
int BLEDevice::unsubscribeFromNotifications(BLECharacteristic * characteristic) {
return BTstack.unsubscribeFromNotifications(this, characteristic);
}
int BLEDevice::subscribeForIndications(BLECharacteristic * characteristic) {
return BTstack.subscribeForIndications(this, characteristic);
}
int BLEDevice::unsubscribeFromIndications(BLECharacteristic * characteristic) {
return BTstack.unsubscribeFromIndications(this, characteristic);
}
static uint16_t (*gattReadCallback)(uint16_t characteristic_id, uint8_t * buffer, uint16_t buffer_size);
static int (*gattWriteCallback)(uint16_t characteristic_id, uint8_t *buffer, uint16_t buffer_size);
// ATT Client Read Callback for Dynamic Data
// - if buffer == NULL, don't copy data, just return size of value
// - if buffer != NULL, copy data and return number bytes copied
// @param offset defines start of attribute value
static uint16_t att_read_callback(hci_con_handle_t con_handle, uint16_t att_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size) {
(void) con_handle;
(void) offset;
if (gattReadCallback) {
return gattReadCallback(att_handle, buffer, buffer_size);
}
return 0;
}
/* LISTING_END */
/*
@section ATT Write
@text The only valid ATT write in this example is to the Client Characteristic Configuration, which configures notification
and indication. If the ATT handle matches the client configuration handle, the new configuration value is stored and used
in the heartbeat handler to decide if a new value should be sent. See Listing attWrite.
*/
/* LISTING_START(attWrite): ATT Write */
static int att_write_callback(hci_con_handle_t con_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size) {
(void) con_handle;
(void) transaction_mode;
(void) offset;
if (gattWriteCallback) {
gattWriteCallback(att_handle, buffer, buffer_size);
}
return 0;
}
BTstackManager::BTstackManager(void) {
// client_packet_handler = NULL;
have_custom_addr = false;
// reset handler
bleAdvertismentCallback = NULL;
bleDeviceConnectedCallback = NULL;
bleDeviceDisconnectedCallback = NULL;
gattServiceDiscoveredCallback = NULL;
gattCharacteristicDiscoveredCallback = NULL;
gattCharacteristicNotificationCallback = NULL;
att_db_util_init();
// disable LOG_INFO messages
hci_dump_enable_log_level(HCI_DUMP_LOG_LEVEL_INFO, 0);
#ifdef __AVR__
// configure stdout to go via Serial
fdev_setup_stream(&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE);
stdout = &uartout;
#endif
}
void BTstackManager::setBLEAdvertisementCallback(void (*callback)(BLEAdvertisement * bleAdvertisement)) {
bleAdvertismentCallback = callback;
}
void BTstackManager::setBLEDeviceConnectedCallback(void (*callback)(BLEStatus status, BLEDevice * device)) {
bleDeviceConnectedCallback = callback;
}
void BTstackManager::setBLEDeviceDisconnectedCallback(void (*callback)(BLEDevice * device)) {
bleDeviceDisconnectedCallback = callback;
}
void BTstackManager::setGATTServiceDiscoveredCallback(void (*callback)(BLEStatus status, BLEDevice * device, BLEService * bleService)) {
gattServiceDiscoveredCallback = callback;
}
void BTstackManager::setGATTCharacteristicDiscoveredCallback(void (*callback)(BLEStatus status, BLEDevice * device, BLECharacteristic * characteristic)) {
gattCharacteristicDiscoveredCallback = callback;
}
void BTstackManager::setGATTCharacteristicNotificationCallback(void (*callback)(BLEDevice * device, uint16_t value_handle, uint8_t* value, uint16_t length)) {
gattCharacteristicNotificationCallback = callback;
}
void BTstackManager::setGATTCharacteristicIndicationCallback(void (*callback)(BLEDevice * device, uint16_t value_handle, uint8_t* value, uint16_t length)) {
gattCharacteristicIndicationCallback = callback;
}
void BTstackManager::setGATTCharacteristicReadCallback(void (*callback)(BLEStatus status, BLEDevice * device, uint8_t * value, uint16_t length)) {
gattCharacteristicReadCallback = callback;
}
void BTstackManager::setGATTCharacteristicWrittenCallback(void (*callback)(BLEStatus status, BLEDevice * device)) {
gattCharacteristicWrittenCallback = callback;
}
void BTstackManager::setGATTCharacteristicSubscribedCallback(void (*callback)(BLEStatus status, BLEDevice * device)) {
gattCharacteristicSubscribedCallback = callback;
}
void BTstackManager::setGATTCharacteristicUnsubscribedCallback(void (*callback)(BLEStatus status, BLEDevice * device)) {
gattCharacteristicUnsubscribedCallback = callback;
}
int BTstackManager::discoverGATTServices(BLEDevice * device) {
gattAction = gattActionServiceQuery;
return gatt_client_discover_primary_services(gatt_client_callback, device->getHandle());
}
int BTstackManager::discoverCharacteristicsForService(BLEDevice * device, BLEService * service) {
gattAction = gattActionCharacteristicQuery;
return gatt_client_discover_characteristics_for_service(gatt_client_callback, device->getHandle(), (gatt_client_service_t*) service->getService());
}
int BTstackManager::readCharacteristic(BLEDevice * device, BLECharacteristic * characteristic) {
return gatt_client_read_value_of_characteristic(gatt_client_callback, device->getHandle(), (gatt_client_characteristic_t*) characteristic->getCharacteristic());
}
int BTstackManager::writeCharacteristic(BLEDevice * device, BLECharacteristic * characteristic, uint8_t * data, uint16_t size) {
gattAction = gattActionWrite;
return gatt_client_write_value_of_characteristic(gatt_client_callback, device->getHandle(), characteristic->getCharacteristic()->value_handle,
size, data);
}
int BTstackManager::writeCharacteristicWithoutResponse(BLEDevice * device, BLECharacteristic * characteristic, uint8_t * data, uint16_t size) {
return gatt_client_write_value_of_characteristic_without_response(device->getHandle(), characteristic->getCharacteristic()->value_handle,
size, data);
}
int BTstackManager::subscribeForNotifications(BLEDevice * device, BLECharacteristic * characteristic) {
gattAction = gattActionSubscribe;
return gatt_client_write_client_characteristic_configuration(gatt_client_callback, device->getHandle(), (gatt_client_characteristic_t*) characteristic->getCharacteristic(),
GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
}
int BTstackManager::subscribeForIndications(BLEDevice * device, BLECharacteristic * characteristic) {
gattAction = gattActionSubscribe;
return gatt_client_write_client_characteristic_configuration(gatt_client_callback, device->getHandle(), (gatt_client_characteristic_t*) characteristic->getCharacteristic(),
GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_INDICATION);
}
int BTstackManager::unsubscribeFromNotifications(BLEDevice * device, BLECharacteristic * characteristic) {
gattAction = gattActionUnsubscribe;
return gatt_client_write_client_characteristic_configuration(gatt_client_callback, device->getHandle(), (gatt_client_characteristic_t*) characteristic->getCharacteristic(),
GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE);
}
int BTstackManager::unsubscribeFromIndications(BLEDevice * device, BLECharacteristic * characteristic) {
gattAction = gattActionUnsubscribe;
return gatt_client_write_client_characteristic_configuration(gatt_client_callback, device->getHandle(), (gatt_client_characteristic_t*) characteristic->getCharacteristic(),
GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE);
}
void BTstackManager::bleConnect(BLEAdvertisement * advertisement, int timeout_ms) {
bleConnect(advertisement->getBdAddr(), timeout_ms);
}
void BTstackManager::bleConnect(BD_ADDR * address, int timeout_ms) {
bleConnect(address->getAddressType(), address->getAddress(), timeout_ms);
}
void BTstackManager::bleConnect(BD_ADDR_TYPE address_type, const char * address, int timeout_ms) {
(void) address_type;
(void) address;
(void) timeout_ms;
// TODO: implement
// log_error("BTstackManager::bleConnect(BD_ADDR_TYPE address_type, const char * address, int timeout_ms) not implemented");
}
void BTstackManager::bleConnect(BD_ADDR_TYPE address_type, const uint8_t address[6], int timeout_ms) {
gap_connect((uint8_t*)address, (bd_addr_type_t) address_type);
if (!timeout_ms) {
return;
}
btstack_run_loop_set_timer(&connection_timer, timeout_ms);
btstack_run_loop_set_timer_handler(&connection_timer, connection_timeout_handler);
btstack_run_loop_add_timer(&connection_timer);
}
void BTstackManager::bleDisconnect(BLEDevice * device) {
btstack_run_loop_remove_timer(&connection_timer);
gap_disconnect(device->getHandle());
}
void BTstackManager::setPublicBdAddr(bd_addr_t addr) {
have_custom_addr = true;
memcpy(public_bd_addr, addr, 6);
}
void bluetooth_hardware_error(uint8_t error) {
printf("Bluetooth Hardware Error event 0x%02x. Restarting...\n\n\n", error);
#ifdef __AVR__
wdt_enable(WDTO_15MS);
// wait for watchdog to trigger
#endif
#ifdef __arm__
// NVIC_SystemReset();
#endif
while (1);
}
#if 0
static hci_transport_config_uart_t config = {
HCI_TRANSPORT_CONFIG_UART,
115200,
0, // main baudrate
1, // flow control
NULL,
};
#endif
static btstack_packet_callback_registration_t hci_event_callback_registration;
void BTstackManager::setup(void) {
setup("BTstack LE Shield");
}
void BTstackManager::setup(const char * name) {
//#ifdef PIN_LED
// pinMode(PIN_LED, OUTPUT);
//#endif
// printf("BTstackManager::setup()\n");
#if 0
btstack_memory_init();
btstack_run_loop_init(btstack_run_loop_embedded_get_instance());
const hci_transport_t * transport = hci_transport_h4_instance(btstack_uart_block_embedded_instance());
hci_init(transport, (void*) &config);
hci_set_chipset(btstack_chipset_em9301_instance());
if (have_custom_addr) {
hci_set_bd_addr(public_bd_addr);
}
hci_set_hardware_error_callback(&bluetooth_hardware_error);
#endif
// inform about BTstack state
hci_event_callback_registration.callback = &packet_handler;
hci_add_event_handler(&hci_event_callback_registration);
l2cap_init();
// setup central device db
le_device_db_init();
sm_init();
att_server_init(att_db_util_get_address(), att_read_callback, att_write_callback);
gatt_client_init();
// setup advertisements params
uint16_t adv_int_min = 0x0030;
uint16_t adv_int_max = 0x0030;
uint8_t adv_type = 0;
bd_addr_t null_addr;
memset(null_addr, 0, 6);
gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00);
// setup advertisements data
int pos = 0;
const uint8_t flags[] = { 0x02, 0x01, 0x02 };
memcpy(&adv_data[pos], flags, sizeof(flags));
pos += sizeof(flags);
adv_data[pos++] = strlen(name) + 1;
adv_data[pos++] = 0x09;
memcpy(&adv_data[pos], name, strlen(name));
pos += strlen(name);
adv_data_len = pos;
gap_advertisements_set_data(adv_data_len, adv_data);
// turn on!
btstack_state = 0;
hci_power_control(HCI_POWER_ON);
}
void BTstackManager::enablePacketLogger(void) {
hci_dump_init(hci_dump_embedded_stdout_get_instance());
}
void BTstackManager::enableDebugLogger() {
// enable LOG_INFO messages
hci_dump_enable_log_level(HCI_DUMP_LOG_LEVEL_INFO, 1);
}
void BTstackManager::loop(void) {
// process data from/to Bluetooth module
// hal_uart_dma_process();
// BTstack Run Loop
// btstack_run_loop_embedded_execute_once();
}
void BTstackManager::bleStartScanning(void) {
// printf("Start scanning\n");
gap_start_scan();
}
void BTstackManager::bleStopScanning(void) {
gap_stop_scan();
}
void BTstackManager::setGATTCharacteristicRead(uint16_t (*cb)(uint16_t characteristic_id, uint8_t * buffer, uint16_t buffer_size)) {
gattReadCallback = cb;
}
void BTstackManager::setGATTCharacteristicWrite(int (*cb)(uint16_t characteristic_id, uint8_t *buffer, uint16_t buffer_size)) {
gattWriteCallback = cb;
}
void BTstackManager::addGATTService(UUID * uuid) {
att_db_util_add_service_uuid128((uint8_t*)uuid->getUuid());
}
uint16_t BTstackManager::addGATTCharacteristic(UUID * uuid, uint16_t flags, const char * text) {
return att_db_util_add_characteristic_uuid128((uint8_t*)uuid->getUuid(), flags, ATT_SECURITY_NONE, ATT_SECURITY_NONE, (uint8_t*)text, strlen(text));
}
uint16_t BTstackManager::addGATTCharacteristic(UUID * uuid, uint16_t flags, uint8_t * data, uint16_t data_len) {
return att_db_util_add_characteristic_uuid128((uint8_t*)uuid->getUuid(), flags, ATT_SECURITY_NONE, ATT_SECURITY_NONE, data, data_len);
}
uint16_t BTstackManager::addGATTCharacteristicDynamic(UUID * uuid, uint16_t flags, uint16_t characteristic_id) {
(void) characteristic_id;
return att_db_util_add_characteristic_uuid128((uint8_t*)uuid->getUuid(), flags | ATT_PROPERTY_DYNAMIC, ATT_SECURITY_NONE, ATT_SECURITY_NONE, NULL, 0);
}
void BTstackManager::setAdvData(uint16_t adv_data_len, const uint8_t * adv_data) {
gap_advertisements_set_data(adv_data_len, (uint8_t*) adv_data);
}
void BTstackManager::startAdvertising() {
gap_advertisements_enable(1);
}
void BTstackManager::stopAdvertising() {
gap_advertisements_enable(0);
}
void BTstackManager::iBeaconConfigure(UUID * uuid, uint16_t major_id, uint16_t minor_id, uint8_t measured_power) {
memcpy(adv_data, iBeaconAdvertisement01, sizeof(iBeaconAdvertisement01));
adv_data[2] = 0x06;
memcpy(&adv_data[3], iBeaconAdvertisement38, sizeof(iBeaconAdvertisement38));
memcpy(&adv_data[9], uuid->getUuid(), 16);
big_endian_store_16(adv_data, 25, major_id);
big_endian_store_16(adv_data, 27, minor_id);
adv_data[29] = measured_power;
adv_data_len = 30;
gap_advertisements_set_data(adv_data_len, adv_data);
}
// 02 01 06 1A FF 4C 00 02 15 -- F8 97 17 7B AE E8 47 67 8E CC CC 69 4F D5 FC EE -- 12 67 00 02 00 00
// 02 01 06 1a ff 4c 00 02 15 -- FB 0B 57 A2 82 28 44 CD 91 3A 94 A1 22 BA 12 06 -- 00 01 00 02 D1 00
BTstackManager BTstack;
+205
View File
@@ -0,0 +1,205 @@
/**
Arduino Wrapper for BTstack
*/
#include <_needsbt.h>
#ifndef __ARDUINO_BTSTACK_H
#define __ARDUINO_BTSTACK_H
#if defined __cplusplus
extern "C" {
#endif
#include "ble/att_db.h"
#include "btstack_util.h"
#include "ble/gatt_client.h"
#include "hci.h"
#include <stdint.h>
typedef enum BLEStatus {
BLE_STATUS_OK,
BLE_STATUS_DONE, // e.g. for service or characteristic discovery done
BLE_STATUS_CONNECTION_TIMEOUT,
BLE_STATUS_CONNECTION_ERROR,
BLE_STATUS_OTHER_ERROR
} BLEStatus;
typedef void (*btstack_packet_handler_t)(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
class UUID {
private:
uint8_t uuid[16];
public:
UUID();
UUID(const uint8_t uuid[16]);
UUID(const char * uuidStr);
const char * getUuidString() const;
const char * getUuid128String() const;
const uint8_t * getUuid(void) const;
bool matches(UUID *uuid) const;
};
typedef enum BD_ADDR_TYPE {
PUBLIC_ADDRESS = 0,
PRIVAT_ADDRESS
} BD_ADDR_TYPE;
class BD_ADDR {
private:
uint8_t address[6];
BD_ADDR_TYPE address_type;
public:
BD_ADDR();
BD_ADDR(const char * address_string, BD_ADDR_TYPE address_type = PUBLIC_ADDRESS);
BD_ADDR(const uint8_t address[6], BD_ADDR_TYPE address_type = PUBLIC_ADDRESS);
const uint8_t * getAddress();
const char * getAddressString();
BD_ADDR_TYPE getAddressType();
};
class BLEAdvertisement {
private:
uint8_t advertising_event_type;
uint8_t rssi;
uint8_t data_length;
uint8_t data[10 + LE_ADVERTISING_DATA_SIZE];
BD_ADDR bd_addr;
UUID * iBeacon_UUID;
public:
BLEAdvertisement(uint8_t * event_packet);
~BLEAdvertisement();
BD_ADDR * getBdAddr();
BD_ADDR_TYPE getBdAddrType();
int getRssi();
bool containsService(UUID * service);
bool nameHasPrefix(const char * namePrefix);
const uint8_t * getAdvData();
bool isIBeacon();
const UUID * getIBeaconUUID();
uint16_t getIBeaconMajorID();
uint16_t getIBecaonMinorID();
uint8_t getiBeaconMeasuredPower();
};
class BLECharacteristic {
private:
gatt_client_characteristic_t characteristic;
UUID uuid;
public:
BLECharacteristic();
BLECharacteristic(gatt_client_characteristic_t characteristic);
const UUID * getUUID();
bool matches(UUID * uuid);
bool isValueHandle(uint16_t value_handle);
const gatt_client_characteristic_t * getCharacteristic();
};
class BLEService {
private:
gatt_client_service_t service;
UUID uuid;
public:
BLEService();
BLEService(gatt_client_service_t service);
const UUID * getUUID();
bool matches(UUID * uuid);
const gatt_client_service_t * getService();
};
class BLEDevice {
private:
hci_con_handle_t handle;
public:
BLEDevice();
BLEDevice(hci_con_handle_t handle);
hci_con_handle_t getHandle();
// discovery of services and characteristics
int discoverGATTServices();
int discoverCharacteristicsForService(BLEService * service);
// read/write
int readCharacteristic(BLECharacteristic * characteristic);
int writeCharacteristic(BLECharacteristic * characteristic, uint8_t * data, uint16_t size);
int writeCharacteristicWithoutResponse(BLECharacteristic * characteristic, uint8_t * data, uint16_t size);
// subscribe/unsubscribe
int subscribeForNotifications(BLECharacteristic * characteristic);
int unsubscribeFromNotifications(BLECharacteristic * characteristic);
int subscribeForIndications(BLECharacteristic * characteristic);
int unsubscribeFromIndications(BLECharacteristic * characteristic);
};
class BTstackManager {
public:
BTstackManager(void);
void setup(void);
void setup(const char * name);
void loop(void);
void setPublicBdAddr(bd_addr_t addr);
void enablePacketLogger();
void enableDebugLogger();
void setAdvData(uint16_t size, const uint8_t * data);
void iBeaconConfigure(UUID * uuid, uint16_t major_id, uint16_t minor_id, uint8_t measured_power = 0xc6);
void startAdvertising();
void stopAdvertising();
void bleStartScanning();
void bleStopScanning();
// connection management
void bleConnect(BD_ADDR_TYPE address_type, const uint8_t address[6], int timeout_ms);
void bleConnect(BD_ADDR_TYPE address_type, const char * address, int timeout_ms);
void bleConnect(BD_ADDR * address, int timeout_ms);
void bleConnect(BLEAdvertisement * advertisement, int timeout_ms);
void bleDisconnect(BLEDevice * device);
// discovery of services and characteristics
int discoverGATTServices(BLEDevice * device);
int discoverCharacteristicsForService(BLEDevice * peripheral, BLEService * service);
// read/write
int readCharacteristic(BLEDevice * device, BLECharacteristic * characteristic);
int writeCharacteristic(BLEDevice * device, BLECharacteristic * characteristic, uint8_t * data, uint16_t size);
int writeCharacteristicWithoutResponse(BLEDevice * device, BLECharacteristic * characteristic, uint8_t * data, uint16_t size);
// subscribe/unsubscribe notification and indications
int subscribeForNotifications(BLEDevice * device, BLECharacteristic * characteristic);
int unsubscribeFromNotifications(BLEDevice * device, BLECharacteristic * characteristic);
int subscribeForIndications(BLEDevice * device, BLECharacteristic * characteristic);
int unsubscribeFromIndications(BLEDevice * device, BLECharacteristic * characteristic);
// Callbacks
void setBLEAdvertisementCallback(void (*)(BLEAdvertisement * bleAdvertisement));
void setBLEDeviceConnectedCallback(void (*)(BLEStatus status, BLEDevice * device));
void setBLEDeviceDisconnectedCallback(void (*)(BLEDevice * device));
void setGATTServiceDiscoveredCallback(void (*)(BLEStatus status, BLEDevice * device, BLEService * bleService));
void setGATTCharacteristicDiscoveredCallback(void (*)(BLEStatus status, BLEDevice * device, BLECharacteristic * characteristic));
void setGATTCharacteristicReadCallback(void (*)(BLEStatus status, BLEDevice * device, uint8_t * value, uint16_t length));
void setGATTCharacteristicNotificationCallback(void (*)(BLEDevice * device, uint16_t value_handle, uint8_t* value, uint16_t length));
void setGATTCharacteristicIndicationCallback(void (*)(BLEDevice * device, uint16_t value_handle, uint8_t* value, uint16_t length));
void setGATTDoneCallback(void (*)(BLEStatus status, BLEDevice * device));
void setGATTCharacteristicWrittenCallback(void (*)(BLEStatus status, BLEDevice * device));
void setGATTCharacteristicSubscribedCallback(void (*)(BLEStatus status, BLEDevice * device));
void setGATTCharacteristicUnsubscribedCallback(void (*)(BLEStatus status, BLEDevice * device));
void setGATTCharacteristicRead(uint16_t (*)(uint16_t characteristic_id, uint8_t * buffer, uint16_t buffer_size));
void setGATTCharacteristicWrite(int (*)(uint16_t characteristic_id, uint8_t *buffer, uint16_t buffer_size));
void addGATTService(UUID * uuid);
uint16_t addGATTCharacteristic(UUID * uuid, uint16_t flags, const char * text);
uint16_t addGATTCharacteristic(UUID * uuid, uint16_t flags, uint8_t * data, uint16_t data_len);
uint16_t addGATTCharacteristicDynamic(UUID * uuid, uint16_t flags, uint16_t characteristic_id);
};
extern BTstackManager BTstack;
#if defined __cplusplus
}
#endif
#endif // __ARDUINO_BTSTACK_H
+4
View File
@@ -2,6 +2,8 @@
# https://arduino.github.io/arduino-cli/library-specification/#keywords
# Formatted by a single true tab (not spaces)
FreeRTOS KEYWORD1
# Datatypes (KEYWORD1)
StackType_t KEYWORD1
BaseType_t KEYWORD1
@@ -34,6 +36,7 @@ pcTaskGetName KEYWORD2
ulTaskNotifyTake KEYWORD2
vTaskNotifyGiveFromISR KEYWORD2
taskYIELD KEYWORD2
vTaskCoreAffinitySet KEYWORD2
vTaskSuspend KEYWORD2
vTaskResume KEYWORD2
xTaskResumeFromISR KEYWORD2
@@ -41,6 +44,7 @@ xTaskGetTickCount KEYWORD2
xTaskGetTickCountFromISR KEYWORD2
uxTaskGetNumberOfTasks KEYWORD2
uxTaskGetStackHighWaterMark KEYWORD2
uxTaskGetSystemState KEYWORD2
# Instances (KEYWORD2)
+1 -4
View File
@@ -29,10 +29,7 @@
#define configUSE_TASK_PREEMPTION_DISABLE 1
#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)
#define configNEWLIB_REENTRANT_IS_DYNAMIC 1
/* Run time stats related definitions. */
void vMainConfigureTimerForRunTimeStats(void);
+13
View File
@@ -1 +1,14 @@
#include "../lib/FreeRTOS-Kernel/tasks.c"
//See https://github.com/FreeRTOS/FreeRTOS-Kernel/pull/496
struct _reent* __wrap___getreent(void) {
// No lock needed because if this changes, we won't be running anymore.
TCB_t *pxCurTask = xTaskGetCurrentTaskHandle();
if (pxCurTask == NULL) {
// No task running. Return global struct.
return _GLOBAL_REENT;
} else {
// We have a task; return its reentrant struct.
return &pxCurTask->xNewLib_reent;
}
}
+102 -52
View File
@@ -40,7 +40,59 @@
#include <pico.h>
#include <pico/time.h>
#include "_freertos.h"
#include <_freertos.h>
// Interfaces for the main core to use FreeRTOS mutexes
extern "C" {
extern volatile bool __otherCoreIdled;
SemaphoreHandle_t __freertos_mutex_create() {
return xSemaphoreCreateMutex();
}
SemaphoreHandle_t _freertos_recursive_mutex_create() {
return xSemaphoreCreateRecursiveMutex();
}
void __freertos_mutex_take(SemaphoreHandle_t mtx) {
xSemaphoreTake(mtx, portMAX_DELAY);
}
int __freertos_mutex_take_from_isr(SemaphoreHandle_t mtx, BaseType_t* pxHigherPriorityTaskWoken) {
return xSemaphoreTakeFromISR(mtx, pxHigherPriorityTaskWoken);
}
int __freertos_mutex_try_take(SemaphoreHandle_t mtx) {
return xSemaphoreTake(mtx, 0);
}
void __freertos_mutex_give(SemaphoreHandle_t mtx) {
xSemaphoreGive(mtx);
}
void __freertos_mutex_give_from_isr(SemaphoreHandle_t mtx, BaseType_t* pxHigherPriorityTaskWoken) {
BaseType_t hiPrio = pxHigherPriorityTaskWoken ? *pxHigherPriorityTaskWoken : pdFALSE;
xSemaphoreGiveFromISR(mtx, &hiPrio);
portYIELD_FROM_ISR(hiPrio);
}
void __freertos_recursive_mutex_take(SemaphoreHandle_t mtx) {
xSemaphoreTakeRecursive(mtx, portMAX_DELAY);
}
int __freertos_recursive_mutex_try_take(SemaphoreHandle_t mtx) {
return xSemaphoreTakeRecursive(mtx, 0);
}
void __freertos_recursive_mutex_give(SemaphoreHandle_t mtx) {
xSemaphoreGiveRecursive(mtx);
}
bool __freertos_check_if_in_isr() {
return portCHECK_IF_IN_ISR();
}
}
/*-----------------------------------------------------------*/
@@ -108,6 +160,40 @@ extern "C" void yield() {
taskYIELD();
}
static TaskHandle_t __idleCoreTask[2];
static void __no_inline_not_in_flash_func(IdleThisCore)(void *param) {
(void) param;
while (true) {
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
vTaskPreemptionDisable(nullptr);
portDISABLE_INTERRUPTS();
__otherCoreIdled = true;
while (__otherCoreIdled) {
/* noop */
}
portENABLE_INTERRUPTS();
vTaskPreemptionEnable(nullptr);
}
}
extern "C" void __no_inline_not_in_flash_func(__freertos_idle_other_core)() {
vTaskPreemptionDisable(nullptr);
xTaskNotifyGive(__idleCoreTask[ 1 ^ sio_hw->cpuid ]);
while (!__otherCoreIdled) {
/* noop */
}
portDISABLE_INTERRUPTS();
vTaskSuspendAll();
}
extern "C" void __no_inline_not_in_flash_func(__freertos_resume_other_core)() {
__otherCoreIdled = false;
portENABLE_INTERRUPTS();
xTaskResumeAll();
vTaskPreemptionEnable(nullptr);
}
extern mutex_t __usb_mutex;
static TaskHandle_t __usbTask;
static void __usb(void *param);
@@ -124,6 +210,12 @@ void startFreeRTOS(void) {
vTaskCoreAffinitySet(c1, 1 << 1);
}
// Create the idle-other-core tasks (for when flash is being written)
xTaskCreate(IdleThisCore, "IdleCore0", 128, 0, configMAX_PRIORITIES - 1, __idleCoreTask + 0);
vTaskCoreAffinitySet(__idleCoreTask[0], 1 << 0);
xTaskCreate(IdleThisCore, "IdleCore1", 128, 0, configMAX_PRIORITIES - 1, __idleCoreTask + 1);
vTaskCoreAffinitySet(__idleCoreTask[1], 1 << 1);
// Initialise and run the freeRTOS scheduler. Execution should never return here.
__freeRTOSinitted = true;
vTaskStartScheduler();
@@ -216,16 +308,20 @@ void vApplicationTickHook(void) {
Private function to enable board led to use it in application hooks
*/
void prvSetMainLedOn(void) {
#ifdef LED_BUILTIN
gpio_init(LED_BUILTIN);
gpio_set_dir(LED_BUILTIN, true);
gpio_put(LED_BUILTIN, true);
#endif
}
/**
Private function to blink board led to use it in application hooks
*/
void prvBlinkMainLed(void) {
#ifdef LED_BUILTIN
gpio_put(LED_BUILTIN, !gpio_get(LED_BUILTIN));
#endif
}
#endif
@@ -376,9 +472,10 @@ static void __usb(void *param) {
__usbInitted = true;
while (true) {
if (mutex_try_enter(&__usb_mutex, NULL)) {
auto m = __get_freertos_mutex_for_ptr(&__usb_mutex);
if (xSemaphoreTake(m, 0)) {
tud_task();
mutex_exit(&__usb_mutex);
xSemaphoreGive(m);
}
vTaskDelay(1 / portTICK_PERIOD_MS);
}
@@ -393,54 +490,7 @@ void __USBStart() {
__SetupDescHIDReport();
__SetupUSBDescriptor();
// Make highest prio and locked to core 0
xTaskCreate(__usb, "USB", 256, 0, configMAX_PRIORITIES - 1, &__usbTask);
// Make high prio and locked to core 0
xTaskCreate(__usb, "USB", 256, 0, configMAX_PRIORITIES - 2, &__usbTask);
vTaskCoreAffinitySet(__usbTask, 1 << 0);
}
// Interfaces for the main core to use FreeRTOS mutexes
extern "C" {
SemaphoreHandle_t __freertos_mutex_create() {
return xSemaphoreCreateMutex();
}
SemaphoreHandle_t _freertos_recursive_mutex_create() {
return xSemaphoreCreateRecursiveMutex();
}
void __freertos_mutex_take(SemaphoreHandle_t mtx) {
xSemaphoreTake(mtx, portMAX_DELAY);
}
void __freertos_mutex_take_from_isr(SemaphoreHandle_t mtx) {
xSemaphoreTakeFromISR(mtx, NULL);
}
int __freertos_mutex_try_take(SemaphoreHandle_t mtx) {
return xSemaphoreTake(mtx, 0);
}
void __freertos_mutex_give(SemaphoreHandle_t mtx) {
xSemaphoreGive(mtx);
}
void __freertos_mutex_give_from_isr(SemaphoreHandle_t mtx) {
xSemaphoreGiveFromISR(mtx, NULL);
}
void __freertos_recursive_mutex_take(SemaphoreHandle_t mtx) {
xSemaphoreTakeRecursive(mtx, portMAX_DELAY);
}
int __freertos_recursive_mutex_try_take(SemaphoreHandle_t mtx) {
return xSemaphoreTakeRecursive(mtx, 0);
}
void __freertos_recursive_mutex_give(SemaphoreHandle_t mtx) {
xSemaphoreGiveRecursive(mtx);
}
}
@@ -21,6 +21,7 @@
#pragma once
#include <_needsbt.h>
#include <Arduino.h>
#include <functional>
#include <pico/cyw43_arch.h>
@@ -21,6 +21,7 @@
#pragma once
#include <_needsbt.h>
#include <Arduino.h>
#include <functional>
#include <pico/cyw43_arch.h>
-4
View File
@@ -282,11 +282,7 @@ HTTPUpdateResult HTTPUpdate::handleUpdate(HTTPClient& http, const String& curren
_cbEnd();
}
#ifdef ATOMIC_FS_UPDATE
if (_rebootOnUpdate) {
#else
if (_rebootOnUpdate && !spiffs) {
#endif
rp2040.restart();
}
@@ -0,0 +1,84 @@
/*
This example demonstrates I2S output with the optional MCLK signal.
Note: System clock sppeds used here may not be compatible with all other libraries
requiring specific sys_clks, particularly Pico PIO USB.
Original 17 November 2016
by Sandeep Mistry
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
modified for MCLK by Richard Palmer June 2023
*/
#include <I2S.h>
// Create the I2S port using a PIO state machine
I2S i2s(OUTPUT);
// GPIO pin numbers
#define pDOUT 19
#define pBCLK 20
#define pWS (pBCLK+1)
#define pMCLK 22 // optional MCLK pin
const int frequency = 440; // frequency of square wave in Hz
const int sampleRate = 16000; // minimum for many i2s DACs
const int bitsPerSample = 16;
const int amplitude = 1 << (bitsPerSample - 2); // amplitude of square wave = 1/2 of maximum
#define MCLK_MUL 256 // depends on audio hardware. Suits common hardware.
const int halfWavelength = sampleRate / (2 * frequency); // half wavelength of square wave
int16_t sample = amplitude; // current sample value
int count = 0;
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, 1);
// set the system clock to a compatible value to the samplerate
i2s.setSysClk(sampleRate); // best to do this before starting anything clock-dependent
Serial.begin(115200);
while (!Serial) {
delay(10);
}
Serial.println("I2S with MCLK - square wave");
i2s.setBCLK(pBCLK);
i2s.setDATA(pDOUT);
i2s.setMCLK(pMCLK); // must be run before setFrequency() and i2s.begin()
i2s.setMCLKmult(MCLK_MUL); // also before i2s.begin()
i2s.setBitsPerSample(16);
// start I2S at the sample rate with 16-bits per sample
if (!i2s.begin(sampleRate)) {
Serial.println("Failed to initialize I2S!");
while (100); // do nothing
}
}
void loop() {
if (count % halfWavelength == 0) {
// invert the sample every half wavelength count multiple to generate square wave
sample = -1 * sample;
}
// write the same sample twice, once for left and once for the right channel
i2s.write(sample);
i2s.write(sample);
// increment the counter for the next sample
count++;
}
+49
View File
@@ -0,0 +1,49 @@
/*
This example just sends out TDM data on 8 channels with their value
equal to the channel number.
Released to the public domain by Earle F. Philhower, III <earlephilhower@yahoo.com>
*/
#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 sampleRate = 1000;
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, 1);
Serial.begin(115200);
Serial.println("I2S TDM test");
i2s.setBCLK(pBCLK);
i2s.setDATA(pDOUT);
i2s.setBitsPerSample(32);
i2s.setTDMFormat();
i2s.setTDMChannels(8);
// start I2S at the sample rate with 16-bits per sample
if (!i2s.begin(sampleRate)) {
Serial.println("Failed to initialize I2S!");
while (1); // do nothing
}
}
void loop() {
uint32_t x = 0;
while (1) {
i2s.write(x & 0x07);
if (!(x % 10000)) {
Serial.println(x);
}
x++;
}
}
+6
View File
@@ -16,10 +16,16 @@ end KEYWORD2
setBCLK KEYWORD2
setDATA KEYWORD2
setMCLK KEYWORD2
setBitsPerSample KEYWORD2
setFrequency KEYWORD2
setBuffers KEYWORD2
setLSBJFormat KEYWORD2
setTDMFormat KEYWORD2
setTDMChannels KEYWORD2
swapClocks KEYWORD2
setMCLKmult KEYWORD2
setSysClk KEYWORD2
read8 KEYWORD2
read16 KEYWORD2
+129 -19
View File
@@ -21,6 +21,7 @@
#include <Arduino.h>
#include "I2S.h"
#include "pio_i2s.pio.h"
#include <pico/stdlib.h>
I2S::I2S(PinMode direction) {
@@ -30,6 +31,8 @@ I2S::I2S(PinMode direction) {
_isOutput = direction == OUTPUT;
_pinBCLK = 26;
_pinDOUT = 28;
_pinMCLK = 25;
_MCLKenabled = false;
#ifdef PIN_I2S_BCLK
_pinBCLK = PIN_I2S_BCLK;
#endif
@@ -48,13 +51,18 @@ I2S::I2S(PinMode direction) {
_freq = 48000;
_arb = nullptr;
_cb = nullptr;
_buffers = 8;
_buffers = 6;
_bufferWords = 0;
_silenceSample = 0;
_isLSBJ = false;
_isTDM = false;
_tdmChannels = 8;
_swapClocks = false;
_multMCLK = 256;
}
I2S::~I2S() {
end();
}
bool I2S::setBCLK(pin_size_t pin) {
@@ -65,6 +73,14 @@ bool I2S::setBCLK(pin_size_t pin) {
return true;
}
bool I2S::setMCLK(pin_size_t pin) {
if (_running || (pin > 28)) {
return false;
}
_pinMCLK = pin;
return true;
}
bool I2S::setDATA(pin_size_t pin) {
if (_running || (pin > 29)) {
return false;
@@ -94,12 +110,41 @@ bool I2S::setBuffers(size_t buffers, size_t bufferWords, int32_t silenceSample)
bool I2S::setFrequency(int newFreq) {
_freq = newFreq;
if (_running) {
float bitClk = _freq * _bps * 2.0 /* channels */ * 2.0 /* edges per clock */;
pio_sm_set_clkdiv(_pio, _sm, (float)clock_get_hz(clk_sys) / bitClk);
if (_MCLKenabled) {
int bitClk = _freq * _bps * (_isTDM ? (double)_tdmChannels : 2.0) /* channels */ * 2.0 /* edges per clock */;
pio_sm_set_clkdiv_int_frac(_pio, _sm, clock_get_hz(clk_sys) / bitClk, 0);
} else {
float bitClk = _freq * _bps * (_isTDM ? (double)_tdmChannels : 2.0) /* channels */ * 2.0 /* edges per clock */;
pio_sm_set_clkdiv(_pio, _sm, (float)clock_get_hz(clk_sys) / bitClk);
}
}
return true;
}
bool I2S::setSysClk(int samplerate) { // optimise sys_clk for desired samplerate
if (samplerate % 11025 == 0) {
set_sys_clock_khz(I2SSYSCLK_44_1, false); // 147.6 unsuccessful - no I2S no USB
return true;
}
if (samplerate % 8000 == 0) {
set_sys_clock_khz(I2SSYSCLK_8, false);
return true;
}
return false;
}
bool I2S::setMCLKmult(int mult) {
if (_running || !_isOutput) {
return false;
}
if ((mult % 64) == 0) {
_MCLKenabled = true;
_multMCLK = mult;
return true;
}
return false;
}
bool I2S::setLSBJFormat() {
if (_running || !_isOutput) {
return false;
@@ -108,6 +153,30 @@ bool I2S::setLSBJFormat() {
return true;
}
bool I2S::setTDMFormat() {
if (_running || !_isOutput) {
return false;
}
_isTDM = true;
return true;
}
bool I2S::setTDMChannels(int channels) {
if (_running || !_isOutput) {
return false;
}
_tdmChannels = channels;
return true;
}
bool I2S::swapClocks() {
if (_running || !_isOutput) {
return false;
}
_swapClocks = true;
return true;
}
void I2S::onTransmit(void(*fn)(void)) {
if (_isOutput) {
_cb = fn;
@@ -126,22 +195,47 @@ void I2S::onReceive(void(*fn)(void)) {
}
}
void I2S::MCLKbegin() {
int off = 0;
_i2sMCLK = new PIOProgram(&pio_i2s_mclk_program);
_i2sMCLK->prepare(&_pioMCLK, &_smMCLK, &off); // not sure how to use the same PIO
pio_i2s_MCLK_program_init(_pioMCLK, _smMCLK, off, _pinMCLK);
int mClk = _multMCLK * _freq * 2.0 /* edges per clock */;
pio_sm_set_clkdiv_int_frac(_pioMCLK, _smMCLK, clock_get_hz(clk_sys) / mClk, 0);
pio_sm_set_enabled(_pioMCLK, _smMCLK, true);
}
bool I2S::begin() {
_running = true;
_hasPeeked = false;
_isHolding = 0;
int off = 0;
_i2s = new PIOProgram(_isOutput ? (_isLSBJ ? &pio_lsbj_out_program : &pio_i2s_out_program) : &pio_i2s_in_program);
_i2s->prepare(&_pio, &_sm, &off);
if (!_swapClocks) {
_i2s = new PIOProgram(_isOutput ? (_isTDM ? &pio_tdm_out_program : (_isLSBJ ? &pio_lsbj_out_program : &pio_i2s_out_program)) : &pio_i2s_in_program);
} else {
_i2s = new PIOProgram(_isOutput ? (_isTDM ? &pio_tdm_out_swap_program : (_isLSBJ ? &pio_lsbj_out_swap_program : &pio_i2s_out_swap_program)) : &pio_i2s_in_swap_program);
}
if (!_i2s->prepare(&_pio, &_sm, &off)) {
_running = false;
delete _i2s;
_i2s = nullptr;
return false;
}
if (_isOutput) {
if (_isLSBJ) {
pio_lsbj_out_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps);
if (_isTDM) {
pio_tdm_out_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps, _swapClocks, _tdmChannels);
} else if (_isLSBJ) {
pio_lsbj_out_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps, _swapClocks);
} else {
pio_i2s_out_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps);
pio_i2s_out_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps, _swapClocks);
}
} else {
pio_i2s_in_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps);
pio_i2s_in_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps, _swapClocks);
}
setFrequency(_freq);
if (_MCLKenabled) {
MCLKbegin();
}
if (_bps == 8) {
uint8_t a = _silenceSample & 0xff;
_silenceSample = (a << 24) | (a << 16) | (a << 8) | a;
@@ -150,10 +244,17 @@ bool I2S::begin() {
_silenceSample = (a << 16) | a;
}
if (!_bufferWords) {
_bufferWords = 16 * (_bps == 32 ? 2 : 1);
_bufferWords = 64 * (_bps == 32 ? 2 : 1);
}
_arb = new AudioBufferManager(_buffers, _bufferWords, _silenceSample, _isOutput ? OUTPUT : INPUT);
_arb->begin(pio_get_dreq(_pio, _sm, _isOutput), _isOutput ? &_pio->txf[_sm] : (volatile void*)&_pio->rxf[_sm]);
if (!_arb->begin(pio_get_dreq(_pio, _sm, _isOutput), _isOutput ? &_pio->txf[_sm] : (volatile void*)&_pio->rxf[_sm])) {
_running = false;
delete _arb;
_arb = nullptr;
delete _i2s;
_i2s = nullptr;
return false;
}
_arb->setCallback(_cb);
pio_sm_set_enabled(_pio, _sm, true);
@@ -162,6 +263,11 @@ bool I2S::begin() {
void I2S::end() {
if (_running) {
if (_MCLKenabled) {
pio_sm_set_enabled(_pioMCLK, _smMCLK, false);
delete _i2sMCLK;
_i2sMCLK = nullptr;
}
pio_sm_set_enabled(_pio, _sm, false);
_running = false;
delete _arb;
@@ -174,10 +280,13 @@ void I2S::end() {
int I2S::available() {
if (!_running) {
return 0;
} else if (_isOutput) {
return availableForWrite(); // Do what I mean, not what I say
} else {
return _arb->available();
auto avail = _arb->available();
avail *= 4; // 4 samples per 32-bits
if (_bps < 24 && !_isOutput) {
avail += _isHolding / 8;
}
return avail;
}
}
@@ -206,7 +315,7 @@ int I2S::read() {
case 16:
ret = _holdWord >> 16;
_holdWord <<= 16;
_isHolding -= 32;
_isHolding -= 16;
return ret;
case 24:
case 32:
@@ -363,13 +472,14 @@ bool I2S::read32(int32_t *l, int32_t *r) {
size_t I2S::write(const uint8_t *buffer, size_t size) {
// We can only write 32-bit chunks here
if (size & 0x3) {
if (size & 0x3 || !_running || !_isOutput) {
return 0;
}
size_t writtenSize = 0;
int32_t *p = (int32_t *)buffer;
uint32_t *p = (uint32_t *)buffer;
while (size) {
if (!write((int32_t)*p)) {
if (!_arb->write(*p, false)) {
// Blocked, stop write here
return writtenSize;
} else {
@@ -385,5 +495,5 @@ int I2S::availableForWrite() {
if (!_running || !_isOutput) {
return 0;
}
return _arb->available();
return available();
}
+28 -2
View File
@@ -30,10 +30,16 @@ public:
bool setBCLK(pin_size_t pin);
bool setDATA(pin_size_t pin);
bool setMCLK(pin_size_t pin);
bool setBitsPerSample(int bps);
bool setBuffers(size_t buffers, size_t bufferWords, int32_t silenceSample = 0);
bool setFrequency(int newFreq);
bool setLSBJFormat();
bool setTDMFormat();
bool setTDMChannels(int channels);
bool swapClocks();
bool setMCLKmult(int mult);
bool setSysClk(int samplerate);
bool begin(long sampleRate) {
setFrequency(sampleRate);
@@ -53,6 +59,15 @@ public:
virtual size_t write(const uint8_t *buffer, size_t size) override;
virtual int availableForWrite() override;
// From the AR
bool getOverUnderflow() {
if (!_running) {
return false;
} else {
return _arb->getOverUnderflow();
}
}
// 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 {
@@ -100,13 +115,19 @@ public:
private:
pin_size_t _pinBCLK;
pin_size_t _pinDOUT;
pin_size_t _pinMCLK;
int _bps;
int _freq;
int _multMCLK;
size_t _buffers;
size_t _bufferWords;
int32_t _silenceSample;
bool _isLSBJ;
bool _isTDM;
int _tdmChannels;
bool _isOutput;
bool _swapClocks;
bool _MCLKenabled;
bool _running;
@@ -121,9 +142,14 @@ private:
int _isHolding = 0;
void (*_cb)();
void MCLKbegin();
AudioBufferManager *_arb;
PIOProgram *_i2s;
PIO _pio;
int _sm;
PIOProgram *_i2sMCLK;
PIO _pio, _pioMCLK;
int _sm, _smMCLK;
static const int I2SSYSCLK_44_1 = 135600; // 44.1, 88.2 kHz sample rates
static const int I2SSYSCLK_8 = 147600; // 8k, 16, 32, 48, 96, 192 kHz
};
+150 -7
View File
@@ -19,7 +19,16 @@
; 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_mclk
; PIO SM clock is MCLK x 2
; MCLK/BCLK sync is maintained by ensuring IN/OUT PIO SM clock dividers
; are x128, 384, 256, 512 or 768 multiples of MCLK SM divider as required.
loop_mclk:
set pins, 1 ; toggle mclk
set pins, 0
; Loop back to beginning...
.program pio_i2s_out
.side_set 2 ; 0 = bclk, 1=wclk
@@ -41,6 +50,60 @@ right:
out pins, 1 side 0b00 ; Last bit of right also has WCLK change
; Loop back to beginning...
.program pio_i2s_out_swap
.side_set 2 ; 0 = wclk, 1=bclk
; The C code should place (number of bits/sample - 2) in Y and
; also update the SHIFTCTRL to be 24 or 32 as appropriate
; +----- BCLK
; |+---- WCLK
mov x, y side 0b10
left:
out pins, 1 side 0b00
jmp x--, left side 0b10
out pins, 1 side 0b01 ; Last bit of left has WCLK change per I2S spec
mov x, y side 0b11
right:
out pins, 1 side 0b01
jmp x--, right side 0b11
out pins, 1 side 0b00 ; Last bit of right also has WCLK change
; Loop back to beginning...
.program pio_tdm_out
.side_set 2 ; 0 = bclk, 1 = wclk
; The C code should place (number of bits - 1) in Y and update SHIFTCTRL
; to be 32 (as per the TDM specs)
; +----- WCLK
; |+---- BCLK
mov x, y side 0b11
bitloop:
out pins, 1 side 0b00
jmp x-- bitloop side 0b01
lastbit:
out pins, 1 side 0b10
; Loop back to the beginning
.program pio_tdm_out_swap
.side_set 2 ; 0 = wclk, 1 = bclk
; The C code should place (number of bits - 1) in Y and update SHIFTCTRL
; to be 32 (as per the TDM specs)
; +----- BCLK
; |+---- WCLK
mov x, y side 0b11
bitloop:
out pins, 1 side 0b00
jmp x-- bitloop side 0b10
lastbit:
out pins, 1 side 0b01
; Loop back to the beginning
.program pio_lsbj_out
.side_set 2 ; 0 = bclk, 1=wclk
@@ -64,6 +127,28 @@ right:
; Loop back to beginning...
.program pio_lsbj_out_swap
.side_set 2 ; 0 = wclk, 1=bclk
; The C code should place (number of bits/sample - 2) in Y and
; also update the SHIFTCTRL to be 24 or 32 as appropriate
; +----- BCLK
; |+---- WCLK
mov x, y side 0b10
left:
out pins, 1 side 0b01
jmp x--, left side 0b11
out pins, 1 side 0b01
mov x, y side 0b11
right:
out pins, 1 side 0b00
jmp x--, right side 0b10
out pins, 1 side 0b00
; 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
@@ -86,16 +171,47 @@ right:
in pins, 1 side 0b01 ; Last bit of right also has WCLK change
; Loop back to beginning...
.program pio_i2s_in_swap ; Note this is the same as _out, just "in" and not "out"
.side_set 2 ; 0 = wclk, 1=bclk
; The C code should place (number of bits/sample - 2) in Y and
; also update the SHIFTCTRL to be 24 or 32 as appropriate
; +----- BCLK
; |+---- WCLK
mov x, y side 0b00
left:
in pins, 1 side 0b10
jmp x--, left side 0b00
in pins, 1 side 0b11 ; Last bit of left has WCLK change per I2S spec
mov x, y side 0b01
right:
in pins, 1 side 0b11
jmp x--, right side 0b01
in pins, 1 side 0b10 ; 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) {
static inline void pio_i2s_MCLK_program_init(PIO pio, uint sm, uint offset, uint MCLK_pin) {
pio_gpio_init(pio, MCLK_pin);
pio_sm_set_consecutive_pindirs(pio, sm, MCLK_pin, 1, true);
pio_sm_config sm_config = pio_i2s_mclk_program_get_default_config(offset);
sm_config_set_set_pins(&sm_config, MCLK_pin, 1);
pio_sm_init(pio, sm, offset, &sm_config);
}
static inline void pio_i2s_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits, bool swap) {
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);
pio_sm_config sm_config = swap ? pio_i2s_out_swap_program_get_default_config(offset) : 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);
@@ -111,12 +227,39 @@ static inline void pio_i2s_out_program_init(PIO pio, uint sm, uint offset, uint
pio_sm_exec(pio, sm, pio_encode_set(pio_y, bits - 2));
}
static inline void pio_lsbj_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits) {
static inline void pio_tdm_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits, bool swap, uint channels) {
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_lsbj_out_program_get_default_config(offset);
pio_sm_config sm_config = swap ? pio_tdm_out_swap_program_get_default_config(offset) : pio_tdm_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, 32);
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
// Can't set constant > 31, so push and pop/mov
pio_sm_put_blocking(pio, sm, bits * channels - 2);
pio_sm_exec(pio, sm, pio_encode_pull(false, false));
pio_sm_exec(pio, sm, pio_encode_mov(pio_y, pio_osr));
// Need to make OSR believe there's nothing left to shift out, or the 1st word will be the count we just passed in, not a sample
pio_sm_exec(pio, sm, pio_encode_out(pio_osr, 32));
}
static inline void pio_lsbj_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits, bool swap) {
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 = swap ? pio_lsbj_out_swap_program_get_default_config(offset) : pio_lsbj_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);
@@ -132,12 +275,12 @@ static inline void pio_lsbj_out_program_init(PIO pio, uint sm, uint offset, uint
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) {
static inline void pio_i2s_in_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits, bool swap) {
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);
pio_sm_config sm_config = swap ? pio_i2s_in_swap_program_get_default_config(offset) : 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);
+229 -6
View File
@@ -8,6 +8,34 @@
#include "hardware/pio.h"
#endif
// ------------ //
// pio_i2s_mclk //
// ------------ //
#define pio_i2s_mclk_wrap_target 0
#define pio_i2s_mclk_wrap 1
static const uint16_t pio_i2s_mclk_program_instructions[] = {
// .wrap_target
0xe001, // 0: set pins, 1
0xe000, // 1: set pins, 0
// .wrap
};
#if !PICO_NO_HARDWARE
static const struct pio_program pio_i2s_mclk_program = {
.instructions = pio_i2s_mclk_program_instructions,
.length = 2,
.origin = -1,
};
static inline pio_sm_config pio_i2s_mclk_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + pio_i2s_mclk_wrap_target, offset + pio_i2s_mclk_wrap);
return c;
}
#endif
// ----------- //
// pio_i2s_out //
// ----------- //
@@ -43,6 +71,103 @@ static inline pio_sm_config pio_i2s_out_program_get_default_config(uint offset)
}
#endif
// ---------------- //
// pio_i2s_out_swap //
// ---------------- //
#define pio_i2s_out_swap_wrap_target 0
#define pio_i2s_out_swap_wrap 7
static const uint16_t pio_i2s_out_swap_program_instructions[] = {
// .wrap_target
0xb022, // 0: mov x, y side 2
0x6001, // 1: out pins, 1 side 0
0x1041, // 2: jmp x--, 1 side 2
0x6801, // 3: out pins, 1 side 1
0xb822, // 4: mov x, y side 3
0x6801, // 5: out pins, 1 side 1
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_swap_program = {
.instructions = pio_i2s_out_swap_program_instructions,
.length = 8,
.origin = -1,
};
static inline pio_sm_config pio_i2s_out_swap_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + pio_i2s_out_swap_wrap_target, offset + pio_i2s_out_swap_wrap);
sm_config_set_sideset(&c, 2, false, false);
return c;
}
#endif
// ----------- //
// pio_tdm_out //
// ----------- //
#define pio_tdm_out_wrap_target 0
#define pio_tdm_out_wrap 3
static const uint16_t pio_tdm_out_program_instructions[] = {
// .wrap_target
0xb822, // 0: mov x, y side 3
0x6001, // 1: out pins, 1 side 0
0x0841, // 2: jmp x--, 1 side 1
0x7001, // 3: out pins, 1 side 2
// .wrap
};
#if !PICO_NO_HARDWARE
static const struct pio_program pio_tdm_out_program = {
.instructions = pio_tdm_out_program_instructions,
.length = 4,
.origin = -1,
};
static inline pio_sm_config pio_tdm_out_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + pio_tdm_out_wrap_target, offset + pio_tdm_out_wrap);
sm_config_set_sideset(&c, 2, false, false);
return c;
}
#endif
// ---------------- //
// pio_tdm_out_swap //
// ---------------- //
#define pio_tdm_out_swap_wrap_target 0
#define pio_tdm_out_swap_wrap 3
static const uint16_t pio_tdm_out_swap_program_instructions[] = {
// .wrap_target
0xb822, // 0: mov x, y side 3
0x6001, // 1: out pins, 1 side 0
0x1041, // 2: jmp x--, 1 side 2
0x6801, // 3: out pins, 1 side 1
// .wrap
};
#if !PICO_NO_HARDWARE
static const struct pio_program pio_tdm_out_swap_program = {
.instructions = pio_tdm_out_swap_program_instructions,
.length = 4,
.origin = -1,
};
static inline pio_sm_config pio_tdm_out_swap_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + pio_tdm_out_swap_wrap_target, offset + pio_tdm_out_swap_wrap);
sm_config_set_sideset(&c, 2, false, false);
return c;
}
#endif
// ------------ //
// pio_lsbj_out //
// ------------ //
@@ -78,6 +203,41 @@ static inline pio_sm_config pio_lsbj_out_program_get_default_config(uint offset)
}
#endif
// ----------------- //
// pio_lsbj_out_swap //
// ----------------- //
#define pio_lsbj_out_swap_wrap_target 0
#define pio_lsbj_out_swap_wrap 7
static const uint16_t pio_lsbj_out_swap_program_instructions[] = {
// .wrap_target
0xb022, // 0: mov x, y side 2
0x6801, // 1: out pins, 1 side 1
0x1841, // 2: jmp x--, 1 side 3
0x6801, // 3: out pins, 1 side 1
0xb822, // 4: mov x, y side 3
0x6001, // 5: out pins, 1 side 0
0x1045, // 6: jmp x--, 5 side 2
0x6001, // 7: out pins, 1 side 0
// .wrap
};
#if !PICO_NO_HARDWARE
static const struct pio_program pio_lsbj_out_swap_program = {
.instructions = pio_lsbj_out_swap_program_instructions,
.length = 8,
.origin = -1,
};
static inline pio_sm_config pio_lsbj_out_swap_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + pio_lsbj_out_swap_wrap_target, offset + pio_lsbj_out_swap_wrap);
sm_config_set_sideset(&c, 2, false, false);
return c;
}
#endif
// ---------- //
// pio_i2s_in //
// ---------- //
@@ -111,12 +271,54 @@ static inline pio_sm_config pio_i2s_in_program_get_default_config(uint offset) {
sm_config_set_sideset(&c, 2, false, false);
return c;
}
#endif
static inline void pio_i2s_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits) {
// --------------- //
// pio_i2s_in_swap //
// --------------- //
#define pio_i2s_in_swap_wrap_target 0
#define pio_i2s_in_swap_wrap 7
static const uint16_t pio_i2s_in_swap_program_instructions[] = {
// .wrap_target
0xa022, // 0: mov x, y side 0
0x5001, // 1: in pins, 1 side 2
0x0041, // 2: jmp x--, 1 side 0
0x5801, // 3: in pins, 1 side 3
0xa822, // 4: mov x, y side 1
0x5801, // 5: in pins, 1 side 3
0x0845, // 6: jmp x--, 5 side 1
0x5001, // 7: in pins, 1 side 2
// .wrap
};
#if !PICO_NO_HARDWARE
static const struct pio_program pio_i2s_in_swap_program = {
.instructions = pio_i2s_in_swap_program_instructions,
.length = 8,
.origin = -1,
};
static inline pio_sm_config pio_i2s_in_swap_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + pio_i2s_in_swap_wrap_target, offset + pio_i2s_in_swap_wrap);
sm_config_set_sideset(&c, 2, false, false);
return c;
}
static inline void pio_i2s_MCLK_program_init(PIO pio, uint sm, uint offset, uint MCLK_pin) {
pio_gpio_init(pio, MCLK_pin);
pio_sm_set_consecutive_pindirs(pio, sm, MCLK_pin, 1, true);
pio_sm_config sm_config = pio_i2s_mclk_program_get_default_config(offset);
sm_config_set_set_pins(&sm_config, MCLK_pin, 1);
pio_sm_init(pio, sm, offset, &sm_config);
}
static inline void pio_i2s_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits, bool swap) {
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);
pio_sm_config sm_config = swap ? pio_i2s_out_swap_program_get_default_config(offset) : 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);
@@ -127,11 +329,31 @@ static inline void pio_i2s_out_program_init(PIO pio, uint sm, uint offset, uint
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_lsbj_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits) {
static inline void pio_tdm_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits, bool swap, uint channels) {
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_lsbj_out_program_get_default_config(offset);
pio_sm_config sm_config = swap ? pio_tdm_out_swap_program_get_default_config(offset) : pio_tdm_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, 32);
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
// Can't set constant > 31, so push and pop/mov
pio_sm_put_blocking(pio, sm, bits * channels - 2);
pio_sm_exec(pio, sm, pio_encode_pull(false, false));
pio_sm_exec(pio, sm, pio_encode_mov(pio_y, pio_osr));
// Need to make OSR believe there's nothing left to shift out, or the 1st word will be the count we just passed in, not a sample
pio_sm_exec(pio, sm, pio_encode_out(pio_osr, 32));
}
static inline void pio_lsbj_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits, bool swap) {
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 = swap ? pio_lsbj_out_swap_program_get_default_config(offset) : pio_lsbj_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);
@@ -142,11 +364,11 @@ static inline void pio_lsbj_out_program_init(PIO pio, uint sm, uint offset, uint
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) {
static inline void pio_i2s_in_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits, bool swap) {
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);
pio_sm_config sm_config = swap ? pio_i2s_in_swap_program_get_default_config(offset) : 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);
@@ -159,3 +381,4 @@ static inline void pio_i2s_in_program_init(PIO pio, uint sm, uint offset, uint d
}
#endif
+1 -1
View File
@@ -40,7 +40,7 @@ Joystick_::Joystick_(void) {
void Joystick_::send_now(void) {
CoreMutex m(&__usb_mutex);
tud_task();
if (tud_hid_ready()) {
if (__USBHIDReady()) {
tud_hid_n_report(0, __USBGetJoystickReportID(), &data, sizeof(data));
}
tud_task();
@@ -26,7 +26,19 @@ void setup() {
void loop() {
if (BOOTSEL) {
Serial.println("Typing password for you...shhhh....");
Serial.println("First, mute the computer to be extra quiet");
Keyboard.consumerPress(KEY_MUTE);
delay(100);
Keyboard.consumerRelease();
Keyboard.print("ThisPasswordIsWeakLikeABaby");
Serial.println("OK, unmute the computer since our secret is done");
Keyboard.consumerPress(KEY_MUTE);
delay(100);
Keyboard.consumerRelease();
while (BOOTSEL);
}
}
+11 -1
View File
@@ -40,12 +40,22 @@ Keyboard_::Keyboard_(void) {
void Keyboard_::sendReport(KeyReport* keys) {
CoreMutex m(&__usb_mutex);
tud_task();
if (tud_hid_ready()) {
if (__USBHIDReady()) {
tud_hid_keyboard_report(__USBGetKeyboardReportID(), keys->modifiers, keys->keys);
}
tud_task();
}
void Keyboard_::sendConsumerReport(uint16_t key) {
CoreMutex m(&__usb_mutex);
tud_task();
if (__USBHIDReady()) {
tud_hid_report(__USBGetKeyboardReportID() + 1, &key, sizeof(key));
}
tud_task();
}
extern "C" void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) {
(void) report_id;
(void) instance;
+1
View File
@@ -28,6 +28,7 @@
class Keyboard_ : public HID_Keyboard {
protected:
virtual void sendReport(KeyReport* keys) override;
virtual void sendConsumerReport(uint16_t key) override;
public:
Keyboard_(void);
@@ -63,4 +63,9 @@ void KeyboardBLE_::sendReport(KeyReport* keys) {
PicoBluetoothBLEHID.send(&data, sizeof(data));
}
void KeyboardBLE_::sendConsumerReport(uint16_t key) {
(void) key;
// TODO - Need some BLE-specific code to send 2nd report
}
KeyboardBLE_ KeyboardBLE;
+1
View File
@@ -28,6 +28,7 @@
class KeyboardBLE_ : public HID_Keyboard {
private:
virtual void sendReport(KeyReport* keys) override;
virtual void sendConsumerReport(uint16_t key) override;
public:
KeyboardBLE_(void);
void begin(const char *localName = nullptr, const char *hidName = nullptr, const uint8_t *layout = KeyboardLayout_en_US);
+5 -1
View File
@@ -34,7 +34,7 @@ KeyboardBT_::KeyboardBT_(void) {
#define REPORT_ID 0x01
static const uint8_t desc_keyboard[] = {TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID))};
static const uint8_t desc_keyboard[] = {TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID)), TUD_HID_REPORT_DESC_CONSUMER(HID_REPORT_ID(REPORT_ID + 1))};
static void _hidReportCB(uint16_t cid, hid_report_type_t report_type, uint16_t report_id, int report_size, uint8_t *report) {
(void) cid;
@@ -71,4 +71,8 @@ void KeyboardBT_::sendReport(KeyReport* keys) {
PicoBluetoothHID.send(REPORT_ID, &data, sizeof(data));
}
void KeyboardBT_::sendConsumerReport(uint16_t key) {
PicoBluetoothHID.send(REPORT_ID + 1, &key, sizeof(key));
}
KeyboardBT_ KeyboardBT;

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