Compare commits
+1
-1
Submodule ArduinoCore-API updated: d955d75747...e37df85425
@@ -12,6 +12,7 @@ See https://arduino-pico.readthedocs.io/en/latest/ along with the examples for m
|
||||
# Supported Boards
|
||||
* Raspberry Pi Pico
|
||||
* Raspberry Pi Pico W
|
||||
* 0xCB Helios
|
||||
* Adafruit Feather RP2040
|
||||
* Adafruit Feather RP2040 SCORPIO
|
||||
* Adafruit ItsyBitsy RP2040
|
||||
@@ -25,7 +26,6 @@ See https://arduino-pico.readthedocs.io/en/latest/ along with the examples for m
|
||||
* Cytron Maker Pi RP2040
|
||||
* Cytron Maker Nano RP2040
|
||||
* DatanoiseTV PicoADK+
|
||||
* Degz Mizu
|
||||
* DeRuiLab FlyBoard2040 Core
|
||||
* DFRobot Beetle RP2040
|
||||
* ElectronicCats Hunter Cat NFC
|
||||
@@ -40,12 +40,15 @@ See https://arduino-pico.readthedocs.io/en/latest/ along with the examples for m
|
||||
* Invector Labs RPICO32
|
||||
* Melopero Cookie RP2040
|
||||
* Melopero Shake RP2040
|
||||
* nullbits Bit-C PRO
|
||||
* Pimoroni PGA2040
|
||||
* Seeed XIAO RP2040
|
||||
* Solder Party RP2040 Stamp
|
||||
* SparkFun ProMicro RP2040
|
||||
* SparkFun Thing Plus RP2040
|
||||
* uPesy RP2040 DevKit
|
||||
* VCC-GND YD-RP2040
|
||||
* Viyalab Mizu RP2040
|
||||
* Waveshare RP2040 Zero
|
||||
* Waveshare RP2040 One
|
||||
* Waveshare RP2040 Plus
|
||||
@@ -167,6 +170,9 @@ The installed tools include a version of OpenOCD (in the pqt-openocd directory)
|
||||
* FreeRTOS SMP support
|
||||
* Overclocking and underclocking from the menus
|
||||
* digitalWrite/Read, shiftIn/Out, tone, analogWrite(PWM)/Read, temperature
|
||||
* Analog stereo audio in using DMA and the built-in ADC
|
||||
* Analog stereo audio out using PWM hardware
|
||||
* USB drive mode for data loggers (SingleFileDrive)
|
||||
* Peripherals: SPI master, Wire(I2C) master/slave, dual UART, emulated EEPROM, I2S audio input, I2S audio output, Servo
|
||||
* printf (i.e. debug) output over USB serial
|
||||
|
||||
|
||||
+2195
-18564
File diff suppressed because it is too large
Load Diff
@@ -68,10 +68,16 @@ void noInterrupts();
|
||||
#define portOutputRegister(port) ((volatile uint32_t*) sio_hw->gpio_out)
|
||||
#define portInputRegister(port) ((volatile uint32_t*) sio_hw->gpio_in)
|
||||
#define portModeRegister(port) ((volatile uint32_t*) sio_hw->gpio_oe)
|
||||
#define digitalWriteFast(pin, val) (val ? sio_hw->gpio_set = (1 << pin) : sio_hw->gpio_clr = (1 << pin))
|
||||
#define digitalReadFast(pin) ((1 << pin) & sio_hw->gpio_in)
|
||||
#define sei() interrupts()
|
||||
#define cli() noInterrupts()
|
||||
|
||||
// ADC RP2040-specific calls
|
||||
void analogReadResolution(int bits);
|
||||
float analogReadTemp(); // Returns core temp in Centigrade
|
||||
#ifdef __cplusplus
|
||||
float analogReadTemp(float vref = 3.3); // Returns core temp in Centigrade
|
||||
#endif
|
||||
|
||||
// PWM RP2040-specific calls
|
||||
void analogWriteFreq(uint32_t freq);
|
||||
|
||||
+6
-7
@@ -192,13 +192,12 @@ File File::openNextFile() {
|
||||
String File::readString() {
|
||||
String ret;
|
||||
ret.reserve(size() - position());
|
||||
char temp[256 + 1];
|
||||
int countRead = readBytes(temp, sizeof(temp) - 1);
|
||||
while (countRead > 0) {
|
||||
temp[countRead] = 0;
|
||||
ret += temp;
|
||||
countRead = readBytes(temp, sizeof(temp) - 1);
|
||||
}
|
||||
uint8_t temp[256];
|
||||
int countRead;
|
||||
do {
|
||||
countRead = read(temp, sizeof(temp));
|
||||
ret.concat(temp, countRead);
|
||||
} while (countRead > 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
+174
-4
@@ -33,6 +33,10 @@
|
||||
#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 <device/usbd_pvt.h>
|
||||
|
||||
// Big, global USB mutex, shared with all USB devices to make sure we don't
|
||||
// have multiple cores updating the TUSB state in parallel
|
||||
@@ -67,10 +71,18 @@ static int __usb_task_irq;
|
||||
#define USBD_STR_PRODUCT (0x02)
|
||||
#define USBD_STR_SERIAL (0x03)
|
||||
#define USBD_STR_CDC (0x04)
|
||||
|
||||
#define USBD_STR_RPI_RESET (0x05)
|
||||
|
||||
#define EPNUM_HID 0x83
|
||||
|
||||
#define USBD_MSC_EPOUT 0x03
|
||||
#define USBD_MSC_EPIN 0x84
|
||||
#define USBD_MSC_EPSIZE 64
|
||||
|
||||
#define TUD_RPI_RESET_DESCRIPTOR(_itfnum, _stridx) \
|
||||
/* Interface */\
|
||||
9, TUSB_DESC_INTERFACE, _itfnum, 0, 0, TUSB_CLASS_VENDOR_SPECIFIC, RESET_INTERFACE_SUBCLASS, RESET_INTERFACE_PROTOCOL, _stridx,
|
||||
|
||||
|
||||
const uint8_t *tud_descriptor_device_cb(void) {
|
||||
static tusb_desc_device_t usbd_desc_device = {
|
||||
@@ -89,7 +101,7 @@ const uint8_t *tud_descriptor_device_cb(void) {
|
||||
.iSerialNumber = USBD_STR_SERIAL,
|
||||
.bNumConfigurations = 1
|
||||
};
|
||||
if (__USBInstallSerial && !__USBInstallKeyboard && !__USBInstallMouse && !__USBInstallJoystick) {
|
||||
if (__USBInstallSerial && !__USBInstallKeyboard && !__USBInstallMouse && !__USBInstallJoystick && !__USBInstallMassStorage) {
|
||||
// Can use as-is, this is the default USB case
|
||||
return (const uint8_t *)&usbd_desc_device;
|
||||
}
|
||||
@@ -103,6 +115,9 @@ const uint8_t *tud_descriptor_device_cb(void) {
|
||||
if (__USBInstallJoystick) {
|
||||
usbd_desc_device.idProduct |= 0x0100;
|
||||
}
|
||||
if (__USBInstallMassStorage) {
|
||||
usbd_desc_device.idProduct ^= 0x2000;
|
||||
}
|
||||
// Set the device class to 0 to indicate multiple device classes
|
||||
usbd_desc_device.bDeviceClass = 0;
|
||||
usbd_desc_device.bDeviceSubClass = 0;
|
||||
@@ -223,7 +238,7 @@ void __SetupUSBDescriptor() {
|
||||
if (!usbd_desc_cfg) {
|
||||
bool hasHID = __USBInstallKeyboard || __USBInstallMouse || __USBInstallJoystick;
|
||||
|
||||
uint8_t interface_count = (__USBInstallSerial ? 2 : 0) + (hasHID ? 1 : 0);
|
||||
uint8_t interface_count = (__USBInstallSerial ? 2 : 0) + (hasHID ? 1 : 0) + (__USBInstallMassStorage ? 1 : 0);
|
||||
|
||||
uint8_t cdc_desc[TUD_CDC_DESC_LEN] = {
|
||||
// Interface number, string index, protocol, report descriptor len, EP In & Out address, size & polling interval
|
||||
@@ -238,7 +253,20 @@ void __SetupUSBDescriptor() {
|
||||
TUD_HID_DESCRIPTOR(hid_itf, 0, HID_ITF_PROTOCOL_NONE, hid_report_len, EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 10)
|
||||
};
|
||||
|
||||
int usbd_desc_len = TUD_CONFIG_DESC_LEN + (__USBInstallSerial ? sizeof(cdc_desc) : 0) + (hasHID ? sizeof(hid_desc) : 0);
|
||||
uint8_t msd_itf = interface_count - 1;
|
||||
uint8_t msd_desc[TUD_MSC_DESC_LEN] = {
|
||||
TUD_MSC_DESCRIPTOR(msd_itf, 0, USBD_MSC_EPOUT, USBD_MSC_EPIN, USBD_MSC_EPSIZE)
|
||||
};
|
||||
|
||||
int usbd_desc_len = TUD_CONFIG_DESC_LEN + (__USBInstallSerial ? sizeof(cdc_desc) : 0) + (hasHID ? sizeof(hid_desc) : 0) + (__USBInstallMassStorage ? sizeof(msd_desc) : 0);
|
||||
|
||||
#ifdef ENABLE_PICOTOOL_USB
|
||||
uint8_t picotool_itf = interface_count++;
|
||||
uint8_t picotool_desc[] = {
|
||||
TUD_RPI_RESET_DESCRIPTOR(picotool_itf, USBD_STR_RPI_RESET)
|
||||
};
|
||||
usbd_desc_len += sizeof(picotool_desc);
|
||||
#endif
|
||||
|
||||
uint8_t tud_cfg_desc[TUD_CONFIG_DESC_LEN] = {
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
@@ -260,6 +288,14 @@ void __SetupUSBDescriptor() {
|
||||
memcpy(ptr, hid_desc, sizeof(hid_desc));
|
||||
ptr += sizeof(hid_desc);
|
||||
}
|
||||
if (__USBInstallMassStorage) {
|
||||
memcpy(ptr, msd_desc, sizeof(msd_desc));
|
||||
ptr += sizeof(msd_desc);
|
||||
}
|
||||
#ifdef ENABLE_PICOTOOL_USB
|
||||
memcpy(ptr, picotool_desc, sizeof(picotool_desc));
|
||||
ptr += sizeof(picotool_desc);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -277,6 +313,9 @@ const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||
[USBD_STR_PRODUCT] = "PicoArduino",
|
||||
[USBD_STR_SERIAL] = idString,
|
||||
[USBD_STR_CDC] = "Board CDC",
|
||||
#ifdef ENABLE_PICOTOOL_USB
|
||||
[USBD_STR_RPI_RESET] = "Reset",
|
||||
#endif
|
||||
};
|
||||
|
||||
if (!idString[0]) {
|
||||
@@ -367,4 +406,135 @@ extern "C" void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_r
|
||||
(void) bufsize;
|
||||
}
|
||||
|
||||
extern "C" int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) __attribute__((weak));
|
||||
extern "C" int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) {
|
||||
(void) lun;
|
||||
(void) lba;
|
||||
(void) offset;
|
||||
(void) buffer;
|
||||
(void) bufsize;
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" bool tud_msc_test_unit_ready_cb(uint8_t lun) __attribute__((weak));
|
||||
extern "C" bool tud_msc_test_unit_ready_cb(uint8_t lun) {
|
||||
(void) lun;
|
||||
return false;
|
||||
}
|
||||
|
||||
extern "C" int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) __attribute__((weak));
|
||||
extern "C" int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) {
|
||||
(void) lun;
|
||||
(void) lba;
|
||||
(void) offset;
|
||||
(void) buffer;
|
||||
(void) bufsize;
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) __attribute__((weak));
|
||||
extern "C" int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) {
|
||||
(void) lun;
|
||||
(void) scsi_cmd;
|
||||
(void) buffer;
|
||||
(void) bufsize;
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size) __attribute__((weak));
|
||||
extern "C" void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size) {
|
||||
(void) lun;
|
||||
*block_count = 0;
|
||||
*block_size = 0;
|
||||
}
|
||||
|
||||
extern "C" void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) __attribute__((weak));
|
||||
extern "C" void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) {
|
||||
(void) lun;
|
||||
vendor_id[0] = 0;
|
||||
product_id[0] = 0;
|
||||
product_rev[0] = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef ENABLE_PICOTOOL_USB
|
||||
|
||||
static uint8_t _picotool_itf_num;
|
||||
|
||||
static void resetd_init() {
|
||||
}
|
||||
|
||||
static void resetd_reset(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
_picotool_itf_num = 0;
|
||||
}
|
||||
|
||||
static uint16_t resetd_open(uint8_t rhport,
|
||||
tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
|
||||
(void) rhport;
|
||||
TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass &&
|
||||
RESET_INTERFACE_SUBCLASS == itf_desc->bInterfaceSubClass &&
|
||||
RESET_INTERFACE_PROTOCOL == itf_desc->bInterfaceProtocol, 0);
|
||||
|
||||
uint16_t const drv_len = sizeof(tusb_desc_interface_t);
|
||||
TU_VERIFY(max_len >= drv_len, 0);
|
||||
|
||||
_picotool_itf_num = itf_desc->bInterfaceNumber;
|
||||
return drv_len;
|
||||
}
|
||||
|
||||
// Support for parameterized reset via vendor interface control request
|
||||
static bool resetd_control_xfer_cb(uint8_t rhport, uint8_t stage,
|
||||
tusb_control_request_t const *request) {
|
||||
(void) rhport;
|
||||
// nothing to do with DATA & ACK stage
|
||||
if (stage != CONTROL_STAGE_SETUP) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (request->wIndex == _picotool_itf_num) {
|
||||
if (request->bRequest == RESET_REQUEST_BOOTSEL) {
|
||||
reset_usb_boot(0, (request->wValue & 0x7f));
|
||||
// does not return, otherwise we'd return true
|
||||
}
|
||||
|
||||
if (request->bRequest == RESET_REQUEST_FLASH) {
|
||||
watchdog_reboot(0, 0, 100);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool resetd_xfer_cb(uint8_t rhport, uint8_t ep_addr,
|
||||
xfer_result_t result, uint32_t xferred_bytes) {
|
||||
(void) rhport;
|
||||
(void) ep_addr;
|
||||
(void) result;
|
||||
(void) xferred_bytes;
|
||||
return true;
|
||||
}
|
||||
|
||||
static usbd_class_driver_t const _resetd_driver = {
|
||||
#if CFG_TUSB_DEBUG >= 2
|
||||
.name = "RESET",
|
||||
#endif
|
||||
.init = resetd_init,
|
||||
.reset = resetd_reset,
|
||||
.open = resetd_open,
|
||||
.control_xfer_cb = resetd_control_xfer_cb,
|
||||
.xfer_cb = resetd_xfer_cb,
|
||||
.sof = NULL
|
||||
};
|
||||
|
||||
// Implement callback to add our custom driver
|
||||
usbd_class_driver_t const *usbd_app_driver_get_cb(uint8_t *driver_count) {
|
||||
*driver_count = 1;
|
||||
return &_resetd_driver;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -26,6 +26,7 @@ extern void __USBInstallSerial() __attribute__((weak));
|
||||
extern void __USBInstallKeyboard() __attribute__((weak));
|
||||
extern void __USBInstallJoystick() __attribute__((weak));
|
||||
extern void __USBInstallMouse() __attribute__((weak));
|
||||
extern void __USBInstallMassStorage() __attribute__((weak));
|
||||
|
||||
// Big, global USB mutex, shared with all USB devices to make sure we don't
|
||||
// have multiple cores updating the TUSB state in parallel
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#define ARDUINO_PICO_MAJOR 2
|
||||
#define ARDUINO_PICO_MINOR 6
|
||||
#define ARDUINO_PICO_REVISION 4
|
||||
#define ARDUINO_PICO_VERSION_STR "2.6.4"
|
||||
#define ARDUINO_PICO_MINOR 7
|
||||
#define ARDUINO_PICO_REVISION 1
|
||||
#define ARDUINO_PICO_VERSION_STR "2.7.1"
|
||||
|
||||
@@ -390,7 +390,12 @@ void __not_in_flash_func(SerialUART::_handleIRQ)(bool inIRQ) {
|
||||
// ICR is write-to-clear
|
||||
uart_get_hw(_uart)->icr = UART_UARTICR_RTIC_BITS | UART_UARTICR_RXIC_BITS;
|
||||
while (uart_is_readable(_uart)) {
|
||||
auto val = uart_getc(_uart);
|
||||
uint32_t raw = uart_get_hw(_uart)->dr;
|
||||
if (raw & 0x700) {
|
||||
// Framing, Parity, or Break. Ignore this bad char
|
||||
continue;
|
||||
}
|
||||
uint8_t val = raw & 0xff;
|
||||
auto next_writer = _writer + 1;
|
||||
if (next_writer == _fifoSize) {
|
||||
next_writer = 0;
|
||||
|
||||
@@ -109,7 +109,7 @@ void tone(uint8_t pin, unsigned int frequency, unsigned long duration) {
|
||||
if (ret > 0) {
|
||||
newTone->alarm = ret;
|
||||
} else {
|
||||
DEBUGCORE("ERROR: Unable to allocate timer for tone(%d, %d, %d)\n",
|
||||
DEBUGCORE("ERROR: Unable to allocate timer for tone(%d, %d, %lu)\n",
|
||||
pin, frequency, duration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
#include "api/String.h"
|
||||
@@ -26,6 +26,8 @@
|
||||
#include <hardware/pll.h>
|
||||
#include <hardware/adc.h>
|
||||
|
||||
void __clearADCPin(pin_size_t p);
|
||||
|
||||
static uint32_t analogScale = 255;
|
||||
static uint32_t analogFreq = 1000;
|
||||
static uint32_t pwmInitted = 0;
|
||||
@@ -41,10 +43,10 @@ extern "C" void analogWriteFreq(uint32_t freq) {
|
||||
return;
|
||||
}
|
||||
if (freq < 100) {
|
||||
DEBUGCORE("ERROR: analogWriteFreq too low (%d)\n", freq);
|
||||
DEBUGCORE("ERROR: analogWriteFreq too low (%lu)\n", freq);
|
||||
analogFreq = 100;
|
||||
} else if (freq > 10'000'000) {
|
||||
DEBUGCORE("ERROR: analogWriteFreq too high (%d)\n", freq);
|
||||
DEBUGCORE("ERROR: analogWriteFreq too high (%lu)\n", freq);
|
||||
analogFreq = 10'000'000;
|
||||
} else {
|
||||
analogFreq = freq;
|
||||
@@ -62,7 +64,7 @@ extern "C" void analogWriteRange(uint32_t range) {
|
||||
pwmInitted = 0;
|
||||
scaleInitted = false;
|
||||
} else {
|
||||
DEBUGCORE("ERROR: analogWriteRange out of range (%d)\n", range);
|
||||
DEBUGCORE("ERROR: analogWriteRange out of range (%lu)\n", range);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,20 +83,21 @@ extern "C" void analogWrite(pin_size_t pin, int val) {
|
||||
DEBUGCORE("ERROR: Illegal analogWrite pin (%d)\n", pin);
|
||||
return;
|
||||
}
|
||||
__clearADCPin(pin);
|
||||
if (!scaleInitted) {
|
||||
// For low frequencies, we need to scale the output max value up to achieve lower periods
|
||||
analogWritePseudoScale = 1;
|
||||
while (((clock_get_hz(clk_sys) / ((float)analogScale * analogFreq)) > 255.0) && (analogScale < 32678)) {
|
||||
analogWritePseudoScale++;
|
||||
analogScale *= 2;
|
||||
DEBUGCORE("Adjusting analogWrite values PS=%d, scale=%d\n", analogWritePseudoScale, analogScale);
|
||||
DEBUGCORE("Adjusting analogWrite values PS=%d, scale=%lu\n", analogWritePseudoScale, analogScale);
|
||||
}
|
||||
// For high frequencies, we need to scale the output max value down to actually hit the frequency target
|
||||
analogWriteSlowScale = 1;
|
||||
while (((clock_get_hz(clk_sys) / ((float)analogScale * analogFreq)) < 1.0) && (analogScale >= 6)) {
|
||||
analogWriteSlowScale++;
|
||||
analogScale /= 2;
|
||||
DEBUGCORE("Adjusting analogWrite values SS=%d, scale=%d\n", analogWriteSlowScale, analogScale);
|
||||
DEBUGCORE("Adjusting analogWrite values SS=%d, scale=%lu\n", analogWriteSlowScale, analogScale);
|
||||
}
|
||||
scaleInitted = true;
|
||||
}
|
||||
@@ -120,7 +123,13 @@ extern "C" void analogWrite(pin_size_t pin, int val) {
|
||||
}
|
||||
|
||||
auto_init_mutex(_adcMutex);
|
||||
static int _readBits = 10;
|
||||
static uint8_t _readBits = 10;
|
||||
static uint8_t _lastADCMux = 0;
|
||||
static uint32_t _adcGPIOInit = 0;
|
||||
|
||||
void __clearADCPin(pin_size_t p) {
|
||||
_adcGPIOInit &= ~(1 << p);
|
||||
}
|
||||
|
||||
extern "C" int analogRead(pin_size_t pin) {
|
||||
CoreMutex m(&_adcMutex);
|
||||
@@ -136,12 +145,18 @@ extern "C" int analogRead(pin_size_t pin) {
|
||||
adc_init();
|
||||
adcInitted = true;
|
||||
}
|
||||
adc_gpio_init(pin);
|
||||
adc_select_input(pin - minPin);
|
||||
if (!(_adcGPIOInit & (1 << pin))) {
|
||||
adc_gpio_init(pin);
|
||||
_adcGPIOInit |= 1 << pin;
|
||||
}
|
||||
if (_lastADCMux != pin) {
|
||||
adc_select_input(pin - minPin);
|
||||
_lastADCMux = pin;
|
||||
}
|
||||
return (_readBits < 12) ? adc_read() >> (12 - _readBits) : adc_read() << (_readBits - 12);
|
||||
}
|
||||
|
||||
extern "C" float analogReadTemp() {
|
||||
extern "C" float analogReadTemp(float vref) {
|
||||
CoreMutex m(&_adcMutex);
|
||||
|
||||
if (!m) {
|
||||
@@ -151,12 +166,13 @@ extern "C" float analogReadTemp() {
|
||||
adc_init();
|
||||
adcInitted = true;
|
||||
}
|
||||
_lastADCMux = 0;
|
||||
adc_set_temp_sensor_enabled(true);
|
||||
delay(1); // Allow things to settle. Without this, readings can be erratic
|
||||
adc_select_input(4); // Temperature sensor
|
||||
int v = adc_read();
|
||||
adc_set_temp_sensor_enabled(false);
|
||||
float t = 27.0f - ((v * 3.3f / 4096.0f) - 0.706f) / 0.001721f; // From the datasheet
|
||||
float t = 27.0f - ((v * vref / 4096.0f) - 0.706f) / 0.001721f; // From the datasheet
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "Arduino.h"
|
||||
#include <hardware/gpio.h>
|
||||
|
||||
extern void __clearADCPin(pin_size_t p);
|
||||
|
||||
static PinMode _pm[30];
|
||||
|
||||
extern "C" void pinMode(pin_size_t ulPin, PinMode ulMode) __attribute__((weak, alias("__pinMode")));
|
||||
@@ -75,6 +77,10 @@ extern "C" void __pinMode(pin_size_t ulPin, PinMode ulMode) {
|
||||
return;
|
||||
}
|
||||
_pm[ulPin] = ulMode;
|
||||
|
||||
if ((ulPin >= std::min(A0, A3)) && (ulPin <= std::max(A0, A3))) {
|
||||
__clearADCPin(ulPin);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void digitalWrite(pin_size_t ulPin, PinStatus ulVal) __attribute__((weak, alias("__digitalWrite")));
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
ADC Input Library
|
||||
=================
|
||||
|
||||
The ADC pins can be sampled and recorded by an application using the same
|
||||
interface as the I2S or PWM Audio libraries. This allows analog devices which
|
||||
need to be periodically sampled to be read by applications, easily, such as:
|
||||
|
||||
* Analog electret microphones
|
||||
|
||||
* Potentiometers
|
||||
|
||||
* Light dependent resistors (LDR), etc.
|
||||
|
||||
|
||||
Up to 4 analog samples can be recorded by the hardware (``A0`` ... ``A4``), and all
|
||||
recording is done at 16-bit levels (but be aware that the ADC in the Pico will only
|
||||
ever return values between 0...4095).
|
||||
|
||||
The interface for the ``ADCInput`` device is very similar to the ``I2S`` input
|
||||
device, and most code can be ported simply by instantiating a ``ADCInput``
|
||||
object in lieu of an ``I2S`` input object and choosing the pins to record.
|
||||
|
||||
Since this uses the ADC hardware, no ``analogRead`` or ``analogReadTemp`` calls are
|
||||
allowed while in use.
|
||||
|
||||
ADC Input API
|
||||
-------------
|
||||
|
||||
ADCInput(pin0 [, pin1, pin2, pin3])
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Creates an ADC input object which will record the pins specified in the code.
|
||||
Only pins ``A0`` ... ``A4`` can be used, and they must be specified in increasing
|
||||
order (i.e. ``ADCInput(A0, A1);`` is valid, but ``ADCInput(A1, A0)`` is not.
|
||||
|
||||
bool setBuffers(size_t buffers, size_t bufferWords)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Set the number of DMA buffers and their size in 32-bit words. Call before
|
||||
``ADCInput::begin()``.
|
||||
|
||||
When running at high sample rates, it is recommended to increase the
|
||||
``bufferWords`` to 32 or higher (i.e. ``adcinput.setBuffers(4, 32);`` ).
|
||||
|
||||
bool setPins(pin_size_t pin [, pin1, pin2, pin3])
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Adjusts the pin to record. Only legal before ``ADCInput::begin()``.
|
||||
|
||||
bool setFrequency(long sampleRate)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Sets the ADC sampling frequency, but does not start recording (however if the
|
||||
device was already running, it will wontinue to run at the new frequency). Note
|
||||
that every pin requested will be sampled at this frequency, one after the other.
|
||||
That is, if you have code like this:
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
ADCInput adc(A0, A1);
|
||||
adc.setFrequency(1000);
|
||||
|
||||
``A0`` will be sampled at 0ms, 1ms, 2ms, etc. and ``A1`` will be sampled at 0.5ms
|
||||
1.5ms, 2.5ms, etc. Each input is sampled at the proper frequency but offset in time
|
||||
since there is only one active ADC at a time.
|
||||
|
||||
bool begin()/begin(long sampleRate)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Start the ADC input up with the given sample rate, or with the value set
|
||||
using the prior ``setFrequency`` call.
|
||||
|
||||
void end()
|
||||
~~~~~~~~~~
|
||||
Stops the ADC Input device.
|
||||
|
||||
int read()
|
||||
~~~~~~~~~~
|
||||
Reads a single sample of recorded ADC data, as a 16-bit value. When multiple pins are
|
||||
recorded the first read will be pin 0, the second will be pin 1, etc. Applications need
|
||||
to keep track of which pin is being returned (normally by always reading out all pins
|
||||
at once). Will not return until data is available.
|
||||
|
||||
int available()
|
||||
~~~~~~~~~~~~~~~
|
||||
Returns the number of samples that can be read without potentially blocking.
|
||||
|
||||
void onReceive(void (\*fn)(void))
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Sets a callback to be called when a ADC input DMA buffer is fully filled.
|
||||
Will be in an interrupt context so the specified function must operate
|
||||
quickly and not use blocking calls like delay().
|
||||
+2
-1
@@ -18,9 +18,10 @@ void analogReadResolution(int bits)
|
||||
Determines the resolution (in bits) of the value returned by the analogRead() function.
|
||||
Default resolution is 10bit.
|
||||
|
||||
float analogReadTemp()
|
||||
float analogReadTemp(float vref = 3.3f)
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns the temperature, in Celsius, of the onboard thermal sensor.
|
||||
If you have a custom Vref for the ADC on your RP2040 board, you can pass it in as a parameter. Calling with no parameters assumes the normal, 3.3V Vref.
|
||||
This reading is not exceedingly accurate and of relatively low
|
||||
resolution, so it is not a replacement for an external temperature
|
||||
sensor in many cases.
|
||||
|
||||
+2
-2
@@ -54,9 +54,9 @@ author = u'Earle F. Philhower, III'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = u'2.6.4'
|
||||
version = u'2.7.1'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = u'2.6.4'
|
||||
release = u'2.7.1'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
@@ -58,6 +58,12 @@ 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 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 begin()/begin(long sampleRate)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Start the I2S device up with the given sample rate, or with the value set
|
||||
|
||||
@@ -30,6 +30,8 @@ For the latest version, always check https://github.com/earlephilhower/arduino-p
|
||||
Digital I/O <digital>
|
||||
EEPROM <eeprom>
|
||||
I2S Audio <i2s>
|
||||
PWM Audio <pwm>
|
||||
Microphone (and Analog Sensor) Input <adc>
|
||||
Serial USB and UARTs <serial>
|
||||
"Software Serial" PIO UART <piouart>
|
||||
Servo <servo>
|
||||
@@ -39,6 +41,8 @@ For the latest version, always check https://github.com/earlephilhower/arduino-p
|
||||
USB (Arduino and Adafruit_TinyUSB) <usb>
|
||||
Multicore Processing <multicore>
|
||||
|
||||
Single File USB Drive <singlefile>
|
||||
|
||||
FreeRTOS SMP (multicore) <freertos>
|
||||
|
||||
WiFi (Pico-W Support) <wifi>
|
||||
|
||||
+34
-8
@@ -9,14 +9,6 @@ installation.
|
||||
|
||||
Installing via Arduino Boards Manager
|
||||
-------------------------------------
|
||||
**Note for Windows Users**: Please do not use the Windows Store version of
|
||||
the actual Arduino application because it has issues detecting attached Pico
|
||||
boards. Use the "Windows ZIP" or plain "Windows" executable (EXE) download
|
||||
direct from https://arduino.cc. and allow it to install any device drivers
|
||||
it suggests. Otherwise the Pico board may not be detected. Also, if trying
|
||||
out the 2.0 beta Arduino please install the release 1.8 version beforehand
|
||||
to ensure needed device drivers are present.
|
||||
|
||||
1. Open up the Arduino IDE and go to File->Preferences.
|
||||
2. In the dialog that pops up, enter the following URL in the "Additional Boards Manager URLs" field: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
|
||||
|
||||
@@ -28,6 +20,24 @@ to ensure needed device drivers are present.
|
||||
|
||||
.. image:: images/install2.png
|
||||
|
||||
Arduino IDE Installation Warning
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
**Note for Windows Users**: Please do not use the Windows Store version of
|
||||
the actual Arduino application because it has issues detecting attached Pico
|
||||
boards. Use the "Windows ZIP" or plain "Windows" executable (EXE) download
|
||||
direct from https://arduino.cc. and allow it to install any device drivers
|
||||
it suggests. Otherwise the Pico board may not be detected. Also, if trying
|
||||
out the 2.0 beta Arduino please install the release 1.8 version beforehand
|
||||
to ensure needed device drivers are present.
|
||||
|
||||
**Note for Linux Users**: If you installed the Arduino IDE using Flatpak, which
|
||||
is common in Pop!_OS, Fedora, and Mint, among others, you may need to configure
|
||||
Flatpak to allow the IDE access to files outside your home folder. The RP2040
|
||||
device is sometimes mounted as a folder in /opt or /media, which Flatpak will
|
||||
prevent the Arduino IDE from accessing. For Arduino IDE V2, override the filesystem
|
||||
restriction using ``flatpak override --user --filesystem=host cc.arduino.IDE2`` . For
|
||||
For Arduino IDE < V2, use ``flatpak override --user --filesystem=host cc.arduino.arduinoide``.
|
||||
|
||||
Installing via GIT
|
||||
------------------
|
||||
To install via GIT (for latest and greatest versions):
|
||||
@@ -125,6 +135,22 @@ For detailed usage information, please check the repo documentation available at
|
||||
|
||||
* https://arduino-pico.readthedocs.io/en/latest/fs.html
|
||||
|
||||
Uploading Sketches with Picotool
|
||||
--------------------------------
|
||||
Because the Picotool uses a custom device driver in the Pico to handle upload, when using the ``Upload Method->Picotool`` mode custom code needs to be run on the Pico which is not included by default for compatibility and code savings.
|
||||
|
||||
So for the first sketch you will need to rebuild (with the ``Upload Method->Picotool`` selected in them menus) and then manually hold down BOOTSEL and insert the Pico USB cable to enter the ROM bootloader.
|
||||
|
||||
After the initial upload, as long as the running binary was built using the ``Picotool`` upload method, then the normal upload process should work.
|
||||
|
||||
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:
|
||||
|
||||
.. code::
|
||||
|
||||
echo 'SUBSYSTEM=="usb", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="0003", MODE="660", GROUP="plugdev"' | sudo tee -a /etc/udev/rules.d/98-Picotool.rules
|
||||
echo 'SUBSYSTEM=="usb", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="000a", MODE="660", GROUP="plugdev"' | sudo tee -a /etc/udev/rules.d/98-Picotool.rules
|
||||
sudo udevadm control --reload
|
||||
|
||||
Uploading Sketches with Picoprobe
|
||||
---------------------------------
|
||||
If you have built a Raspberry Pi Picoprobe, you can use OpenOCD to handle your sketch uploads and for debugging with GDB.
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
PWM Audio Library
|
||||
=================
|
||||
|
||||
Relatively good quality analog audio out can be generated by using the
|
||||
RP2040 onboard PWM hardware. It can drive an amplifier for speaker output,
|
||||
or be decoupled using a capacitor to drive a headphone-level signal. Mono
|
||||
and stereo signals can be generated.
|
||||
|
||||
All samples are sent to the ``PWMAudio`` library as **signed 16 bits per sample**.
|
||||
Due to frequency limitations of the PWM hardware, at higher bit rates
|
||||
these 16-bits will automatically be reduced to the maximum the hardware
|
||||
can handle.
|
||||
|
||||
Multiple ``PWMAudio`` devices are supported, depending on availability of
|
||||
DMA channels.
|
||||
|
||||
The interface for the ``PWMAudio`` device is very similar to the ``I2S``
|
||||
device, and most code can be ported simply by instantiating a ``PWMAudio``
|
||||
object in lieu of an ``I2S`` object.
|
||||
|
||||
PWM Class API
|
||||
-------------
|
||||
|
||||
PWMAudio(pin)
|
||||
~~~~~~~~~~~~~
|
||||
Creates a mono PWM output port. Any pin can be used, but no ``analogWrite``
|
||||
calls are allowed to any other pins using that pin's PWM slice hardware.
|
||||
See the RP2040 datasheet for more details about PWM slices.
|
||||
|
||||
PWMAudio(pin, true)
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
Creates a stereo PWM output port. Only even pins (left signal) can be used, the next odd
|
||||
pin will automatically be assigned to the right channel (i.e. ``PWMAudio pwm(0, true);``
|
||||
will make GP0 as the left channel, GP1 as the right channel). The same restriction as
|
||||
in mono mode applies.
|
||||
|
||||
bool setBuffers(size_t buffers, size_t bufferWords)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Set the number of DMA buffers and their size in 32-bit words.
|
||||
Call before ``PWMAudio::begin()``.
|
||||
|
||||
When running at high sample rates, it is recommended to increase the
|
||||
``bufferWords`` to 32 or higher (i.e. ``pwm.setBuffers(4, 32);`` ).
|
||||
|
||||
bool setPin(pin_size_t pin)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Adjusts the pin to connect to the PWM audio output. Only legal before
|
||||
``PWMAudio::begin()``.
|
||||
|
||||
bool setStereo(bool stereo)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Adjusts the mono/stereo setting of the PWM audio output. Only legal before
|
||||
``PWMAudio::begin()``.
|
||||
|
||||
bool setFrequency(long sampleRate)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Sets the sample frequency, but does not start the PWM device (however if the
|
||||
device was already running, it will wontinue to run at the new frequency).
|
||||
|
||||
bool begin()/begin(long sampleRate)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Start the PWM Audio device up with the given sample rate, or with the value set
|
||||
using the prior ``setFrequency`` call.
|
||||
|
||||
void end()
|
||||
~~~~~~~~~~
|
||||
Stops the PWMAudio device.
|
||||
|
||||
void flush()
|
||||
~~~~~~~~~~~~
|
||||
Waits until all the PWM Audio buffers have been output.
|
||||
|
||||
size_t write(int16_t sample, bool sync = true)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Writes a single 16-bit sample to the buffer. It is up to the user to keep track
|
||||
of left/right channels when in stereo mode. In mono mode, one sample is written
|
||||
per timestep while in stereo mode two ``write()`` calls are required.
|
||||
|
||||
This call will block (wait) until space is available to actually write
|
||||
the data if ``sync`` is not specified or set to ``true``.
|
||||
|
||||
size_t write(const uint8_t \*buffer, size_t size)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Transfers number of bytes from an application buffer to the PWM Audio output buffer.
|
||||
Be aware that ``size`` is in *bytes** and not samples. Size must be a multiple
|
||||
of **4 bytes**. Will not block, so check the return value to find out how
|
||||
many bytes were actually written.
|
||||
|
||||
int availableForWrite()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Returns the number of samples that can be written without potentially blocking.
|
||||
|
||||
void onTransmit(void (\*fn)(void))
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Sets a callback to be called when a PWM Audio DMA buffer is fully transmitted.
|
||||
Will be in an interrupt context so the specified function must operate
|
||||
quickly and not use blocking calls like delay() or write to the PWM Audio.
|
||||
@@ -0,0 +1,82 @@
|
||||
SingleFileDrive
|
||||
===============
|
||||
|
||||
USB drive mode is supported through the ``SingleFileDrive`` class which
|
||||
allows the Pico to emulate a FAT-formatted USB stick while preserving the
|
||||
onboard LittleFS filesystem. A single file can be exported this way without
|
||||
needing to use FAT as the onboard filesystem (FAT is not appropriate for
|
||||
flash-based devices without complicated wear leveling because of the update
|
||||
frequency of the FAT tables).
|
||||
|
||||
This emulation is very simple and only allows for the reading of the single
|
||||
file, and deleting it.
|
||||
|
||||
Callbacks, Interrupt Safety, and File Operations
|
||||
------------------------------------------------
|
||||
|
||||
The ``SingleFileDrive`` library allows your application to get a callback
|
||||
when a PC attempts to mount or unmount the Pico as a drive. Your app can
|
||||
also get a callback if the user attempts to delete the file (but your
|
||||
sketch does not actually need to delete the file, it's up to you).
|
||||
|
||||
Note that when the USB drive is mounted by a PC it is not safe for your
|
||||
main sketch to make changes to the LittleFS filesystem or the file being
|
||||
exported. So, normally, your ``onPlug`` callback will set a flag letting
|
||||
your application know not to touch the filesystem, with the ``onUnplug``
|
||||
callback clearing this flag.
|
||||
|
||||
Also, because the USB port can be connected at any time, it is important
|
||||
to disable interrupts using ``noInterrupts()`` before writing to a file
|
||||
you will be exporting (and restoring them with ``interrupts()`` afterwards).
|
||||
It is also important to ``close()`` the file after each update, or the
|
||||
on-flash version the ``SingleFileDrive`` will attempt to export may not be
|
||||
up to date causing issues later on.
|
||||
|
||||
See the included ``DataLoggerUSB`` sketch for an example of working with
|
||||
these limitations.
|
||||
|
||||
Using SingleFileDrive
|
||||
---------------------
|
||||
|
||||
Implementing the drive requires including the header file, starting LittleFS,
|
||||
defining your callbacks, and telling the library what file to export. No
|
||||
polling or other calls are required outside of your ``setup()``. (Note that
|
||||
the callback routines allow for a parameter to be passed to them, but in most
|
||||
cases this can be safely ignored.)
|
||||
|
||||
.. code:: cpp
|
||||
|
||||
#include <LittleFS.h>
|
||||
#include <SingleFileDrive.h>
|
||||
|
||||
void myPlugCB(uint32_t data) {
|
||||
// Tell my app not to write to flash, we're connected
|
||||
}
|
||||
|
||||
void myUnplugCB(uint32_t data) {
|
||||
// I can start writing to flash again
|
||||
}
|
||||
|
||||
void myDeleteDB(uint32_t data) {
|
||||
// Maybe LittleFS.remove("myfile.txt")? or do nothing
|
||||
}
|
||||
|
||||
void setup() {
|
||||
LittleFS.begin();
|
||||
singleFileDrive.onPlug(myPlugCB);
|
||||
singleFileDrive.onUnplug(myUnplugCB);
|
||||
singleFileDrive.onDelete(myDeleteCB);
|
||||
singleFileDrive.begin("littlefsfile.csv", "Data Recorder.csv");
|
||||
// ... rest of setup ...
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Take some measurements, delay, etc.
|
||||
if (okay-to-write) {
|
||||
noInterrupts();
|
||||
File f = LittleFS.open("littlefsfile.csv", "a");
|
||||
f.printf("%d,%d,%d\n", data1, data2, data3);
|
||||
f.close();
|
||||
interrupts();
|
||||
}
|
||||
}
|
||||
@@ -72,15 +72,14 @@
|
||||
//------------- CLASS -------------//
|
||||
#define CFG_TUD_HID (2)
|
||||
#define CFG_TUD_CDC (1)
|
||||
#define CFG_TUD_MSC (0)
|
||||
#define CFG_TUD_MSC (1)
|
||||
#define CFG_TUD_MIDI (0)
|
||||
#define CFG_TUD_VENDOR (0)
|
||||
|
||||
#define CFG_TUD_CDC_RX_BUFSIZE (256)
|
||||
#define CFG_TUD_CDC_TX_BUFSIZE (256)
|
||||
|
||||
#define CFG_TUD_MIDI_RX_BUFSIZE (64)
|
||||
#define CFG_TUD_MIDI_TX_BUFSIZE (64)
|
||||
#define CFG_TUD_MSC_EP_BUFSIZE (64)
|
||||
|
||||
// HID buffer size Should be sufficient to hold ID (if any) + Data
|
||||
#define CFG_TUD_HID_EP_BUFSIZE (64)
|
||||
|
||||
@@ -54,6 +54,9 @@ SerialPIO KEYWORD2
|
||||
setFIFOSize KEYWORD2
|
||||
setPollingMode KEYWORD2
|
||||
|
||||
digitalWriteFast KEYWORD2
|
||||
digitalReadFast KEYWORD2
|
||||
|
||||
enableDoubleResetBootloader KEYWORD2
|
||||
|
||||
OUTPUT_2MA LITERAL1
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -9,6 +9,7 @@
|
||||
-iwithprefixbefore/pico-sdk/src/common/pico_stdlib/include
|
||||
-iwithprefixbefore/pico-sdk/src/common/pico_sync/include
|
||||
-iwithprefixbefore/pico-sdk/src/common/pico_time/include
|
||||
-iwithprefixbefore/pico-sdk/src/common/pico_usb_reset_interface/include
|
||||
-iwithprefixbefore/pico-sdk/src/common/pico_util/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2040/hardware_regs/include
|
||||
-iwithprefixbefore/pico-sdk/src/rp2040/hardware_structs/include
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Mono analog microphone example using electret mike on A0
|
||||
Run using the Arduino Serial Plotter to see waveform.
|
||||
Released to the Public Domain by Earle F. Philhower, III
|
||||
|
||||
Wire the mike's VCC to 3.3V on the Pico, connect the mike's
|
||||
GND to a convenient Pico GND, and then connect mike OUT to A0
|
||||
*/
|
||||
|
||||
#include <ADCInput.h>
|
||||
|
||||
ADCInput mike(A0);
|
||||
// For stereo/dual mikes, could use this line instead
|
||||
// ADCInput(A0, A1);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
mike.begin(8000);
|
||||
|
||||
while (1) {
|
||||
Serial.printf("%d\n", mike.read());
|
||||
// For stereo/dual mikes, use this line instead
|
||||
// Serial.printf("%d %d\n", mike.read(), mike.read());
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
/* Nothing here */
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
ADCInput KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
begin KEYWORD2
|
||||
end KEYWORD2
|
||||
|
||||
setPins KEYWORD2
|
||||
setFrequency KEYWORD2
|
||||
setBuffers KEYWORD2
|
||||
|
||||
onReceive KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
@@ -0,0 +1,10 @@
|
||||
name=ADCInput
|
||||
version=1.0
|
||||
author=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
sentence=Records ADC values (i.e. microphone, sensors) and presents an I2S-like callback/immediate read interface
|
||||
paragraph=Records ADC values (i.e. microphone, sensors) and presents an I2S-like callback/immediate read interface
|
||||
category=Communication
|
||||
url=http://github.com/earlephilhower/arduino-pico
|
||||
architectures=rp2040
|
||||
dot_a_linkage=true
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
ADCInput
|
||||
Records ADC values (i.e. microphone, sensors) and presents an I2S-like
|
||||
callback/immediate read interface
|
||||
|
||||
Copyright (c) 2023 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "ADCInput.h"
|
||||
#include <hardware/adc.h>
|
||||
|
||||
ADCInput::ADCInput(pin_size_t p0, pin_size_t p1, pin_size_t p2, pin_size_t p3) {
|
||||
_running = false;
|
||||
setPins(p0, p1, p2, p3);
|
||||
_freq = 48000;
|
||||
_arb = nullptr;
|
||||
_cb = nullptr;
|
||||
_buffers = 8;
|
||||
_bufferWords = 0;
|
||||
}
|
||||
|
||||
ADCInput::~ADCInput() {
|
||||
end();
|
||||
}
|
||||
|
||||
bool ADCInput::setBuffers(size_t buffers, size_t bufferWords) {
|
||||
if (_running || (buffers < 3) || (bufferWords < 8)) {
|
||||
return false;
|
||||
}
|
||||
_buffers = buffers;
|
||||
_bufferWords = bufferWords;
|
||||
return true;
|
||||
}
|
||||
|
||||
int ADCInput::_mask(pin_size_t p) {
|
||||
switch (p) {
|
||||
case 26: return 1;
|
||||
case 27: return 2;
|
||||
case 28: return 4;
|
||||
case 29: return 8;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool ADCInput::setPins(pin_size_t pin0, pin_size_t pin1, pin_size_t pin2, pin_size_t pin3) {
|
||||
if (_running) {
|
||||
return false;
|
||||
}
|
||||
_pinMask = _mask(pin0) | _mask(pin1) | _mask(pin2) | _mask(pin3);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ADCInput::setFrequency(int newFreq) {
|
||||
_freq = newFreq * __builtin_popcount(_pinMask); // Want to sample all channels at given frequency
|
||||
adc_set_clkdiv(48000000.0f / _freq - 1.0f);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ADCInput::onReceive(void(*fn)(void)) {
|
||||
_cb = fn;
|
||||
if (_running) {
|
||||
_arb->setCallback(_cb);
|
||||
}
|
||||
}
|
||||
|
||||
bool ADCInput::begin() {
|
||||
_running = true;
|
||||
|
||||
_isHolding = 0;
|
||||
|
||||
if (!_bufferWords) {
|
||||
_bufferWords = 16;
|
||||
}
|
||||
|
||||
// Set up the GPIOs to go to ADC
|
||||
adc_init();
|
||||
int cnt = 0;
|
||||
for (int mask = 1, pin = 26; pin <= 29; mask <<= 1, pin++) {
|
||||
if (_pinMask & mask) {
|
||||
if (!cnt) {
|
||||
adc_select_input(pin - 26);
|
||||
}
|
||||
cnt++;
|
||||
adc_gpio_init(pin);
|
||||
}
|
||||
}
|
||||
adc_set_round_robin(_pinMask);
|
||||
adc_fifo_setup(true, true, 1, false, false);
|
||||
|
||||
setFrequency(_freq);
|
||||
|
||||
_arb = new AudioBufferManager(_buffers, _bufferWords, 0, INPUT, DMA_SIZE_16);
|
||||
_arb->begin(DREQ_ADC, (volatile void*)&adc_hw->fifo);
|
||||
_arb->setCallback(_cb);
|
||||
|
||||
adc_fifo_drain();
|
||||
|
||||
adc_run(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ADCInput::end() {
|
||||
if (_running) {
|
||||
_running = false;
|
||||
delete _arb;
|
||||
_arb = nullptr;
|
||||
}
|
||||
adc_run(false);
|
||||
adc_fifo_drain();
|
||||
}
|
||||
|
||||
int ADCInput::available() {
|
||||
if (!_running) {
|
||||
return 0;
|
||||
} else {
|
||||
return _arb->available();
|
||||
}
|
||||
}
|
||||
|
||||
int ADCInput::read() {
|
||||
if (!_running) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (_hasPeeked) {
|
||||
_hasPeeked = false;
|
||||
return _peekSaved;
|
||||
}
|
||||
|
||||
if (_isHolding <= 0) {
|
||||
_arb->read(&_holdWord, true);
|
||||
_isHolding = 32;
|
||||
}
|
||||
|
||||
int ret = _holdWord & 0x0fff;
|
||||
_holdWord >>= 16;
|
||||
_isHolding -= 16;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ADCInput::peek() {
|
||||
if (!_running) {
|
||||
return -1;
|
||||
}
|
||||
if (!_hasPeeked) {
|
||||
_peekSaved = read();
|
||||
_hasPeeked = true;
|
||||
}
|
||||
return _peekSaved;
|
||||
}
|
||||
|
||||
void ADCInput::flush() {
|
||||
if (_running) {
|
||||
_arb->flush();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
ADCInput
|
||||
Records ADC values (i.e. microphone, sensors) and presents an I2S-like
|
||||
callback/immediate read interface
|
||||
|
||||
Copyright (c) 2023 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
#include "AudioBufferManager.h"
|
||||
|
||||
class ADCInput : public Stream {
|
||||
public:
|
||||
ADCInput(pin_size_t pin0, pin_size_t pin1 = 255, pin_size_t pin2 = 255, pin_size_t pin3 = 255);
|
||||
virtual ~ADCInput();
|
||||
|
||||
bool setBuffers(size_t buffers, size_t bufferWords);
|
||||
bool setFrequency(int newFreq);
|
||||
bool setPins(pin_size_t pin0, pin_size_t pin1 = 255, pin_size_t pin2 = 255, pin_size_t pin3 = 255);
|
||||
|
||||
bool begin(long sampleRate) {
|
||||
setFrequency(sampleRate);
|
||||
return begin();
|
||||
}
|
||||
|
||||
bool begin();
|
||||
void end();
|
||||
|
||||
// from Stream
|
||||
virtual int available() override;
|
||||
virtual int read() override;
|
||||
virtual int peek() override;
|
||||
virtual void flush() override;
|
||||
|
||||
// from Print, not supported
|
||||
virtual size_t write(const uint8_t *buffer, size_t size) override {
|
||||
(void) buffer;
|
||||
(void) size;
|
||||
return -1;
|
||||
}
|
||||
virtual size_t write(uint8_t x) override {
|
||||
(void) x;
|
||||
return -1;
|
||||
}
|
||||
virtual int availableForWrite() override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Note that these callback are called from **INTERRUPT CONTEXT** and hence
|
||||
// should be in RAM, not FLASH, and should be quick to execute.
|
||||
void onReceive(void(*)(void));
|
||||
|
||||
private:
|
||||
uint32_t _pinMask;
|
||||
|
||||
int _freq;
|
||||
|
||||
size_t _buffers;
|
||||
size_t _bufferWords;
|
||||
|
||||
bool _running;
|
||||
void (*_cb)();
|
||||
|
||||
bool _hasPeeked;
|
||||
uint32_t _peekSaved;
|
||||
uint32_t _holdWord = 0;
|
||||
int _isHolding = 0;
|
||||
|
||||
int _mask(pin_size_t pin);
|
||||
|
||||
AudioBufferManager *_arb;
|
||||
};
|
||||
Submodule libraries/Adafruit_TinyUSB_Arduino updated: 5be0b21c0a...e1e922d2ca
@@ -0,0 +1,10 @@
|
||||
name=AudioBufferManager
|
||||
version=1.0.0
|
||||
author=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
sentence=Manages DMA buffers for audio output
|
||||
paragraph=Manages DMA buffers for audio output
|
||||
category=Device Control
|
||||
url=https://github.com/earlephilhower/arduino-pico
|
||||
architectures=rp2040
|
||||
dot_a_linkage=true
|
||||
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
AudioBufferManager for Raspnerry Pi Pico RP2040
|
||||
Implements a DMA controlled linked-list series of buffers
|
||||
|
||||
Copyright (c) 2022 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "hardware/dma.h"
|
||||
#include "hardware/irq.h"
|
||||
#include "AudioBufferManager.h"
|
||||
|
||||
static int __channelCount = 0; // # of channels left. When we hit 0, then remove our handler
|
||||
static AudioBufferManager* __channelMap[12]; // Lets the IRQ handler figure out where to dispatch to
|
||||
|
||||
AudioBufferManager::AudioBufferManager(size_t bufferCount, size_t bufferWords, int32_t silenceSample, PinMode direction, enum dma_channel_transfer_size dmaSize) {
|
||||
_running = false;
|
||||
|
||||
// Need at least 2 DMA buffers and 1 user or this isn't going to work at all
|
||||
if (bufferCount < 3) {
|
||||
bufferCount = 3;
|
||||
}
|
||||
|
||||
_bufferCount = bufferCount;
|
||||
_wordsPerBuffer = bufferWords;
|
||||
_isOutput = direction == OUTPUT;
|
||||
_dmaSize = dmaSize;
|
||||
_overunderflow = false;
|
||||
_callback = nullptr;
|
||||
_userOff = 0;
|
||||
|
||||
// Create the silence buffer, fill with appropriate value
|
||||
_silence = new AudioBuffer;
|
||||
_silence->next = nullptr;
|
||||
_silence->buff = new uint32_t[_wordsPerBuffer];
|
||||
for (uint32_t x = 0; x < _wordsPerBuffer; x++) {
|
||||
_silence->buff[x] = silenceSample;
|
||||
}
|
||||
|
||||
// No filled buffers yet
|
||||
_filled = nullptr;
|
||||
|
||||
// Create all buffers on the empty chain
|
||||
_empty = nullptr;
|
||||
for (size_t i = 0; i < bufferCount; i++) {
|
||||
auto ab = new AudioBuffer;
|
||||
ab->buff = new uint32_t[_wordsPerBuffer];
|
||||
bzero(ab->buff, _wordsPerBuffer * 4);
|
||||
ab->next = nullptr;
|
||||
_addToList(&_empty, ab);
|
||||
}
|
||||
|
||||
_active[0] = _silence;
|
||||
_active[1] = _silence;
|
||||
}
|
||||
|
||||
AudioBufferManager::~AudioBufferManager() {
|
||||
if (_running) {
|
||||
_running = false;
|
||||
for (auto i = 0; i < 2; i++) {
|
||||
dma_channel_set_irq0_enabled(_channelDMA[i], false);
|
||||
__channelMap[_channelDMA[i]] = nullptr;
|
||||
dma_channel_abort(_channelDMA[i]);
|
||||
dma_channel_unclaim(_channelDMA[i]);
|
||||
}
|
||||
__channelCount--;
|
||||
if (!__channelCount) {
|
||||
irq_set_enabled(DMA_IRQ_0, false);
|
||||
// TODO - how can we know if there are no other parts of the core using DMA0 IRQ??
|
||||
irq_remove_handler(DMA_IRQ_0, _irq);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (_active[i] != _silence) {
|
||||
_deleteAudioBuffer(_active[i]);
|
||||
}
|
||||
}
|
||||
while (_filled) {
|
||||
auto x = _filled->next;
|
||||
_deleteAudioBuffer(_filled);
|
||||
_filled = x;
|
||||
}
|
||||
while (_empty) {
|
||||
auto x = _empty->next;
|
||||
_deleteAudioBuffer(_empty);
|
||||
_empty = x;
|
||||
}
|
||||
_deleteAudioBuffer(_silence);
|
||||
}
|
||||
|
||||
void AudioBufferManager::setCallback(void (*fn)()) {
|
||||
_callback = fn;
|
||||
}
|
||||
|
||||
bool AudioBufferManager::begin(int dreq, volatile void *pioFIFOAddr) {
|
||||
_running = true;
|
||||
|
||||
// Get ping and pong DMA channels
|
||||
for (auto i = 0; i < 2; i++) {
|
||||
_channelDMA[i] = dma_claim_unused_channel(true);
|
||||
if (_channelDMA[i] == -1) {
|
||||
if (i == 1) {
|
||||
dma_channel_unclaim(_channelDMA[0]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool needSetIRQ = __channelCount == 0;
|
||||
// Need to know both channels to set up ping-pong, so do in 2 stages
|
||||
for (auto i = 0; i < 2; i++) {
|
||||
dma_channel_config c = dma_channel_get_default_config(_channelDMA[i]);
|
||||
channel_config_set_transfer_data_size(&c, _dmaSize); // 16b/32b transfers into PIO FIFO
|
||||
if (_isOutput) {
|
||||
channel_config_set_read_increment(&c, true); // Reading incrementing addresses
|
||||
channel_config_set_write_increment(&c, false); // Writing to the same FIFO address
|
||||
} else {
|
||||
channel_config_set_read_increment(&c, false); // Reading same FIFO address
|
||||
channel_config_set_write_increment(&c, true); // Writing to incrememting buffers
|
||||
}
|
||||
channel_config_set_dreq(&c, dreq); // Wait for the PIO TX FIFO specified
|
||||
channel_config_set_chain_to(&c, _channelDMA[i ^ 1]); // Start other channel when done
|
||||
channel_config_set_irq_quiet(&c, false); // Need IRQs
|
||||
|
||||
if (_isOutput) {
|
||||
dma_channel_configure(_channelDMA[i], &c, pioFIFOAddr, _silence->buff, _wordsPerBuffer * (_dmaSize == DMA_SIZE_16 ? 2 : 1), false);
|
||||
} else {
|
||||
_active[i] = _takeFromList(&_empty);
|
||||
dma_channel_configure(_channelDMA[i], &c, _active[i]->buff, pioFIFOAddr, _wordsPerBuffer * (_dmaSize == DMA_SIZE_16 ? 2 : 1), false);
|
||||
}
|
||||
dma_channel_set_irq0_enabled(_channelDMA[i], true);
|
||||
__channelMap[_channelDMA[i]] = this;
|
||||
__channelCount++;
|
||||
}
|
||||
if (needSetIRQ) {
|
||||
irq_add_shared_handler(DMA_IRQ_0, _irq, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
}
|
||||
|
||||
dma_channel_start(_channelDMA[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Following 2 routines use volatile because the IRQ may update the "this"
|
||||
// pointer and change the list head while we are waiting. Volatile will
|
||||
// cause GCC to keep re-reading from memory and not use cached value read
|
||||
// on the first pass.
|
||||
|
||||
bool AudioBufferManager::write(uint32_t v, bool sync) {
|
||||
if (!_running || !_isOutput) {
|
||||
return false;
|
||||
}
|
||||
AudioBuffer ** volatile p = (AudioBuffer ** volatile)&_empty;
|
||||
if (!*p) {
|
||||
if (!sync) {
|
||||
return false;
|
||||
} else {
|
||||
while (!*p) {
|
||||
/* noop busy wait */
|
||||
}
|
||||
}
|
||||
}
|
||||
(*p)->buff[_userOff++] = v;
|
||||
if (_userOff == _wordsPerBuffer) {
|
||||
_addToList(&_filled, _takeFromList(p));
|
||||
_userOff = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AudioBufferManager::read(uint32_t *v, bool sync) {
|
||||
if (!_running || _isOutput) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AudioBuffer ** volatile p = (AudioBuffer ** volatile)&_filled;
|
||||
if (!*p) {
|
||||
if (!sync) {
|
||||
return false;
|
||||
} else {
|
||||
while (!*p) {
|
||||
/* noop busy wait */
|
||||
}
|
||||
}
|
||||
}
|
||||
auto ret = (*p)->buff[_userOff++];
|
||||
if (_userOff == _wordsPerBuffer) {
|
||||
_addToList(&_empty, _takeFromList(p));
|
||||
_userOff = 0;
|
||||
}
|
||||
*v = ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AudioBufferManager::getOverUnderflow() {
|
||||
bool hold = _overunderflow;
|
||||
_overunderflow = false;
|
||||
return hold;
|
||||
}
|
||||
|
||||
int AudioBufferManager::available() {
|
||||
AudioBuffer *p = _isOutput ? _empty : _filled;
|
||||
|
||||
if (!_running || !p) {
|
||||
// No buffers available...
|
||||
return 0;
|
||||
}
|
||||
|
||||
int avail = _wordsPerBuffer - _userOff; // Currently available in this buffer
|
||||
|
||||
// Each add'l buffer has wpb spaces...
|
||||
auto x = p->next;
|
||||
while (x) {
|
||||
avail += _wordsPerBuffer;
|
||||
x = x->next;
|
||||
}
|
||||
return avail;
|
||||
}
|
||||
|
||||
void AudioBufferManager::flush() {
|
||||
AudioBuffer ** volatile a = (AudioBuffer ** volatile)&_active[0];
|
||||
AudioBuffer ** volatile b = (AudioBuffer ** volatile)&_active[1];
|
||||
AudioBuffer ** volatile c = (AudioBuffer ** volatile)&_filled;
|
||||
while (*c && (*b != (AudioBuffer * volatile)_silence) && (*a != (AudioBuffer * volatile)_silence)) {
|
||||
// busy wait until all user written data enroute
|
||||
}
|
||||
}
|
||||
|
||||
void __not_in_flash_func(AudioBufferManager::_dmaIRQ)(int channel) {
|
||||
if (!_running) {
|
||||
return;
|
||||
}
|
||||
if (_isOutput) {
|
||||
if (_active[0] != _silence) {
|
||||
_addToList(&_empty, _active[0]);
|
||||
}
|
||||
_active[0] = _active[1];
|
||||
if (!_filled) {
|
||||
_active[1] = _silence;
|
||||
} else {
|
||||
_active[1] = _takeFromList(&_filled);
|
||||
}
|
||||
_overunderflow = _overunderflow | (_active[1] == _silence);
|
||||
dma_channel_set_read_addr(channel, _active[0]->buff, false);
|
||||
} else {
|
||||
if (_empty) {
|
||||
_addToList(&_filled, _active[0]);
|
||||
_active[0] = _active[1];
|
||||
_active[1] = _takeFromList(&_empty);
|
||||
} else {
|
||||
_overunderflow = true;
|
||||
}
|
||||
dma_channel_set_write_addr(channel, _active[0]->buff, false);
|
||||
}
|
||||
dma_channel_set_trans_count(channel, _wordsPerBuffer * (_dmaSize == DMA_SIZE_16 ? 2 : 1), false);
|
||||
dma_channel_acknowledge_irq0(channel);
|
||||
if (_callback) {
|
||||
_callback();
|
||||
}
|
||||
}
|
||||
|
||||
void __not_in_flash_func(AudioBufferManager::_irq)() {
|
||||
for (size_t i = 0; i < sizeof(__channelMap); i++) {
|
||||
if (dma_channel_get_irq0_status(i) && __channelMap[i]) {
|
||||
__channelMap[i]->_dmaIRQ(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
AudioBufferManager for Rasperry Pi Pico
|
||||
Implements a DMA controlled linked-list series of buffers
|
||||
|
||||
Copyright (c) 2022 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
#include "hardware/dma.h"
|
||||
|
||||
class AudioBufferManager {
|
||||
public:
|
||||
AudioBufferManager(size_t bufferCount, size_t bufferWords, int32_t silenceSample, PinMode direction = OUTPUT, enum dma_channel_transfer_size dmaSize = DMA_SIZE_32);
|
||||
~AudioBufferManager();
|
||||
|
||||
void setCallback(void (*fn)());
|
||||
|
||||
bool begin(int dreq, volatile void *pioFIFOAddr);
|
||||
|
||||
bool write(uint32_t v, bool sync = true);
|
||||
bool read(uint32_t *v, bool sync = true);
|
||||
void flush();
|
||||
|
||||
bool getOverUnderflow();
|
||||
int available();
|
||||
|
||||
private:
|
||||
void _dmaIRQ(int channel);
|
||||
static void _irq();
|
||||
|
||||
typedef struct AudioBuffer {
|
||||
struct AudioBuffer *next;
|
||||
uint32_t *buff;
|
||||
} AudioBuffer;
|
||||
|
||||
bool _running = false;
|
||||
|
||||
AudioBuffer *_silence = nullptr; // A single silence buffer to be looped on underflow
|
||||
AudioBuffer *_filled = nullptr; // List of buffers ready to be played
|
||||
AudioBuffer *_empty = nullptr; // List of buffers waiting to be filled. *_empty = currently writing
|
||||
AudioBuffer *_active[2] = { nullptr, nullptr }; // The 2 buffers currently in use for DMA
|
||||
|
||||
// Can't use std::list because we need to put in RAM for IRQ use, so roll our own
|
||||
void __not_in_flash_func(_addToList)(AudioBuffer **list, AudioBuffer *element) {
|
||||
noInterrupts();
|
||||
// Find end of list, if any
|
||||
while ((*list) && ((*list)->next != nullptr)) {
|
||||
list = &(*list)->next;
|
||||
}
|
||||
if (*list) {
|
||||
(*list)->next = element;
|
||||
} else {
|
||||
*list = element;
|
||||
}
|
||||
element->next = nullptr; // Belt and braces
|
||||
interrupts();
|
||||
}
|
||||
|
||||
AudioBuffer *__not_in_flash_func(_takeFromList)(AudioBuffer **list) {
|
||||
noInterrupts();
|
||||
auto ret = *list;
|
||||
if (ret) {
|
||||
*list = ret->next;
|
||||
}
|
||||
interrupts();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void _deleteAudioBuffer(AudioBuffer *ab) {
|
||||
delete[] ab->buff;
|
||||
delete ab;
|
||||
}
|
||||
|
||||
int _bitsPerSample;
|
||||
size_t _wordsPerBuffer;
|
||||
size_t _bufferCount;
|
||||
enum dma_channel_transfer_size _dmaSize;
|
||||
bool _isOutput;
|
||||
|
||||
int _channelDMA[2];
|
||||
void (*_callback)();
|
||||
|
||||
bool _overunderflow;
|
||||
|
||||
// User buffer pointer
|
||||
size_t _userOff = 0;
|
||||
};
|
||||
@@ -30,7 +30,7 @@ void ps() {
|
||||
Serial.printf("# Tasks: %d\n", tasks);
|
||||
Serial.println("ID, NAME, STATE, PRIO, CYCLES");
|
||||
for (int i=0; i < tasks; i++) {
|
||||
Serial.printf("%d: %-16s %-10s %d %lu\n", i, pxTaskStatusArray[i].pcTaskName, eTaskStateName[pxTaskStatusArray[i].eCurrentState], pxTaskStatusArray[i].uxCurrentPriority, pxTaskStatusArray[i].ulRunTimeCounter);
|
||||
Serial.printf("%d: %-16s %-10s %d %lu\n", i, pxTaskStatusArray[i].pcTaskName, eTaskStateName[pxTaskStatusArray[i].eCurrentState], (int)pxTaskStatusArray[i].uxCurrentPriority, pxTaskStatusArray[i].ulRunTimeCounter);
|
||||
}
|
||||
delete[] pxTaskStatusArray;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ setBCLK KEYWORD2
|
||||
setDATA KEYWORD2
|
||||
setBitsPerSample KEYWORD2
|
||||
setFrequency KEYWORD2
|
||||
setBuffers KEYWORD2
|
||||
setLSBJFormat KEYWORD2
|
||||
|
||||
read8 KEYWORD2
|
||||
read16 KEYWORD2
|
||||
|
||||
@@ -1,256 +0,0 @@
|
||||
/*
|
||||
AudioRingBuffer for Raspnerry Pi Pico RP2040
|
||||
Implements a ring buffer for PIO DMA for I2S read or write
|
||||
|
||||
Copyright (c) 2022 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <vector>
|
||||
#include "hardware/dma.h"
|
||||
#include "hardware/irq.h"
|
||||
#include "hardware/pio.h"
|
||||
#include "pio_i2s.pio.h"
|
||||
#include "AudioRingBuffer.h"
|
||||
|
||||
static int __channelCount = 0; // # of channels left. When we hit 0, then remove our handler
|
||||
static AudioRingBuffer* __channelMap[12]; // Lets the IRQ handler figure out where to dispatch to
|
||||
|
||||
AudioRingBuffer::AudioRingBuffer(size_t bufferCount, size_t bufferWords, int32_t silenceSample, PinMode direction) {
|
||||
_running = false;
|
||||
_silenceSample = silenceSample;
|
||||
_bufferCount = bufferCount;
|
||||
_wordsPerBuffer = bufferWords;
|
||||
_isOutput = direction == OUTPUT;
|
||||
_overunderflow = false;
|
||||
_callback = nullptr;
|
||||
_userBuffer = -1;
|
||||
_userOff = 0;
|
||||
for (size_t i = 0; i < bufferCount; i++) {
|
||||
auto ab = new AudioBuffer;
|
||||
ab->buff = new uint32_t[_wordsPerBuffer];
|
||||
ab->empty = true;
|
||||
_buffers.push_back(ab);
|
||||
}
|
||||
}
|
||||
|
||||
AudioRingBuffer::~AudioRingBuffer() {
|
||||
if (_running) {
|
||||
for (auto i = 0; i < 2; i++) {
|
||||
dma_channel_set_irq0_enabled(_channelDMA[i], false);
|
||||
dma_channel_unclaim(_channelDMA[i]);
|
||||
__channelMap[_channelDMA[i]] = nullptr;
|
||||
}
|
||||
while (_buffers.size()) {
|
||||
auto ab = _buffers.back();
|
||||
_buffers.pop_back();
|
||||
delete[] ab->buff;
|
||||
delete ab;
|
||||
}
|
||||
__channelCount--;
|
||||
if (!__channelCount) {
|
||||
irq_set_enabled(DMA_IRQ_0, false);
|
||||
// TODO - how can we know if there are no other parts of the core using DMA0 IRQ??
|
||||
irq_remove_handler(DMA_IRQ_0, _irq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioRingBuffer::setCallback(void (*fn)()) {
|
||||
_callback = fn;
|
||||
}
|
||||
|
||||
bool AudioRingBuffer::begin(int dreq, volatile void *pioFIFOAddr) {
|
||||
_running = true;
|
||||
// Set all buffers to silence, empty
|
||||
for (auto buff : _buffers) {
|
||||
buff->empty = true;
|
||||
if (_isOutput) {
|
||||
for (uint32_t x = 0; x < _wordsPerBuffer; x++) {
|
||||
buff->buff[x] = _silenceSample;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Get ping and pong DMA channels
|
||||
for (auto i = 0; i < 2; i++) {
|
||||
_channelDMA[i] = dma_claim_unused_channel(true);
|
||||
if (_channelDMA[i] == -1) {
|
||||
if (i == 1) {
|
||||
dma_channel_unclaim(_channelDMA[0]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool needSetIRQ = __channelCount == 0;
|
||||
// Need to know both channels to set up ping-pong, so do in 2 stages
|
||||
for (auto i = 0; i < 2; i++) {
|
||||
dma_channel_config c = dma_channel_get_default_config(_channelDMA[i]);
|
||||
channel_config_set_transfer_data_size(&c, DMA_SIZE_32); // 32b transfers into PIO FIFO
|
||||
if (_isOutput) {
|
||||
channel_config_set_read_increment(&c, true); // Reading incrementing addresses
|
||||
channel_config_set_write_increment(&c, false); // Writing to the same FIFO address
|
||||
} else {
|
||||
channel_config_set_read_increment(&c, false); // Reading same FIFO address
|
||||
channel_config_set_write_increment(&c, true); // Writing to incrememting buffers
|
||||
}
|
||||
channel_config_set_dreq(&c, dreq); // Wait for the PIO TX FIFO specified
|
||||
channel_config_set_chain_to(&c, _channelDMA[i ^ 1]); // Start other channel when done
|
||||
channel_config_set_irq_quiet(&c, false); // Need IRQs
|
||||
|
||||
if (_isOutput) {
|
||||
dma_channel_configure(_channelDMA[i], &c, pioFIFOAddr, _buffers[i]->buff, _wordsPerBuffer, false);
|
||||
} else {
|
||||
dma_channel_configure(_channelDMA[i], &c, _buffers[i]->buff, pioFIFOAddr, _wordsPerBuffer, false);
|
||||
}
|
||||
dma_channel_set_irq0_enabled(_channelDMA[i], true);
|
||||
__channelMap[_channelDMA[i]] = this;
|
||||
__channelCount++;
|
||||
}
|
||||
if (needSetIRQ) {
|
||||
irq_add_shared_handler(DMA_IRQ_0, _irq, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
}
|
||||
_curBuffer = 0;
|
||||
_nextBuffer = 2 % _bufferCount;
|
||||
dma_channel_start(_channelDMA[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AudioRingBuffer::write(uint32_t v, bool sync) {
|
||||
if (!_running || !_isOutput) {
|
||||
return false;
|
||||
}
|
||||
if (_userBuffer == -1) {
|
||||
// First write or overflow, pick spot 2 buffers out
|
||||
_userBuffer = (_nextBuffer + 2) % _bufferCount;
|
||||
_userOff = 0;
|
||||
}
|
||||
if (!_buffers[_userBuffer]->empty) {
|
||||
if (!sync) {
|
||||
return false;
|
||||
} else {
|
||||
while (!_buffers[_userBuffer]->empty) {
|
||||
/* noop busy wait */
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_userBuffer == _curBuffer) {
|
||||
if (!sync) {
|
||||
return false;
|
||||
} else {
|
||||
while (_userBuffer == _curBuffer) {
|
||||
/* noop busy wait */
|
||||
}
|
||||
}
|
||||
}
|
||||
_buffers[_userBuffer]->buff[_userOff++] = v;
|
||||
if (_userOff == _wordsPerBuffer) {
|
||||
_buffers[_userBuffer]->empty = false;
|
||||
_userBuffer = (_userBuffer + 1) % _bufferCount;
|
||||
_userOff = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AudioRingBuffer::read(uint32_t *v, bool sync) {
|
||||
if (!_running || _isOutput) {
|
||||
return false;
|
||||
}
|
||||
if (_userBuffer == -1) {
|
||||
// First write or overflow, pick last filled buffer
|
||||
_userBuffer = (_curBuffer - 1 + _bufferCount) % _bufferCount;
|
||||
_userOff = 0;
|
||||
}
|
||||
if (_buffers[_userBuffer]->empty) {
|
||||
if (!sync) {
|
||||
return false;
|
||||
} else {
|
||||
while (_buffers[_userBuffer]->empty) {
|
||||
/* noop busy wait */
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_userBuffer == _curBuffer) {
|
||||
if (!sync) {
|
||||
return false;
|
||||
} else {
|
||||
while (_userBuffer == _curBuffer) {
|
||||
/* noop busy wait */
|
||||
}
|
||||
}
|
||||
}
|
||||
auto ret = _buffers[_userBuffer]->buff[_userOff++];
|
||||
if (_userOff == _wordsPerBuffer) {
|
||||
_buffers[_userBuffer]->empty = true;
|
||||
_userBuffer = (_userBuffer + 1) % _bufferCount;
|
||||
_userOff = 0;
|
||||
}
|
||||
*v = ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AudioRingBuffer::getOverUnderflow() {
|
||||
bool hold = _overunderflow;
|
||||
_overunderflow = false;
|
||||
return hold;
|
||||
}
|
||||
|
||||
int AudioRingBuffer::available() {
|
||||
if (!_running) {
|
||||
return 0;
|
||||
}
|
||||
int avail;
|
||||
avail = _wordsPerBuffer - _userOff;
|
||||
avail += ((_bufferCount + _curBuffer - _userBuffer) % _bufferCount) * _wordsPerBuffer;
|
||||
return avail;
|
||||
}
|
||||
|
||||
void AudioRingBuffer::flush() {
|
||||
while (_curBuffer != _userBuffer) {
|
||||
// busy wait
|
||||
}
|
||||
}
|
||||
|
||||
void __not_in_flash_func(AudioRingBuffer::_dmaIRQ)(int channel) {
|
||||
if (_isOutput) {
|
||||
for (uint32_t x = 0; x < _wordsPerBuffer; x++) {
|
||||
_buffers[_curBuffer]->buff[x] = _silenceSample;
|
||||
}
|
||||
_buffers[_curBuffer]-> empty = true;
|
||||
_overunderflow = _overunderflow | _buffers[_nextBuffer]->empty;
|
||||
dma_channel_set_read_addr(channel, _buffers[_nextBuffer]->buff, false);
|
||||
} else {
|
||||
_buffers[_curBuffer]-> empty = false;
|
||||
_overunderflow = _overunderflow | !_buffers[_nextBuffer]->empty;
|
||||
dma_channel_set_write_addr(channel, _buffers[_nextBuffer]->buff, false);
|
||||
}
|
||||
dma_channel_set_trans_count(channel, _wordsPerBuffer, false);
|
||||
_curBuffer = (_curBuffer + 1) % _bufferCount;
|
||||
_nextBuffer = (_nextBuffer + 1) % _bufferCount;
|
||||
dma_channel_acknowledge_irq0(channel);
|
||||
if (_callback) {
|
||||
_callback();
|
||||
}
|
||||
}
|
||||
|
||||
void __not_in_flash_func(AudioRingBuffer::_irq)() {
|
||||
for (size_t i = 0; i < sizeof(__channelMap); i++) {
|
||||
if (dma_channel_get_irq0_status(i) && __channelMap[i]) {
|
||||
__channelMap[i]->_dmaIRQ(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
AudioRingBuffer for Rasperry Pi Pico
|
||||
Implements a ring buffer for PIO DMA for I2S read or write
|
||||
|
||||
Copyright (c) 2022 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
#include <vector>
|
||||
|
||||
class AudioRingBuffer {
|
||||
public:
|
||||
AudioRingBuffer(size_t bufferCount, size_t bufferWords, int32_t silenceSample, PinMode direction = OUTPUT);
|
||||
~AudioRingBuffer();
|
||||
|
||||
void setCallback(void (*fn)());
|
||||
|
||||
bool begin(int dreq, volatile void *pioFIFOAddr);
|
||||
|
||||
bool write(uint32_t v, bool sync = true);
|
||||
bool read(uint32_t *v, bool sync = true);
|
||||
void flush();
|
||||
|
||||
bool getOverUnderflow();
|
||||
int available();
|
||||
|
||||
private:
|
||||
void _dmaIRQ(int channel);
|
||||
static void _irq();
|
||||
|
||||
typedef struct {
|
||||
uint32_t *buff;
|
||||
volatile bool empty;
|
||||
} AudioBuffer;
|
||||
|
||||
bool _running = false;
|
||||
std::vector<AudioBuffer*> _buffers;
|
||||
volatile int _curBuffer;
|
||||
volatile int _nextBuffer;
|
||||
size_t _chunkSampleCount;
|
||||
int _bitsPerSample;
|
||||
size_t _wordsPerBuffer;
|
||||
size_t _bufferCount;
|
||||
bool _isOutput;
|
||||
int32_t _silenceSample;
|
||||
int _channelDMA[2];
|
||||
void (*_callback)();
|
||||
|
||||
bool _overunderflow;
|
||||
|
||||
// User buffer pointer
|
||||
int _userBuffer = -1;
|
||||
size_t _userOff = 0;
|
||||
};
|
||||
+47
-25
@@ -49,8 +49,9 @@ I2S::I2S(PinMode direction) {
|
||||
_arb = nullptr;
|
||||
_cb = nullptr;
|
||||
_buffers = 8;
|
||||
_bufferWords = 16;
|
||||
_bufferWords = 0;
|
||||
_silenceSample = 0;
|
||||
_isLSBJ = false;
|
||||
}
|
||||
|
||||
I2S::~I2S() {
|
||||
@@ -99,6 +100,14 @@ bool I2S::setFrequency(int newFreq) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool I2S::setLSBJFormat() {
|
||||
if (_running || !_isOutput) {
|
||||
return false;
|
||||
}
|
||||
_isLSBJ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void I2S::onTransmit(void(*fn)(void)) {
|
||||
if (_isOutput) {
|
||||
_cb = fn;
|
||||
@@ -121,10 +130,14 @@ bool I2S::begin() {
|
||||
_running = true;
|
||||
_hasPeeked = false;
|
||||
int off = 0;
|
||||
_i2s = new PIOProgram(_isOutput ? &pio_i2s_out_program : &pio_i2s_in_program);
|
||||
_i2s = new PIOProgram(_isOutput ? (_isLSBJ ? &pio_lsbj_out_program : &pio_i2s_out_program) : &pio_i2s_in_program);
|
||||
_i2s->prepare(&_pio, &_sm, &off);
|
||||
if (_isOutput) {
|
||||
pio_i2s_out_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps);
|
||||
if (_isLSBJ) {
|
||||
pio_lsbj_out_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps);
|
||||
} else {
|
||||
pio_i2s_out_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps);
|
||||
}
|
||||
} else {
|
||||
pio_i2s_in_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps);
|
||||
}
|
||||
@@ -136,7 +149,10 @@ bool I2S::begin() {
|
||||
uint16_t a = _silenceSample & 0xffff;
|
||||
_silenceSample = (a << 16) | a;
|
||||
}
|
||||
_arb = new AudioRingBuffer(_buffers, _bufferWords, _silenceSample, _isOutput ? OUTPUT : INPUT);
|
||||
if (!_bufferWords) {
|
||||
_bufferWords = 16 * (_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]);
|
||||
_arb->setCallback(_cb);
|
||||
pio_sm_set_enabled(_pio, _sm, true);
|
||||
@@ -145,18 +161,24 @@ bool I2S::begin() {
|
||||
}
|
||||
|
||||
void I2S::end() {
|
||||
_running = false;
|
||||
delete _arb;
|
||||
_arb = nullptr;
|
||||
delete _i2s;
|
||||
_i2s = nullptr;
|
||||
if (_running) {
|
||||
pio_sm_set_enabled(_pio, _sm, false);
|
||||
_running = false;
|
||||
delete _arb;
|
||||
_arb = nullptr;
|
||||
delete _i2s;
|
||||
_i2s = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int I2S::available() {
|
||||
if (!_running || _isOutput) {
|
||||
if (!_running) {
|
||||
return 0;
|
||||
} else if (_isOutput) {
|
||||
return availableForWrite(); // Do what I mean, not what I say
|
||||
} else {
|
||||
return _arb->available();
|
||||
}
|
||||
return _arb->available();
|
||||
}
|
||||
|
||||
int I2S::read() {
|
||||
@@ -169,9 +191,9 @@ int I2S::read() {
|
||||
return _peekSaved;
|
||||
}
|
||||
|
||||
if (_wasHolding <= 0) {
|
||||
if (_isHolding <= 0) {
|
||||
read(&_holdWord, true);
|
||||
_wasHolding = 32;
|
||||
_isHolding = 32;
|
||||
}
|
||||
|
||||
int ret;
|
||||
@@ -179,18 +201,18 @@ int I2S::read() {
|
||||
case 8:
|
||||
ret = _holdWord >> 24;
|
||||
_holdWord <<= 8;
|
||||
_wasHolding -= 8;
|
||||
_isHolding -= 8;
|
||||
return ret;
|
||||
case 16:
|
||||
ret = _holdWord >> 16;
|
||||
_holdWord <<= 16;
|
||||
_wasHolding -= 32;
|
||||
_isHolding -= 32;
|
||||
return ret;
|
||||
case 24:
|
||||
case 32:
|
||||
default:
|
||||
ret = _holdWord;
|
||||
_wasHolding = 0;
|
||||
_isHolding = 0;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -219,26 +241,26 @@ size_t I2S::_writeNatural(int32_t s) {
|
||||
switch (_bps) {
|
||||
case 8:
|
||||
_holdWord |= s & 0xff;
|
||||
if (_wasHolding >= 24) {
|
||||
if (_isHolding >= 24) {
|
||||
auto ret = write(_holdWord, true);
|
||||
_holdWord = 0;
|
||||
_wasHolding = 0;
|
||||
_isHolding = 0;
|
||||
return ret;
|
||||
} else {
|
||||
_holdWord <<= 8;
|
||||
_wasHolding += 8;
|
||||
_isHolding += 8;
|
||||
return 1;
|
||||
}
|
||||
case 16:
|
||||
_holdWord |= s & 0xffff;
|
||||
if (_wasHolding) {
|
||||
if (_isHolding) {
|
||||
auto ret = write(_holdWord, true);
|
||||
_holdWord = 0;
|
||||
_wasHolding = 0;
|
||||
_isHolding = 0;
|
||||
return ret;
|
||||
} else {
|
||||
_holdWord <<= 16;
|
||||
_wasHolding = 16;
|
||||
_isHolding = 16;
|
||||
return 1;
|
||||
}
|
||||
case 24:
|
||||
@@ -295,13 +317,13 @@ bool I2S::read8(int8_t *l, int8_t *r) {
|
||||
if (!_running || _isOutput) {
|
||||
return false;
|
||||
}
|
||||
if (_wasHolding) {
|
||||
if (_isHolding) {
|
||||
*l = (_holdWord >> 8) & 0xff;
|
||||
*r = (_holdWord >> 0) & 0xff;
|
||||
_wasHolding = 0;
|
||||
_isHolding = 0;
|
||||
} else {
|
||||
read(&_holdWord, true);
|
||||
_wasHolding = 16;
|
||||
_isHolding = 16;
|
||||
*l = (_holdWord >> 24) & 0xff;
|
||||
*r = (_holdWord >> 16) & 0xff;
|
||||
}
|
||||
|
||||
@@ -21,8 +21,7 @@
|
||||
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
#include <vector>
|
||||
#include "AudioRingBuffer.h"
|
||||
#include "AudioBufferManager.h"
|
||||
|
||||
class I2S : public Stream {
|
||||
public:
|
||||
@@ -34,6 +33,7 @@ public:
|
||||
bool setBitsPerSample(int bps);
|
||||
bool setBuffers(size_t buffers, size_t bufferWords, int32_t silenceSample = 0);
|
||||
bool setFrequency(int newFreq);
|
||||
bool setLSBJFormat();
|
||||
|
||||
bool begin(long sampleRate) {
|
||||
setFrequency(sampleRate);
|
||||
@@ -105,6 +105,7 @@ private:
|
||||
size_t _buffers;
|
||||
size_t _bufferWords;
|
||||
int32_t _silenceSample;
|
||||
bool _isLSBJ;
|
||||
bool _isOutput;
|
||||
|
||||
bool _running;
|
||||
@@ -117,11 +118,11 @@ private:
|
||||
bool _writtenHalf;
|
||||
|
||||
int32_t _holdWord = 0;
|
||||
int _wasHolding = 0;
|
||||
int _isHolding = 0;
|
||||
|
||||
void (*_cb)();
|
||||
|
||||
AudioRingBuffer *_arb;
|
||||
AudioBufferManager *_arb;
|
||||
PIOProgram *_i2s;
|
||||
PIO _pio;
|
||||
int _sm;
|
||||
|
||||
@@ -41,6 +41,29 @@ right:
|
||||
out pins, 1 side 0b00 ; Last bit of right also has WCLK change
|
||||
; Loop back to beginning...
|
||||
|
||||
|
||||
.program pio_lsbj_out
|
||||
.side_set 2 ; 0 = bclk, 1=wclk
|
||||
|
||||
; The C code should place (number of bits/sample - 2) in Y and
|
||||
; also update the SHIFTCTRL to be 24 or 32 as appropriate
|
||||
|
||||
; +----- WCLK
|
||||
; |+---- BCLK
|
||||
mov x, y side 0b01
|
||||
left:
|
||||
out pins, 1 side 0b10
|
||||
jmp x--, left side 0b11
|
||||
out pins, 1 side 0b10
|
||||
|
||||
mov x, y side 0b11
|
||||
right:
|
||||
out pins, 1 side 0b00
|
||||
jmp x--, right side 0b01
|
||||
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
|
||||
@@ -73,7 +96,28 @@ static inline void pio_i2s_out_program_init(PIO pio, uint sm, uint offset, uint
|
||||
pio_gpio_init(pio, clock_pin_base + 1);
|
||||
|
||||
pio_sm_config sm_config = pio_i2s_out_program_get_default_config(offset);
|
||||
|
||||
|
||||
sm_config_set_out_pins(&sm_config, data_pin, 1);
|
||||
sm_config_set_sideset_pins(&sm_config, clock_pin_base);
|
||||
sm_config_set_out_shift(&sm_config, false, true, (bits <= 16) ? 2 * bits : bits);
|
||||
sm_config_set_fifo_join(&sm_config, PIO_FIFO_JOIN_TX);
|
||||
|
||||
pio_sm_init(pio, sm, offset, &sm_config);
|
||||
|
||||
uint pin_mask = (1u << data_pin) | (3u << clock_pin_base);
|
||||
pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask);
|
||||
pio_sm_set_pins(pio, sm, 0); // clear pins
|
||||
|
||||
pio_sm_exec(pio, sm, pio_encode_set(pio_y, bits - 2));
|
||||
}
|
||||
|
||||
static inline void pio_lsbj_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits) {
|
||||
pio_gpio_init(pio, data_pin);
|
||||
pio_gpio_init(pio, clock_pin_base);
|
||||
pio_gpio_init(pio, clock_pin_base + 1);
|
||||
|
||||
pio_sm_config sm_config = pio_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);
|
||||
|
||||
@@ -43,6 +43,41 @@ static inline pio_sm_config pio_i2s_out_program_get_default_config(uint offset)
|
||||
}
|
||||
#endif
|
||||
|
||||
// ------------ //
|
||||
// pio_lsbj_out //
|
||||
// ------------ //
|
||||
|
||||
#define pio_lsbj_out_wrap_target 0
|
||||
#define pio_lsbj_out_wrap 7
|
||||
|
||||
static const uint16_t pio_lsbj_out_program_instructions[] = {
|
||||
// .wrap_target
|
||||
0xa822, // 0: mov x, y side 1
|
||||
0x7001, // 1: out pins, 1 side 2
|
||||
0x1841, // 2: jmp x--, 1 side 3
|
||||
0x7001, // 3: out pins, 1 side 2
|
||||
0xb822, // 4: mov x, y side 3
|
||||
0x6001, // 5: out pins, 1 side 0
|
||||
0x0845, // 6: jmp x--, 5 side 1
|
||||
0x6001, // 7: out pins, 1 side 0
|
||||
// .wrap
|
||||
};
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
static const struct pio_program pio_lsbj_out_program = {
|
||||
.instructions = pio_lsbj_out_program_instructions,
|
||||
.length = 8,
|
||||
.origin = -1,
|
||||
};
|
||||
|
||||
static inline pio_sm_config pio_lsbj_out_program_get_default_config(uint offset) {
|
||||
pio_sm_config c = pio_get_default_sm_config();
|
||||
sm_config_set_wrap(&c, offset + pio_lsbj_out_wrap_target, offset + pio_lsbj_out_wrap);
|
||||
sm_config_set_sideset(&c, 2, false, false);
|
||||
return c;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ---------- //
|
||||
// pio_i2s_in //
|
||||
// ---------- //
|
||||
@@ -92,6 +127,21 @@ 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) {
|
||||
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);
|
||||
sm_config_set_out_pins(&sm_config, data_pin, 1);
|
||||
sm_config_set_sideset_pins(&sm_config, clock_pin_base);
|
||||
sm_config_set_out_shift(&sm_config, false, true, (bits <= 16) ? 2 * bits : bits);
|
||||
sm_config_set_fifo_join(&sm_config, PIO_FIFO_JOIN_TX);
|
||||
pio_sm_init(pio, sm, offset, &sm_config);
|
||||
uint pin_mask = (1u << data_pin) | (3u << clock_pin_base);
|
||||
pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask);
|
||||
pio_sm_set_pins(pio, sm, 0); // clear pins
|
||||
pio_sm_exec(pio, sm, pio_encode_set(pio_y, bits - 2));
|
||||
}
|
||||
static inline void pio_i2s_in_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits) {
|
||||
pio_gpio_init(pio, data_pin);
|
||||
pio_gpio_init(pio, clock_pin_base);
|
||||
@@ -109,4 +159,3 @@ static inline void pio_i2s_in_program_init(PIO pio, uint sm, uint offset, uint d
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
Submodule libraries/LittleFS/lib/littlefs updated: 40dba4a556...6a53d76e90
@@ -457,14 +457,14 @@ public:
|
||||
if (_creation) {
|
||||
int rc = lfs_setattr(_fs->getFS(), _name.get(), 'c', (const void *)&_creation, sizeof(_creation));
|
||||
if (rc < 0) {
|
||||
DEBUGV("Unable to set creation time on '%s' to %d\n", _name.get(), _creation);
|
||||
DEBUGV("Unable to set creation time on '%s' to %lld\n", _name.get(), _creation);
|
||||
}
|
||||
}
|
||||
// Add metadata with last write time
|
||||
time_t now = _timeCallback();
|
||||
int rc = lfs_setattr(_fs->getFS(), _name.get(), 't', (const void *)&now, sizeof(now));
|
||||
if (rc < 0) {
|
||||
DEBUGV("Unable to set last write time on '%s' to %d\n", _name.get(), now);
|
||||
DEBUGV("Unable to set last write time on '%s' to %lld\n", _name.get(), now);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
This example plays a raw, headerless, mono 16b, 44.1 sample using the PWMAudio library on GPIO 1.
|
||||
|
||||
Released to the public domain by Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
*/
|
||||
|
||||
#include <PWMAudio.h>
|
||||
#include "wav.h"
|
||||
|
||||
// The sample pointers
|
||||
const int16_t *start = (const int16_t *)out_raw;
|
||||
const int16_t *p = start;
|
||||
|
||||
// Create the PWM audio device on GPIO 1. Hook amp/speaker between GPIO1 and convenient GND.
|
||||
PWMAudio pwm(1);
|
||||
|
||||
unsigned int count = 0;
|
||||
|
||||
void cb() {
|
||||
while (pwm.availableForWrite()) {
|
||||
pwm.write(*p++);
|
||||
count += 2;
|
||||
if (count >= sizeof(out_raw)) {
|
||||
count = 0;
|
||||
p = start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
pwm.onTransmit(cb);
|
||||
pwm.begin(44100);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
/* noop, everything is done in the CB */
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
This example plays a tune that alternates between left and right channels using a simple sine wave.
|
||||
|
||||
Released to the public domain by Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
*/
|
||||
|
||||
#include <PWMAudio.h>
|
||||
|
||||
PWMAudio pwm(0, true); // GP0 = left, GP1 = right
|
||||
|
||||
const int freq = 48000; // Output frequency for PWM
|
||||
|
||||
int16_t al = 0;
|
||||
int16_t ar = 0;
|
||||
|
||||
const int notes[] = { 784, 880, 698, 349, 523 };
|
||||
const int dly[] = { 400, 500, 700, 500, 1000 };
|
||||
const int noteCnt = sizeof(notes) / sizeof(notes[0]);
|
||||
|
||||
int freqL = 1;
|
||||
int freqR = 1;
|
||||
|
||||
double sineTable[128]; // Precompute sine wave in 128 steps
|
||||
|
||||
unsigned int cnt = 0;
|
||||
void cb() {
|
||||
while (pwm.availableForWrite()) {
|
||||
double now = ((double)cnt) / (double)freq;
|
||||
int fl = freqL << 7; // Prescale by 128 to avoid FP math later on
|
||||
int fr = freqR << 7; // Prescale by 128 to avoid FP math later on
|
||||
pwm.write((uint16_t)(al * sineTable[(int)(now * fl) & 127]));
|
||||
pwm.write((uint16_t)(ar * sineTable[(int)(now * fr) & 127]));
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
// Set up sine table for waveform generation
|
||||
for (int i = 0; i < 128; i++) {
|
||||
sineTable[i] = sin(i * 2.0 * 3.14159 / 128.0);
|
||||
}
|
||||
pwm.setBuffers(4, 32); // Give larger buffers since we're are 48khz sample rate
|
||||
pwm.onTransmit(cb);
|
||||
pwm.begin(freq);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
delay(1000);
|
||||
|
||||
// Send it out on the LHS
|
||||
ar = 0;
|
||||
for (int i = 0; i < noteCnt; i++) {
|
||||
freqL = notes[i];
|
||||
al = 5000;
|
||||
delay(dly[i]);
|
||||
}
|
||||
al = 0;
|
||||
delay(1000);
|
||||
|
||||
// Hear reply on RHS
|
||||
for (int i = 0; i < noteCnt; i++) {
|
||||
freqR = notes[i] / 4;
|
||||
ar = 15000;
|
||||
delay(dly[i]);
|
||||
}
|
||||
ar = 0;
|
||||
delay(3000);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
PWMAudio KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
begin KEYWORD2
|
||||
end KEYWORD2
|
||||
|
||||
setPin KEYWORD2
|
||||
setFrequency KEYWORD2
|
||||
setBuffers KEYWORD2
|
||||
setStereo KEYWORD2
|
||||
|
||||
onTransmit KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
@@ -0,0 +1,10 @@
|
||||
name=PWMAudio
|
||||
version=1.0
|
||||
author=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
sentence=Plays audio on a pin using the PWM hardware, no DAC required
|
||||
paragraph=Plays audio on a pin using the PWM hardware, no DAC required
|
||||
category=Communication
|
||||
url=http://github.com/earlephilhower/arduino-pico
|
||||
architectures=rp2040
|
||||
dot_a_linkage=true
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
PWMAudio
|
||||
Plays a 16b audio stream on a user defined pin using PWM
|
||||
|
||||
Copyright (c) 2022 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <Arduino.h>
|
||||
#include "PWMAudio.h"
|
||||
#include <hardware/pwm.h>
|
||||
|
||||
|
||||
PWMAudio::PWMAudio(pin_size_t pin, bool stereo) {
|
||||
_running = false;
|
||||
_pin = pin;
|
||||
_freq = 48000;
|
||||
_arb = nullptr;
|
||||
_cb = nullptr;
|
||||
_buffers = 8;
|
||||
_bufferWords = 0;
|
||||
_stereo = stereo;
|
||||
}
|
||||
|
||||
PWMAudio::~PWMAudio() {
|
||||
end();
|
||||
}
|
||||
|
||||
bool PWMAudio::setBuffers(size_t buffers, size_t bufferWords) {
|
||||
if (_running || (buffers < 3) || (bufferWords < 8)) {
|
||||
return false;
|
||||
}
|
||||
_buffers = buffers;
|
||||
_bufferWords = bufferWords;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PWMAudio::setPin(pin_size_t pin) {
|
||||
if (_running) {
|
||||
return false;
|
||||
}
|
||||
_pin = pin;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PWMAudio::setStereo(bool stereo) {
|
||||
if (_running) {
|
||||
return false;
|
||||
}
|
||||
_stereo = stereo;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PWMAudio::setFrequency(int newFreq) {
|
||||
_freq = newFreq;
|
||||
|
||||
// Figure out the scale factor for PWM values
|
||||
float fPWM = 65535.0 * _freq; // ideal
|
||||
|
||||
if (fPWM > clock_get_hz(clk_sys)) {
|
||||
// Need to downscale the range to hit the frequency target
|
||||
float pwmMax = (float) clock_get_hz(clk_sys) / (float) _freq;
|
||||
_pwmScale = pwmMax;
|
||||
fPWM = clock_get_hz(clk_sys);
|
||||
} else {
|
||||
_pwmScale = 1 << 16;
|
||||
}
|
||||
|
||||
pwm_config c = pwm_get_default_config();
|
||||
pwm_config_set_clkdiv(&c, clock_get_hz(clk_sys) / fPWM);
|
||||
pwm_config_set_wrap(&c, _pwmScale);
|
||||
pwm_init(pwm_gpio_to_slice_num(_pin), &c, _running);
|
||||
gpio_set_function(_pin, GPIO_FUNC_PWM);
|
||||
pwm_set_gpio_level(_pin, (0x8000 * _pwmScale) >> 16);
|
||||
if (_stereo) {
|
||||
gpio_set_function(_pin + 1, GPIO_FUNC_PWM);
|
||||
pwm_set_gpio_level(_pin + 1, (0x8000 * _pwmScale) >> 16);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PWMAudio::onTransmit(void(*fn)(void)) {
|
||||
_cb = fn;
|
||||
if (_running) {
|
||||
_arb->setCallback(_cb);
|
||||
}
|
||||
}
|
||||
|
||||
bool PWMAudio::begin() {
|
||||
if (_stereo && (_pin & 1)) {
|
||||
// Illegal, need to have consecutive pins on the same PWM slice
|
||||
Serial.printf("ERROR: PWMAudio stereo mode requires pin be even\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
_running = true;
|
||||
_wasHolding = false;
|
||||
|
||||
if (!_bufferWords) {
|
||||
_bufferWords = 16;
|
||||
}
|
||||
|
||||
setFrequency(_freq);
|
||||
|
||||
uint32_t ccAddr = PWM_BASE + PWM_CH0_CC_OFFSET + pwm_gpio_to_slice_num(_pin) * 20;
|
||||
|
||||
_arb = new AudioBufferManager(_buffers, _bufferWords, 0x80008000, OUTPUT, DMA_SIZE_32);
|
||||
_arb->begin(pwm_get_dreq(pwm_gpio_to_slice_num(_pin)), (volatile void*)ccAddr);
|
||||
_arb->setCallback(_cb);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PWMAudio::end() {
|
||||
if (_running) {
|
||||
_running = false;
|
||||
pinMode(_pin, OUTPUT);
|
||||
if (_stereo) {
|
||||
pinMode(_pin + 1, OUTPUT);
|
||||
}
|
||||
delete _arb;
|
||||
_arb = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int PWMAudio::available() {
|
||||
return availableForWrite(); // Do what I mean, not what I say
|
||||
}
|
||||
|
||||
int PWMAudio::read() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int PWMAudio::peek() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
void PWMAudio::flush() {
|
||||
if (_running) {
|
||||
_arb->flush();
|
||||
}
|
||||
}
|
||||
|
||||
int PWMAudio::availableForWrite() {
|
||||
if (!_running) {
|
||||
return 0;
|
||||
}
|
||||
return _arb->available();
|
||||
}
|
||||
|
||||
size_t PWMAudio::write(int16_t val, bool sync) {
|
||||
if (!_running) {
|
||||
return 0;
|
||||
}
|
||||
// Go from signed -32K...32K to unsigned 0...64K
|
||||
uint32_t sample = (uint32_t)(val + 0x8000);
|
||||
// Adjust to the real range
|
||||
sample *= _pwmScale;
|
||||
sample >>= 16;
|
||||
if (!_stereo) {
|
||||
// Duplicate sample since we don't care which PWM channel
|
||||
sample = (sample & 0xffff) | (sample << 16);
|
||||
return _arb->write(sample, sync);
|
||||
} else {
|
||||
if (_wasHolding) {
|
||||
_holdWord = (_holdWord & 0xffff) | (sample << 16);
|
||||
auto ret = _arb->write(_holdWord, sync);
|
||||
if (ret) {
|
||||
_wasHolding = false;
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
_holdWord = sample;
|
||||
_wasHolding = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t PWMAudio::write(const uint8_t *buffer, size_t size) {
|
||||
// We can only write 16-bit chunks here
|
||||
if (size & 0x1) {
|
||||
return 0;
|
||||
}
|
||||
size_t writtenSize = 0;
|
||||
int16_t *p = (int16_t *)buffer;
|
||||
while (size) {
|
||||
if (!write((int16_t)*p)) {
|
||||
// Blocked, stop write here
|
||||
return writtenSize;
|
||||
} else {
|
||||
p++;
|
||||
size -= 4;
|
||||
writtenSize += 4;
|
||||
}
|
||||
}
|
||||
return writtenSize;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
PWMAudio
|
||||
Plays a signed 16b audio stream on a user defined pin using PWM
|
||||
|
||||
Copyright (c) 2022 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
#include "AudioBufferManager.h"
|
||||
|
||||
class PWMAudio : public Stream {
|
||||
public:
|
||||
PWMAudio(pin_size_t pin = 0, bool stereo = false);
|
||||
virtual ~PWMAudio();
|
||||
|
||||
bool setBuffers(size_t buffers, size_t bufferWords);
|
||||
bool setFrequency(int newFreq);
|
||||
bool setPin(pin_size_t pin);
|
||||
bool setStereo(bool stereo = true);
|
||||
|
||||
bool begin(long sampleRate) {
|
||||
setFrequency(sampleRate);
|
||||
return begin();
|
||||
}
|
||||
|
||||
bool begin();
|
||||
void end();
|
||||
|
||||
// from Stream
|
||||
virtual int available() override;
|
||||
virtual int read() override;
|
||||
virtual int peek() override;
|
||||
virtual void flush() override;
|
||||
|
||||
// from Print (see notes on write() methods below)
|
||||
virtual size_t write(const uint8_t *buffer, size_t size) override;
|
||||
virtual int availableForWrite() override;
|
||||
|
||||
virtual size_t write(uint8_t x) override {
|
||||
return write((int16_t) x, true);
|
||||
}
|
||||
|
||||
// Write 16 bit value to port, user responsible for packing/alignment, etc.
|
||||
size_t write(int16_t val, bool sync = true);
|
||||
size_t write(int val, bool sync = true) {
|
||||
return write((int16_t) val, sync);
|
||||
}
|
||||
|
||||
// Note that these callback are called from **INTERRUPT CONTEXT** and hence
|
||||
// should be in RAM, not FLASH, and should be quick to execute.
|
||||
void onTransmit(void(*)(void));
|
||||
|
||||
private:
|
||||
pin_size_t _pin;
|
||||
bool _stereo;
|
||||
|
||||
int _freq;
|
||||
|
||||
size_t _buffers;
|
||||
size_t _bufferWords;
|
||||
|
||||
uint32_t _pwmScale;
|
||||
|
||||
bool _running;
|
||||
|
||||
bool _wasHolding;
|
||||
uint32_t _holdWord;
|
||||
|
||||
void (*_cb)();
|
||||
|
||||
AudioBufferManager *_arb;
|
||||
};
|
||||
@@ -5,6 +5,6 @@ maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
sentence=Configures requests for OTA bootloader
|
||||
paragraph=Example repository for Ethernet drivers
|
||||
category=Device Control
|
||||
url=https://github.com/earlephilhower.arduino-pico
|
||||
url=https://github.com/earlephilhower/arduino-pico
|
||||
architectures=rp2040
|
||||
dot_a_linkage=true
|
||||
|
||||
@@ -178,7 +178,7 @@ void SPIClassRP2040::transfer(const void *txbuf, void *rxbuf, size_t count) {
|
||||
}
|
||||
|
||||
void SPIClassRP2040::beginTransaction(SPISettings settings) {
|
||||
DEBUGSPI("SPI::beginTransaction(clk=%d, bo=%s\n", _spis.getClockFreq(), (_spis.getBitOrder() == MSBFIRST) ? "MSB" : "LSB");
|
||||
DEBUGSPI("SPI::beginTransaction(clk=%lu, bo=%s\n", _spis.getClockFreq(), (_spis.getBitOrder() == MSBFIRST) ? "MSB" : "LSB");
|
||||
if (_initted && settings == _spis) {
|
||||
DEBUGSPI("SPI: Reusing existing initted SPI\n");
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
// Simple logger with USB upload to PC
|
||||
// Uses SingleFileDrive to export an onboard LittleFS file to the computer
|
||||
// The PC can open/copy the file, and then the user can delete it to restart
|
||||
|
||||
// Released to the public domain, 2022 - Earle F. Philhower, III
|
||||
|
||||
#include <SingleFileDrive.h>
|
||||
#include <LittleFS.h>
|
||||
|
||||
uint32_t cnt = 0;
|
||||
bool okayToWrite = true;
|
||||
|
||||
// Make the CSV file and give it a simple header
|
||||
void headerCSV() {
|
||||
File f = LittleFS.open("data.csv", "w");
|
||||
f.printf("sample,millis,temp,rand\n");
|
||||
f.close();
|
||||
cnt = 0;
|
||||
}
|
||||
|
||||
// Called when the USB stick connected to a PC and the drive opened
|
||||
// Note this is from a USB IRQ so no printing to SerialUSB/etc.
|
||||
void plug(uint32_t i) {
|
||||
(void) i;
|
||||
okayToWrite = false;
|
||||
}
|
||||
|
||||
// Called when the USB is ejected or removed from a PC
|
||||
// Note this is from a USB IRQ so no printing to SerialUSB/etc.
|
||||
void unplug(uint32_t i) {
|
||||
(void) i;
|
||||
okayToWrite = true;
|
||||
}
|
||||
|
||||
// Called when the PC tries to delete the single file
|
||||
// Note this is from a USB IRQ so no printing to SerialUSB/etc.
|
||||
void deleteCSV(uint32_t i) {
|
||||
(void) i;
|
||||
LittleFS.remove("data.csv");
|
||||
headerCSV();
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin();
|
||||
delay(5000);
|
||||
|
||||
LittleFS.begin();
|
||||
|
||||
// Set up the USB disk share
|
||||
singleFileDrive.onDelete(deleteCSV);
|
||||
singleFileDrive.onPlug(plug);
|
||||
singleFileDrive.onUnplug(unplug);
|
||||
singleFileDrive.begin("data.csv", "Recorded data from the Raspberry Pi Pico.csv");
|
||||
|
||||
// Find the last written data
|
||||
File f = LittleFS.open("data.csv", "r");
|
||||
if (!f || !f.size()) {
|
||||
cnt = 1;
|
||||
headerCSV();
|
||||
} else {
|
||||
if (f.size() > 2048) {
|
||||
f.seek(f.size() - 1024);
|
||||
}
|
||||
do {
|
||||
String s = f.readStringUntil('\n');
|
||||
sscanf(s.c_str(), "%lu,", &cnt);
|
||||
} while (f.available());
|
||||
f.close();
|
||||
cnt++;
|
||||
}
|
||||
|
||||
Serial.printf("Starting acquisition at %lu\n", cnt);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
float temp = analogReadTemp();
|
||||
uint32_t hwrand = rp2040.hwrand32();
|
||||
// Make sure the USB connect doesn't happen while we're writing!
|
||||
noInterrupts();
|
||||
if (okayToWrite) {
|
||||
Serial.printf("Sampling...%lu\n", cnt);
|
||||
// Don't want the USB to connect during an update!
|
||||
File f = LittleFS.open("data.csv", "a");
|
||||
if (f) {
|
||||
f.printf("%lu,%lu,%f,%lu\n", cnt++, millis(), temp, hwrand);
|
||||
f.close();
|
||||
}
|
||||
}
|
||||
interrupts();
|
||||
|
||||
delay(10000);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
SingleFileDrive KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
singleFileDrive KEYWORD1
|
||||
onDelete KEYWORD1
|
||||
onPlug KEYWORD1
|
||||
onUnplug KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
@@ -0,0 +1,10 @@
|
||||
name=SingleFileDrive
|
||||
version=1.0.0
|
||||
author=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
sentence=Allows using USB MSC (USB stick) to transfer an onboard flash file to a PC
|
||||
paragraph=Emulates a USB stick and presents a single file for users to copy over/erase
|
||||
category=Device Control
|
||||
url=https://github.com/earlephilhower/arduino-pico
|
||||
architectures=rp2040
|
||||
dot_a_linkage=true
|
||||
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
SingleFileDrive - Emulates a USB stick for easy data transfer
|
||||
Copyright (c) 2022 Earle F. Philhower, III. All rights reserved.
|
||||
|
||||
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 <SingleFileDrive.h>
|
||||
#include <LittleFS.h>
|
||||
#include <class/msc/msc.h>
|
||||
|
||||
SingleFileDrive singleFileDrive;
|
||||
|
||||
static const uint32_t _hddsize = (256 * 1024 * 1024); // 256MB
|
||||
static const uint32_t _hddsects = _hddsize / 512;
|
||||
|
||||
// Ensure we are logged in to the USB framework
|
||||
void __USBInstallMassStorage() {
|
||||
/* dummy */
|
||||
}
|
||||
|
||||
SingleFileDrive::SingleFileDrive() {
|
||||
}
|
||||
|
||||
SingleFileDrive::~SingleFileDrive() {
|
||||
end();
|
||||
}
|
||||
|
||||
void SingleFileDrive::onDelete(void (*cb)(uint32_t), uint32_t cbData) {
|
||||
_cbDelete = cb;
|
||||
_cbDeleteData = cbData;
|
||||
}
|
||||
|
||||
void SingleFileDrive::onPlug(void (*cb)(uint32_t), uint32_t cbData) {
|
||||
_cbPlug = cb;
|
||||
_cbPlugData = cbData;
|
||||
}
|
||||
|
||||
void SingleFileDrive::onUnplug(void (*cb)(uint32_t), uint32_t cbData) {
|
||||
_cbUnplug = cb;
|
||||
_cbUnplugData = cbData;
|
||||
}
|
||||
|
||||
bool SingleFileDrive::begin(const char *localFile, const char *dosFile) {
|
||||
if (_started) {
|
||||
return false;
|
||||
}
|
||||
_localFile = strdup(localFile);
|
||||
_dosFile = strdup(dosFile);
|
||||
_started = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SingleFileDrive::end() {
|
||||
_started = false;
|
||||
free(_localFile);
|
||||
free(_dosFile);
|
||||
_localFile = nullptr;
|
||||
_dosFile = nullptr;
|
||||
}
|
||||
|
||||
void SingleFileDrive::bootSector(char buff[512]) {
|
||||
// 256MB FAT16 stolen from mkfs.fat
|
||||
// dd if=/dev/zero of=/tmp/fat.bin bs=1M seek=255 count=1
|
||||
// mkfs.fat -F 16 -r 16 -n PICODISK -i 12345678 -s 128 -m ':(' /tmp/fat.bin
|
||||
const uint8_t hdr[] = {
|
||||
0xeb, 0x3c, 0x90, 0x6d, 0x6b, 0x66, 0x73, 0x2e, 0x66, 0x61, 0x74, 0x00,
|
||||
0x02, 0x80, 0x80, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0xf8, 0x80, 0x00,
|
||||
0x20, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
|
||||
0x80, 0x00, 0x29, 0x78, 0x56, 0x34, 0x12, 0x50, 0x49, 0x43, 0x4f, 0x44,
|
||||
0x49, 0x53, 0x4b, 0x20, 0x20, 0x20, 0x46, 0x41, 0x54, 0x31, 0x36, 0x20,
|
||||
0x20, 0x20, 0x0e, 0x1f, 0xbe, 0x5b, 0x7c, 0xac, 0x22, 0xc0, 0x74, 0x0b,
|
||||
0x56, 0xb4, 0x0e, 0xbb, 0x07, 0x00, 0xcd, 0x10, 0x5e, 0xeb, 0xf0, 0x32,
|
||||
0xe4, 0xcd, 0x16, 0xcd, 0x19, 0xeb, 0xfe, 0x3a, 0x28, 0x0d, 0x0a, 0x00
|
||||
};
|
||||
memset(buff, 0, 512);
|
||||
memcpy(buff, hdr, sizeof(hdr));
|
||||
buff[0x1fe] = 0x55;
|
||||
buff[0x1ff] = 0xff;
|
||||
}
|
||||
|
||||
static char _toLegalFATChar(char c) {
|
||||
const char *odds = "!#$%&'()-@^_`{}~";
|
||||
c = toupper(c);
|
||||
if (((c >= '0') && (c <= '9')) || ((c >= 'A') && (c <= 'Z')) || strchr(odds, c)) {
|
||||
return c;
|
||||
} else {
|
||||
return '~';
|
||||
}
|
||||
}
|
||||
|
||||
void SingleFileDrive::directorySector(char buff[512]) {
|
||||
const uint8_t lbl[] = {
|
||||
0x50, 0x49, 0x43, 0x4f, 0x44, 0x49, 0x53, 0x4b, 0x20, 0x20, 0x20, 0x08, 0x00, 0x00, 0xac, 0x56,
|
||||
0x82, 0x55, 0x82, 0x55, 0x00, 0x00, 0xac, 0x56, 0x82, 0x55
|
||||
}; //, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
memset(buff, 0, 512);
|
||||
memcpy(buff, lbl, sizeof(lbl));
|
||||
buff += 32; // Skip the just-set label
|
||||
|
||||
// Create a legal 11-char UPPERCASE FILENAME WITH 0x20 PAD
|
||||
char SFN[11];
|
||||
memset(SFN, ' ', 11);
|
||||
for (int i = 0; (i < 8) && _dosFile[i] && (_dosFile[i] != '.'); i++) {
|
||||
SFN[i] = _toLegalFATChar(_dosFile[i]);
|
||||
}
|
||||
char *dot = _dosFile + strlen(_dosFile) - 1;
|
||||
while ((dot >= _dosFile) && (*dot != '.')) {
|
||||
dot--;
|
||||
}
|
||||
if (*dot == '.') {
|
||||
dot++;
|
||||
for (int i = 0; (i < 3) && dot[i]; i++) {
|
||||
SFN[8 + i] = _toLegalFATChar(dot[i]);
|
||||
}
|
||||
}
|
||||
uint8_t chksum = 0; // for LFN
|
||||
for (int i = 0; i < 11; i++) {
|
||||
chksum = (chksum >> 1) + (chksum << 7) + SFN[i];
|
||||
}
|
||||
|
||||
// Create LFN structure
|
||||
int entries = (strlen(_dosFile) + 12) / 13; // round up
|
||||
for (int i = 0; i < entries; i++) {
|
||||
*buff++ = (entries - i) | (i == 0 ? 0x40 : 0);
|
||||
const char *partname = _dosFile + 13 * (entries - i - 1);
|
||||
for (int j = 0; j < 13; j++) {
|
||||
uint16_t u;
|
||||
if (j > (int)strlen(partname)) {
|
||||
u = 0xffff;
|
||||
} else {
|
||||
u = partname[j] & 0xff;
|
||||
}
|
||||
*buff++ = u & 0xff;
|
||||
*buff++ = (u >> 8) & 0xff;
|
||||
if (j == 4) {
|
||||
*buff++ = 0x0f; // LFN ATTR
|
||||
*buff++ = 0;
|
||||
*buff++ = chksum;
|
||||
} else if (j == 10) {
|
||||
*buff++ = 0;
|
||||
*buff++ = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create SFN
|
||||
memset(buff, 0, 32);
|
||||
for (int i = 0; i < 11; i++) {
|
||||
buff[i] = SFN[i];
|
||||
}
|
||||
buff[0x0b] = 0x20; // ATTR = Archive
|
||||
// Ignore creation data/time, etc.
|
||||
buff[0x1a] = 0x03; // Starting cluster 3
|
||||
File f = LittleFS.open(_localFile, "r");
|
||||
int size = f.size();
|
||||
f.close();
|
||||
buff[0x1c] = size & 255;
|
||||
buff[0x1d] = (size >> 8) & 255;
|
||||
buff[0x1e] = (size >> 16) & 255; // 16MB or smaller
|
||||
}
|
||||
|
||||
void SingleFileDrive::fatSector(char fat[512]) {
|
||||
memset(fat, 0, 512);
|
||||
fat[0x00] = 0xff;
|
||||
fat[0x01] = 0xf8;
|
||||
fat[0x02] = 0xff;
|
||||
fat[0x03] = 0xff;
|
||||
int cluster = 3;
|
||||
File f = LittleFS.open(_localFile, "r");
|
||||
int size = f.size();
|
||||
f.close();
|
||||
while (size > 65536) {
|
||||
fat[cluster * 2] = (cluster + 1) & 0xff;
|
||||
fat[cluster * 2 + 1] = ((cluster + 1) >> 8) & 0xff;
|
||||
cluster++;
|
||||
size -= 65536;
|
||||
}
|
||||
fat[cluster * 2] = 0xff;
|
||||
fat[cluster * 2 + 1] = 0xff;
|
||||
}
|
||||
|
||||
// Invoked to determine max LUN
|
||||
extern "C" uint8_t tud_msc_get_maxlun_cb(void) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Invoked when received SCSI_CMD_INQUIRY
|
||||
// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
|
||||
extern "C" void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) {
|
||||
(void) lun;
|
||||
|
||||
const char vid[] = "PicoDisk";
|
||||
const char pid[] = "Mass Storage";
|
||||
const char rev[] = "1.0";
|
||||
|
||||
memcpy(vendor_id, vid, strlen(vid));
|
||||
memcpy(product_id, pid, strlen(pid));
|
||||
memcpy(product_rev, rev, strlen(rev));
|
||||
}
|
||||
|
||||
bool SingleFileDrive::testUnitReady() {
|
||||
return _started;
|
||||
}
|
||||
|
||||
// Invoked when received Test Unit Ready command.
|
||||
// return true allowing host to read/write this LUN e.g SD card inserted
|
||||
extern "C" bool tud_msc_test_unit_ready_cb(uint8_t lun) {
|
||||
(void) lun;
|
||||
|
||||
return singleFileDrive.testUnitReady();
|
||||
}
|
||||
|
||||
// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
|
||||
// Application update block count and block size
|
||||
extern "C" void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size) {
|
||||
(void) lun;
|
||||
*block_count = _hddsects;
|
||||
*block_size = 512;
|
||||
}
|
||||
|
||||
|
||||
// Callback invoked when received READ10 command.
|
||||
// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
|
||||
extern "C" int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) {
|
||||
(void) lun;
|
||||
return singleFileDrive.read10(lba, offset, buffer, bufsize);
|
||||
}
|
||||
|
||||
int32_t SingleFileDrive::read10(uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) {
|
||||
if (!_started || (lba >= _hddsects)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t toread = bufsize;
|
||||
uint8_t *curbuff = (uint8_t *)buffer;
|
||||
|
||||
while (bufsize > 0) {
|
||||
if (lba == 0) {
|
||||
bootSector(_sectBuff);
|
||||
} else if ((lba == 128) || (lba == 256)) {
|
||||
fatSector(_sectBuff);
|
||||
} else if (lba == 384) {
|
||||
directorySector(_sectBuff);
|
||||
} else if (lba >= 640) {
|
||||
File f = LittleFS.open(_localFile, "r");
|
||||
f.seek((lba - 640) * 512);
|
||||
f.read((uint8_t*)_sectBuff, 512);
|
||||
f.close();
|
||||
} else {
|
||||
memset(_sectBuff, 0, sizeof(_sectBuff));
|
||||
}
|
||||
|
||||
uint32_t cplen = 512 - offset;
|
||||
if (bufsize < cplen) {
|
||||
cplen = bufsize;
|
||||
}
|
||||
memcpy(curbuff, _sectBuff + offset, cplen);
|
||||
curbuff += cplen;
|
||||
offset = 0;
|
||||
lba++;
|
||||
bufsize -= cplen;
|
||||
}
|
||||
|
||||
return toread;
|
||||
}
|
||||
|
||||
extern "C" bool tud_msc_is_writable_cb(uint8_t lun) {
|
||||
(void) lun;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Callback invoked when received WRITE10 command.
|
||||
// Process data in buffer to disk's storage and return number of written bytes
|
||||
extern "C" int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) {
|
||||
(void) lun;
|
||||
return singleFileDrive.write10(lba, offset, buffer, bufsize);
|
||||
}
|
||||
|
||||
int32_t SingleFileDrive::write10(uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) {
|
||||
if (!_started || (lba >= _hddsects)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t addr = lba * 512 + offset;
|
||||
uint32_t hotspot = 384 * 512 + 0x20;
|
||||
if ((addr > hotspot) || (addr + bufsize < hotspot)) {
|
||||
// Did not try and erase the file entry, ignore
|
||||
return bufsize;
|
||||
}
|
||||
int off = hotspot - addr;
|
||||
uint8_t *ptr = (uint8_t *)buffer;
|
||||
ptr += off;
|
||||
if (*ptr == 0xe5) {
|
||||
if (_cbDelete) {
|
||||
_cbDelete(_cbDeleteData);
|
||||
}
|
||||
}
|
||||
|
||||
return bufsize;
|
||||
}
|
||||
|
||||
extern "C" bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, uint8_t add_sense_qualifier);
|
||||
|
||||
// Callback invoked when received an SCSI command not in built-in list below
|
||||
// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
|
||||
// - READ10 and WRITE10 has their own callbacks
|
||||
extern "C" int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) {
|
||||
const int SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL = 0x1E;
|
||||
const int SCSI_CMD_START_STOP_UNIT = 0x1B;
|
||||
const int SCSI_SENSE_ILLEGAL_REQUEST = 0x05;
|
||||
|
||||
void const* response = NULL;
|
||||
int32_t resplen = 0;
|
||||
|
||||
// most scsi handled is input
|
||||
bool in_xfer = true;
|
||||
scsi_start_stop_unit_t const * start_stop = (scsi_start_stop_unit_t const *) scsi_cmd;
|
||||
switch (scsi_cmd[0]) {
|
||||
case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
|
||||
// Host is about to read/write etc ... better not to disconnect disk
|
||||
if (scsi_cmd[4] & 1) {
|
||||
singleFileDrive.plug();
|
||||
}
|
||||
resplen = 0;
|
||||
break;
|
||||
case SCSI_CMD_START_STOP_UNIT:
|
||||
// Host try to eject/safe remove/poweroff us. We could safely disconnect with disk storage, or go into lower power
|
||||
if (!start_stop->start && start_stop->load_eject) {
|
||||
singleFileDrive.unplug();
|
||||
} else if (start_stop->start && start_stop->load_eject) {
|
||||
singleFileDrive.plug();
|
||||
}
|
||||
resplen = 0;
|
||||
break;
|
||||
default:
|
||||
// Set Sense = Invalid Command Operation
|
||||
tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
|
||||
// negative means error -> tinyusb could stall and/or response with failed status
|
||||
resplen = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
// return resplen must not larger than bufsize
|
||||
if (resplen > bufsize) {
|
||||
resplen = bufsize;
|
||||
}
|
||||
|
||||
if (response && (resplen > 0)) {
|
||||
if (in_xfer) {
|
||||
memcpy(buffer, response, resplen);
|
||||
} else {
|
||||
// SCSI output
|
||||
}
|
||||
}
|
||||
|
||||
return resplen;
|
||||
}
|
||||
|
||||
void SingleFileDrive::plug() {
|
||||
if (_started && _cbPlug) {
|
||||
_cbPlug(_cbPlugData);
|
||||
}
|
||||
}
|
||||
|
||||
void SingleFileDrive::unplug() {
|
||||
if (_started && _cbUnplug) {
|
||||
_cbUnplug(_cbUnplugData);
|
||||
}
|
||||
}
|
||||
|
||||
// Callback invoked on start/stop
|
||||
extern "C" bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) {
|
||||
(void) lun;
|
||||
(void) power_condition;
|
||||
if (start && load_eject) {
|
||||
singleFileDrive.plug();
|
||||
} else if (!start && load_eject) {
|
||||
singleFileDrive.unplug();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
SingleFileDrive - Emulates a USB stick for easy data transfer
|
||||
Copyright (c) 2022 Earle F. Philhower, III. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
class SingleFileDrive {
|
||||
public:
|
||||
SingleFileDrive();
|
||||
~SingleFileDrive();
|
||||
|
||||
bool begin(const char *localFile, const char *dosFile);
|
||||
void end();
|
||||
|
||||
void onDelete(void (*cb)(uint32_t), uint32_t cbData = 0);
|
||||
void onPlug(void (*cb)(uint32_t), uint32_t cbData = 0);
|
||||
void onUnplug(void (*cb)(uint32_t), uint32_t cbData = 0);
|
||||
|
||||
// Only for internal TinyUSB callback use
|
||||
bool testUnitReady();
|
||||
int32_t read10(uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize);
|
||||
int32_t write10(uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize);
|
||||
void plug();;
|
||||
void unplug();
|
||||
|
||||
private:
|
||||
void bootSector(char buff[512]);
|
||||
void directorySector(char buff[512]);
|
||||
void fatSector(char buff[512]);
|
||||
|
||||
private:
|
||||
bool _started = false;
|
||||
char *_localFile = nullptr;
|
||||
char *_dosFile = nullptr;
|
||||
|
||||
char _sectBuff[512]; // Read sector region
|
||||
|
||||
void (*_cbDelete)(uint32_t) = nullptr;
|
||||
uint32_t _cbDeleteData = 0;
|
||||
|
||||
void (*_cbPlug)(uint32_t) = nullptr;
|
||||
uint32_t _cbPlugData = 0;
|
||||
|
||||
void (*_cbUnplug)(uint32_t) = nullptr;
|
||||
uint32_t _cbUnplugData = 0;
|
||||
};
|
||||
|
||||
extern SingleFileDrive singleFileDrive;
|
||||
@@ -129,7 +129,7 @@ bool UpdaterClass::begin(size_t size, int command) {
|
||||
_command = command;
|
||||
|
||||
#ifdef DEBUG_UPDATER
|
||||
DEBUG_UPDATER.printf_P(PSTR("[begin] _startAddress: 0x%08X (%d)\n"), _startAddress, _startAddress);
|
||||
DEBUG_UPDATER.printf_P(PSTR("[begin] _startAddress: 0x%08lX (%lu)\n"), _startAddress, _startAddress);
|
||||
DEBUG_UPDATER.printf_P(PSTR("[begin] _size: 0x%08zX (%zd)\n"), _size, _size);
|
||||
#endif
|
||||
|
||||
@@ -188,7 +188,7 @@ bool UpdaterClass::end(bool evenIfRemaining) {
|
||||
_fp.read((uint8_t *)&sigLen, sizeof(uint32_t));
|
||||
}
|
||||
#ifdef DEBUG_UPDATER
|
||||
DEBUG_UPDATER.printf_P(PSTR("[Updater] sigLen: %d\n"), sigLen);
|
||||
DEBUG_UPDATER.printf_P(PSTR("[Updater] sigLen: %lu\n"), sigLen);
|
||||
#endif
|
||||
if (sigLen != expectedSigLen) {
|
||||
_setError(UPDATE_ERROR_SIGN);
|
||||
@@ -276,7 +276,7 @@ bool UpdaterClass::end(bool evenIfRemaining) {
|
||||
picoOTA.addFile("firmware.bin");
|
||||
picoOTA.commit();
|
||||
#ifdef DEBUG_UPDATER
|
||||
DEBUG_UPDATER.printf_P(PSTR("Staged: address:0x%08X, size:0x%08zX\n"), _startAddress, _size);
|
||||
DEBUG_UPDATER.printf_P(PSTR("Staged: address:0x%08lX, size:0x%08zX\n"), _startAddress, _size);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <WiFiClient.h>
|
||||
#include <WebServer.h>
|
||||
#include <LEAmDNS.h>
|
||||
#include <StreamString.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
@@ -48,14 +49,13 @@ const int led = LED_BUILTIN;
|
||||
void handleRoot() {
|
||||
static int cnt = 0;
|
||||
digitalWrite(led, 1);
|
||||
char temp[400];
|
||||
int sec = millis() / 1000;
|
||||
int min = sec / 60;
|
||||
int hr = min / 60;
|
||||
|
||||
snprintf(temp, 400,
|
||||
|
||||
"<html>\
|
||||
StreamString temp;
|
||||
temp.reserve(500); // Preallocate a large chunk to avoid memory fragmentation
|
||||
temp.printf("<html>\
|
||||
<head>\
|
||||
<meta http-equiv='refresh' content='5'/>\
|
||||
<title>Pico-W Demo</title>\
|
||||
@@ -70,9 +70,7 @@ void handleRoot() {
|
||||
<p>Page Count: %d</p>\
|
||||
<img src=\"/test.svg\" />\
|
||||
</body>\
|
||||
</html>",
|
||||
|
||||
hr, min % 60, sec % 60, rp2040.getFreeHeap(), ++cnt);
|
||||
</html>", hr, min % 60, sec % 60, rp2040.getFreeHeap(), ++cnt);
|
||||
server.send(200, "text/html", temp);
|
||||
digitalWrite(led, 0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WebServer.h>
|
||||
#include <LEAmDNS.h>
|
||||
|
||||
#ifndef STASSID
|
||||
#define STASSID "your-ssid"
|
||||
#define STAPSK "your-password"
|
||||
#endif
|
||||
|
||||
const char* ssid = STASSID;
|
||||
const char* password = STAPSK;
|
||||
|
||||
WebServer server(80);
|
||||
|
||||
const int led = LED_BUILTIN;
|
||||
|
||||
void handleRoot() {
|
||||
digitalWrite(led, 1);
|
||||
server.send(200, "text/plain", "hello from pico w!\r\n");
|
||||
digitalWrite(led, 0);
|
||||
}
|
||||
|
||||
void handleNotFound() {
|
||||
digitalWrite(led, 1);
|
||||
String message = "File Not Found\n\n";
|
||||
message += "URI: ";
|
||||
message += server.uri();
|
||||
message += "\nMethod: ";
|
||||
message += (server.method() == HTTP_GET) ? "GET" : "POST";
|
||||
message += "\nArguments: ";
|
||||
message += server.args();
|
||||
message += "\n";
|
||||
for (uint8_t i = 0; i < server.args(); i++) {
|
||||
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
|
||||
}
|
||||
server.send(404, "text/plain", message);
|
||||
digitalWrite(led, 0);
|
||||
}
|
||||
|
||||
void setup(void) {
|
||||
pinMode(led, OUTPUT);
|
||||
digitalWrite(led, 0);
|
||||
Serial.begin(115200);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
Serial.println("");
|
||||
|
||||
// Wait for connection
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
Serial.println("");
|
||||
Serial.print("Connected to ");
|
||||
Serial.println(ssid);
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
if (MDNS.begin("picow")) {
|
||||
Serial.println("MDNS responder started");
|
||||
}
|
||||
|
||||
server.on("/", handleRoot);
|
||||
|
||||
server.on("/inline", []() {
|
||||
server.send(200, "text/plain", "this works as well");
|
||||
});
|
||||
|
||||
server.on("/gif", []() {
|
||||
static const uint8_t gif[] = {
|
||||
0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x10, 0x00, 0x10, 0x00, 0x80, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x2c, 0x00, 0x00, 0x00, 0x00,
|
||||
0x10, 0x00, 0x10, 0x00, 0x00, 0x02, 0x19, 0x8c, 0x8f, 0xa9, 0xcb, 0x9d,
|
||||
0x00, 0x5f, 0x74, 0xb4, 0x56, 0xb0, 0xb0, 0xd2, 0xf2, 0x35, 0x1e, 0x4c,
|
||||
0x0c, 0x24, 0x5a, 0xe6, 0x89, 0xa6, 0x4d, 0x01, 0x00, 0x3b
|
||||
};
|
||||
char gif_colored[sizeof(gif)];
|
||||
memcpy_P(gif_colored, gif, sizeof(gif));
|
||||
// Set the background to a random set of colors
|
||||
gif_colored[16] = millis() % 256;
|
||||
gif_colored[17] = millis() % 256;
|
||||
gif_colored[18] = millis() % 256;
|
||||
server.send(200, "image/gif", gif_colored, sizeof(gif_colored));
|
||||
});
|
||||
|
||||
server.onNotFound(handleNotFound);
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
// Hook examples
|
||||
|
||||
server.addHook([](const String & method, const String & url, WiFiClient * client, WebServer::ContentTypeFunction contentType) {
|
||||
(void)method; // GET, PUT, ...
|
||||
(void)url; // example: /root/myfile.html
|
||||
(void)client; // the webserver tcp client connection
|
||||
(void)contentType; // contentType(".html") => "text/html"
|
||||
Serial.printf("A useless web hook has passed\n");
|
||||
return WebServer::CLIENT_REQUEST_CAN_CONTINUE;
|
||||
});
|
||||
|
||||
server.addHook([](const String&, const String & url, WiFiClient*, WebServer::ContentTypeFunction) {
|
||||
if (url.startsWith("/fail")) {
|
||||
Serial.printf("An always failing web hook has been triggered\n");
|
||||
return WebServer::CLIENT_MUST_STOP;
|
||||
}
|
||||
return WebServer::CLIENT_REQUEST_CAN_CONTINUE;
|
||||
});
|
||||
|
||||
server.addHook([](const String&, const String & url, WiFiClient * client, WebServer::ContentTypeFunction) {
|
||||
if (url.startsWith("/dump")) {
|
||||
Serial.printf("The dumper web hook is on the run\n");
|
||||
|
||||
// Here the request is not interpreted, so we cannot for sure
|
||||
// swallow the exact amount matching the full request+content,
|
||||
// hence the tcp connection cannot be handled anymore by the
|
||||
auto last = millis();
|
||||
while ((millis() - last) < 500) {
|
||||
char buf[32];
|
||||
size_t len = client->read((uint8_t*)buf, sizeof(buf));
|
||||
if (len > 0) {
|
||||
Serial.printf("(<%d> chars)", (int)len);
|
||||
Serial.write(buf, len);
|
||||
last = millis();
|
||||
}
|
||||
}
|
||||
// Two choices: return MUST STOP and webserver will close it
|
||||
// (we already have the example with '/fail' hook)
|
||||
// or IS GIVEN and webserver will forget it
|
||||
// trying with IS GIVEN and storing it on a dumb WiFiClient.
|
||||
// check the client connection: it should not immediately be closed
|
||||
// (make another '/dump' one to close the first)
|
||||
Serial.printf("\nTelling server to forget this connection\n");
|
||||
static WiFiClient forgetme = *client; // stop previous one if present and transfer client refcounter
|
||||
return WebServer::CLIENT_IS_GIVEN;
|
||||
}
|
||||
return WebServer::CLIENT_REQUEST_CAN_CONTINUE;
|
||||
});
|
||||
|
||||
// Hook examples
|
||||
/////////////////////////////////////////////////////////
|
||||
|
||||
server.begin();
|
||||
Serial.println("HTTP server started");
|
||||
}
|
||||
|
||||
void loop(void) {
|
||||
server.handleClient();
|
||||
MDNS.update();
|
||||
}
|
||||
@@ -249,20 +249,32 @@ void HTTPServer::httpHandleClient() {
|
||||
case HC_WAIT_READ:
|
||||
// Wait for data from client to become available
|
||||
if (_currentClient->available()) {
|
||||
if (_parseRequest(_currentClient)) {
|
||||
// because HTTP_MAX_SEND_WAIT is expressed in milliseconds,
|
||||
// it must be divided by 1000
|
||||
switch (_parseRequest(_currentClient)) {
|
||||
case CLIENT_REQUEST_CAN_CONTINUE:
|
||||
// Because HTTP_MAX_SEND_WAIT is expressed in milliseconds, it must be divided by 1000
|
||||
_currentClient->setTimeout(HTTP_MAX_SEND_WAIT / 1000);
|
||||
_contentLength = CONTENT_LENGTH_NOT_SET;
|
||||
_handleRequest();
|
||||
|
||||
// Fix for issue with Chrome based browsers: https://github.com/espressif/arduino-esp32/issues/3652
|
||||
// if (_currentClient->connected()) {
|
||||
// _currentStatus = HC_WAIT_CLOSE;
|
||||
// _statusChange = millis();
|
||||
// keepCurrentClient = true;
|
||||
// }
|
||||
}
|
||||
/* fallthrough */
|
||||
case CLIENT_REQUEST_IS_HANDLED:
|
||||
if (_currentClient->connected() || _currentClient->available()) {
|
||||
_currentStatus = HC_WAIT_CLOSE;
|
||||
_statusChange = millis();
|
||||
keepCurrentClient = true;
|
||||
} else {
|
||||
log_v("webserver: peer has closed after served\n");
|
||||
}
|
||||
break;
|
||||
case CLIENT_MUST_STOP:
|
||||
log_v("Close client\n");
|
||||
_currentClient->stop();
|
||||
break;
|
||||
case CLIENT_IS_GIVEN:
|
||||
// client must not be stopped but must not be handled here anymore
|
||||
// (example: tcp connection given to websocket)
|
||||
log_v("Give client\n");
|
||||
break;
|
||||
} // switch _parseRequest()
|
||||
} else { // !_currentClient->available()
|
||||
if (millis() - _statusChange <= HTTP_MAX_DATA_WAIT) {
|
||||
keepCurrentClient = true;
|
||||
|
||||
@@ -48,6 +48,8 @@ enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH };
|
||||
#define CONTENT_LENGTH_UNKNOWN ((size_t) -1)
|
||||
#define CONTENT_LENGTH_NOT_SET ((size_t) -2)
|
||||
|
||||
#define WEBSERVER_HAS_HOOK 1
|
||||
|
||||
class HTTPServer;
|
||||
|
||||
typedef struct {
|
||||
@@ -183,6 +185,25 @@ public:
|
||||
return _currentClient->write(file);
|
||||
}
|
||||
|
||||
// Hook
|
||||
enum ClientFuture { CLIENT_REQUEST_CAN_CONTINUE, CLIENT_REQUEST_IS_HANDLED, CLIENT_MUST_STOP, CLIENT_IS_GIVEN };
|
||||
typedef String(*ContentTypeFunction)(const String&);
|
||||
using HookFunction = std::function<ClientFuture(const String& method, const String& url, WiFiClient* client, ContentTypeFunction contentType)>;
|
||||
void addHook(HookFunction hook) {
|
||||
if (_hook) {
|
||||
auto previousHook = _hook;
|
||||
_hook = [previousHook, hook](const String & method, const String & url, WiFiClient * client, ContentTypeFunction contentType) {
|
||||
auto whatNow = previousHook(method, url, client, contentType);
|
||||
if (whatNow == CLIENT_REQUEST_CAN_CONTINUE) {
|
||||
return hook(method, url, client, contentType);
|
||||
}
|
||||
return whatNow;
|
||||
};
|
||||
} else {
|
||||
_hook = hook;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual size_t _currentClientWrite(const char* b, size_t l) {
|
||||
return _currentClient->write(b, l);
|
||||
@@ -193,7 +214,7 @@ protected:
|
||||
void _addRequestHandler(RequestHandler* handler);
|
||||
void _handleRequest();
|
||||
void _finalizeResponse();
|
||||
bool _parseRequest(WiFiClient* client);
|
||||
ClientFuture _parseRequest(WiFiClient* client);
|
||||
void _parseArguments(String data);
|
||||
static String _responseCodeToString(int code);
|
||||
bool _parseForm(WiFiClient* client, String boundary, uint32_t len);
|
||||
@@ -249,4 +270,6 @@ protected:
|
||||
String _snonce; // Store noance and opaque for future comparison
|
||||
String _sopaque;
|
||||
String _srealm; // Store the Auth realm between Calls
|
||||
|
||||
HookFunction _hook;
|
||||
};
|
||||
|
||||
@@ -78,7 +78,7 @@ static char* readBytesWithTimeout(WiFiClient* client, size_t maxLength, size_t&
|
||||
return buf;
|
||||
}
|
||||
|
||||
bool HTTPServer::_parseRequest(WiFiClient* client) {
|
||||
HTTPServer::ClientFuture HTTPServer::_parseRequest(WiFiClient* client) {
|
||||
// Read the first line of HTTP request
|
||||
String req = client->readStringUntil('\r');
|
||||
client->readStringUntil('\n');
|
||||
@@ -93,7 +93,7 @@ bool HTTPServer::_parseRequest(WiFiClient* client) {
|
||||
int addr_end = req.indexOf(' ', addr_start + 1);
|
||||
if (addr_start == -1 || addr_end == -1) {
|
||||
log_e("Invalid request: %s", req.c_str());
|
||||
return false;
|
||||
return CLIENT_MUST_STOP;
|
||||
}
|
||||
|
||||
String methodStr = req.substring(0, addr_start);
|
||||
@@ -110,6 +110,13 @@ bool HTTPServer::_parseRequest(WiFiClient* client) {
|
||||
_chunked = false;
|
||||
_clientContentLength = 0; // not known yet, or invalid
|
||||
|
||||
if (_hook) {
|
||||
auto whatNow = _hook(methodStr, url, client, mime::getContentType);
|
||||
if (whatNow != CLIENT_REQUEST_CAN_CONTINUE) {
|
||||
return whatNow;
|
||||
}
|
||||
}
|
||||
|
||||
HTTPMethod method = HTTP_ANY;
|
||||
size_t num_methods = sizeof(_http_method_str) / sizeof(const char *);
|
||||
for (size_t i = 0; i < num_methods; i++) {
|
||||
@@ -120,7 +127,7 @@ bool HTTPServer::_parseRequest(WiFiClient* client) {
|
||||
}
|
||||
if (method == HTTP_ANY) {
|
||||
log_e("Unknown HTTP Method: %s", methodStr.c_str());
|
||||
return false;
|
||||
return CLIENT_MUST_STOP;
|
||||
}
|
||||
_currentMethod = method;
|
||||
|
||||
@@ -186,7 +193,7 @@ bool HTTPServer::_parseRequest(WiFiClient* client) {
|
||||
char* plainBuf = readBytesWithTimeout(client, _clientContentLength, plainLength, HTTP_MAX_POST_WAIT);
|
||||
if ((int)plainLength < (int)_clientContentLength) {
|
||||
free(plainBuf);
|
||||
return false;
|
||||
return CLIENT_MUST_STOP;
|
||||
}
|
||||
if (_clientContentLength > 0) {
|
||||
if (isEncoded) {
|
||||
@@ -214,7 +221,7 @@ bool HTTPServer::_parseRequest(WiFiClient* client) {
|
||||
// it IS a form
|
||||
_parseArguments(searchStr);
|
||||
if (!_parseForm(client, boundaryStr, _clientContentLength)) {
|
||||
return false;
|
||||
return CLIENT_MUST_STOP;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -249,7 +256,7 @@ bool HTTPServer::_parseRequest(WiFiClient* client) {
|
||||
log_v("Request: %s", url.c_str());
|
||||
log_v(" Arguments: %s", searchStr.c_str());
|
||||
|
||||
return true;
|
||||
return CLIENT_REQUEST_CAN_CONTINUE;
|
||||
}
|
||||
|
||||
bool HTTPServer::_collectHeader(const char* headerName, const char* headerValue) {
|
||||
|
||||
@@ -113,7 +113,7 @@ void loop() {
|
||||
client.setTrustAnchors(&cert);
|
||||
fetchURL(&client, host, port, path);
|
||||
finish = millis();
|
||||
Serial.printf("Total time: %dms\n", finish - start);
|
||||
Serial.printf("Total time: %lums\n", finish - start);
|
||||
|
||||
BearSSL::Session session;
|
||||
client.setSession(&session);
|
||||
@@ -122,21 +122,21 @@ void loop() {
|
||||
client.setTrustAnchors(&cert);
|
||||
fetchURL(&client, host, port, path);
|
||||
finish = millis();
|
||||
Serial.printf("Total time: %dms\n", finish - start);
|
||||
Serial.printf("Total time: %lums\n", finish - start);
|
||||
|
||||
Serial.printf("Connecting with the just initialized session...");
|
||||
start = millis();
|
||||
client.setTrustAnchors(&cert);
|
||||
fetchURL(&client, host, port, path);
|
||||
finish = millis();
|
||||
Serial.printf("Total time: %dms\n", finish - start);
|
||||
Serial.printf("Total time: %lums\n", finish - start);
|
||||
|
||||
Serial.printf("Connecting again with the initialized session...");
|
||||
start = millis();
|
||||
client.setTrustAnchors(&cert);
|
||||
fetchURL(&client, host, port, path);
|
||||
finish = millis();
|
||||
Serial.printf("Total time: %dms\n", finish - start);
|
||||
Serial.printf("Total time: %lums\n", finish - start);
|
||||
|
||||
delay(10000); // Avoid DDOSing github
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ void fetchURL(BearSSL::WiFiClientSecure *client, const char *host, const uint16_
|
||||
} while (millis() < to);
|
||||
}
|
||||
client->stop();
|
||||
Serial.printf("BSSL stack used: %d\n-------\n\n", stack_thunk_get_max_usage());
|
||||
Serial.printf("BSSL stack used: %lu\n-------\n\n", stack_thunk_get_max_usage());
|
||||
}
|
||||
|
||||
void fetchNoConfig() {
|
||||
|
||||
@@ -24,7 +24,7 @@ const char *encToString(uint8_t enc) {
|
||||
|
||||
void loop() {
|
||||
delay(5000);
|
||||
Serial.printf("Beginning scan at %d\n", millis());
|
||||
Serial.printf("Beginning scan at %lu\n", millis());
|
||||
auto cnt = WiFi.scanNetworks();
|
||||
if (!cnt) {
|
||||
Serial.printf("No networks found\n");
|
||||
@@ -34,7 +34,7 @@ void loop() {
|
||||
for (auto i = 0; i < cnt; i++) {
|
||||
uint8_t bssid[6];
|
||||
WiFi.BSSID(i, bssid);
|
||||
Serial.printf("%32s %5s %17s %2d %4d\n", WiFi.SSID(i), encToString(WiFi.encryptionType(i)), macToString(bssid), WiFi.channel(i), WiFi.RSSI(i));
|
||||
Serial.printf("%32s %5s %17s %2d %4ld\n", WiFi.SSID(i), encToString(WiFi.encryptionType(i)), macToString(bssid), WiFi.channel(i), WiFi.RSSI(i));
|
||||
}
|
||||
}
|
||||
Serial.printf("\n--- Sleeping ---\n\n\n");
|
||||
|
||||
@@ -57,7 +57,8 @@ beginEnterprise KEYWORD2
|
||||
setHostname KEYWORD2
|
||||
end KEYWORD2
|
||||
getTime KEYWORD2
|
||||
lowPowerMode KEYWORD2
|
||||
aggressiveLowPowerMode KEYWORD2
|
||||
defaultLowPowerMode KEYWORD2
|
||||
noLowPowerMode KEYWORD2
|
||||
ping KEYWORD2
|
||||
beginMulticast KEYWORD2
|
||||
|
||||
@@ -99,6 +99,7 @@ int WiFiClass::begin(const char* ssid, const char *passphrase) {
|
||||
if (!_wifi.begin()) {
|
||||
return WL_IDLE_STATUS;
|
||||
}
|
||||
noLowPowerMode();
|
||||
// Enable CYW43 event debugging (make sure Debug Port is set)
|
||||
//cyw43_state.trace_flags = 0xffff;
|
||||
while (!_calledESP && ((millis() - start < (uint32_t)2 * _timeout)) && !connected()) {
|
||||
@@ -134,6 +135,7 @@ uint8_t WiFiClass::beginAP(const char *ssid, const char* passphrase) {
|
||||
if (!_wifi.begin()) {
|
||||
return WL_IDLE_STATUS;
|
||||
}
|
||||
noLowPowerMode();
|
||||
IPAddress gw = _wifi.gatewayIP();
|
||||
if (!gw.isSet()) {
|
||||
gw = IPAddress(192, 168, 42, 1);
|
||||
@@ -565,14 +567,19 @@ unsigned long WiFiClass::getTime() {
|
||||
return millis();
|
||||
}
|
||||
|
||||
void WiFiClass::lowPowerMode() {
|
||||
void WiFiClass::aggressiveLowPowerMode() {
|
||||
cyw43_wifi_pm(&cyw43_state, CYW43_AGGRESSIVE_PM);
|
||||
}
|
||||
|
||||
void WiFiClass::noLowPowerMode() {
|
||||
void WiFiClass::defaultLowPowerMode() {
|
||||
cyw43_wifi_pm(&cyw43_state, CYW43_DEFAULT_PM);
|
||||
}
|
||||
|
||||
// The difference between the default CYW43_DEFAULT_PM (0xA11142) and not low power (0xA11140) is that it changed from "Powersave mode on specified interface with High throughput" to "No Powersave mode". All other parameters stayed the same.
|
||||
void WiFiClass::noLowPowerMode() {
|
||||
cyw43_wifi_pm(&cyw43_state, 0xA11140);
|
||||
}
|
||||
|
||||
int WiFiClass::ping(const char* hostname, uint8_t ttl) {
|
||||
IPAddress ip;
|
||||
if (!hostByName(hostname, ip)) {
|
||||
|
||||
@@ -368,7 +368,8 @@ public:
|
||||
|
||||
unsigned long getTime();
|
||||
|
||||
void lowPowerMode();
|
||||
void aggressiveLowPowerMode();
|
||||
void defaultLowPowerMode();
|
||||
void noLowPowerMode();
|
||||
|
||||
int ping(const char* hostname, uint8_t ttl = 128);
|
||||
@@ -381,7 +382,7 @@ public:
|
||||
void feedWatchdog();
|
||||
|
||||
private:
|
||||
int _timeout = 10000;
|
||||
int _timeout = 15000;
|
||||
String _ssid;
|
||||
String _password;
|
||||
bool _wifiHWInitted = false;
|
||||
|
||||
@@ -78,7 +78,7 @@ uint8_t WiFiMulti::run(uint32_t to) {
|
||||
for (int i = 0; i < cnt; i++) {
|
||||
if (WiFi.RSSI(i) > maxRSSID) {
|
||||
for (auto j = _list.begin(); j != _list.end(); j++) {
|
||||
DEBUGV("[WIFIMULTI] Checking for '%s' at %d\n", WiFi.SSID(i), WiFi.RSSI(i));
|
||||
DEBUGV("[WIFIMULTI] Checking for '%s' at %ld\n", WiFi.SSID(i), WiFi.RSSI(i));
|
||||
if (!strcmp(j->ssid, WiFi.SSID(i))) {
|
||||
hit = j;
|
||||
maxRSSID = WiFi.RSSI(i);
|
||||
|
||||
@@ -184,8 +184,8 @@ static void dhcp_server_process(void *arg, struct udp_pcb *upcb, struct pbuf *p,
|
||||
(void)src_addr;
|
||||
(void)src_port;
|
||||
|
||||
// This is around 548 bytes
|
||||
dhcp_msg_t dhcp_msg;
|
||||
// This is around 548 bytes, so make static so it will live in the heap, not the stack (too large)
|
||||
static dhcp_msg_t dhcp_msg;
|
||||
|
||||
#define DHCP_MIN_SIZE (240 + 3)
|
||||
if (p->tot_len < DHCP_MIN_SIZE) {
|
||||
|
||||
@@ -374,12 +374,22 @@ public:
|
||||
return 0;
|
||||
}
|
||||
size_t sent = 0;
|
||||
uint8_t buff[256];
|
||||
while (stream.available()) {
|
||||
char b;
|
||||
b = stream.read();
|
||||
if (write(&b, 1)) {
|
||||
sent ++;
|
||||
// Stream only lets you read 1 byte at a time, so buffer in local copy
|
||||
size_t i;
|
||||
for (i = 0; (i < sizeof(buff)) && stream.available(); i++) {
|
||||
buff[i] = stream.read();
|
||||
}
|
||||
if (i) {
|
||||
// Send as a single packet
|
||||
int len = write((const char *)buff, i);
|
||||
sent += len;
|
||||
if (len != (int)i) {
|
||||
break; // Write error...
|
||||
}
|
||||
} else {
|
||||
// Out of data...
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -641,7 +651,7 @@ protected:
|
||||
|
||||
void _error(err_t err) {
|
||||
(void) err;
|
||||
DEBUGV(":er %d 0x%08x\r\n", (int) err, (uint32_t) _datasource);
|
||||
DEBUGV(":er %d 0x%08lx\r\n", (int) err, (uint32_t) _datasource);
|
||||
tcp_arg(_pcb, nullptr);
|
||||
tcp_sent(_pcb, nullptr);
|
||||
tcp_recv(_pcb, nullptr);
|
||||
|
||||
@@ -182,6 +182,9 @@ void TwoWire::onIRQ() {
|
||||
if (_onReceiveCallback && _buffLen) {
|
||||
_onReceiveCallback(_buffLen);
|
||||
}
|
||||
_buffLen = 0;
|
||||
_buffOff = 0;
|
||||
_slaveStartDet = false;
|
||||
_i2c->hw->clr_restart_det;
|
||||
}
|
||||
// STOP_DET
|
||||
|
||||
@@ -6,7 +6,7 @@ void setup() {
|
||||
uint32_t a = rp2040.getCycleCount();
|
||||
delay(1000);
|
||||
uint32_t b = rp2040.getCycleCount();
|
||||
Serial.printf("There are %d cycles in one second.\n\n\n", b - a);
|
||||
Serial.printf("There are %lu cycles in one second.\n\n\n", b - a);
|
||||
|
||||
delay(3000);
|
||||
|
||||
@@ -16,6 +16,6 @@ void setup() {
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.printf("%15u - %15llu\n", rp2040.getCycleCount(), rp2040.getCycleCount64());
|
||||
Serial.printf("%15lu - %15llu\n", rp2040.getCycleCount(), rp2040.getCycleCount64());
|
||||
delay(1500);
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "framework-arduinopico",
|
||||
"version": "1.20604.0",
|
||||
"version": "1.20701.0",
|
||||
"description": "Arduino Wiring-based Framework (RPi Pico RP2040)",
|
||||
"keywords": [
|
||||
"framework",
|
||||
|
||||
@@ -111,6 +111,8 @@ sed 's/^tools.uf2conv.network_cmd=.*//g' | \
|
||||
sed 's/^#tools.uf2conv.network_cmd=/tools.uf2conv.network_cmd=/g' | \
|
||||
sed 's/^tools.picoprobe.cmd=.*//g' | \
|
||||
sed 's/^#tools.picoprobe.cmd=/tools.picoprobe.cmd=/g' | \
|
||||
sed 's/^tools.picotool.cmd=.*//g' | \
|
||||
sed 's/^#tools.picotool.cmd=/tools.picotool.cmd=/g' | \
|
||||
sed 's/^tools.picodebug.cmd=.*//g' | \
|
||||
sed 's/^#tools.picodebug.cmd=/tools.picodebug.cmd=/g' | \
|
||||
sed 's/^discovery.rp2040.pattern=.*//g' | \
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
{
|
||||
"name": "Raspberry Pi Pico W"
|
||||
},
|
||||
{
|
||||
"name": "0xCB Helios"
|
||||
},
|
||||
{
|
||||
"name": "Adafruit Feather RP2040"
|
||||
},
|
||||
@@ -62,9 +65,6 @@
|
||||
{
|
||||
"name": "DatanoiseTV PicoADK"
|
||||
},
|
||||
{
|
||||
"name": "Degz Mizu"
|
||||
},
|
||||
{
|
||||
"name": "DeRuiLab FlyBoard2040Core"
|
||||
},
|
||||
@@ -110,6 +110,9 @@
|
||||
{
|
||||
"name": "Melopero Shake RP2040"
|
||||
},
|
||||
{
|
||||
"name": "nullbits Bit-C PRO"
|
||||
},
|
||||
{
|
||||
"name": "Pimoroni PGA2040"
|
||||
},
|
||||
@@ -128,6 +131,12 @@
|
||||
{
|
||||
"name": "Seeed XIAO RP2040"
|
||||
},
|
||||
{
|
||||
"name": "VCC-GND YD RP2040"
|
||||
},
|
||||
{
|
||||
"name": "Viyalab Mizu RP2040"
|
||||
},
|
||||
{
|
||||
"name": "Waveshare RP2040 Zero"
|
||||
},
|
||||
@@ -162,22 +171,22 @@
|
||||
"toolsDependencies": [
|
||||
{
|
||||
"packager": "rp2040",
|
||||
"version": "1.4.0-c-0196c06",
|
||||
"version": "1.5.0-a-5007782",
|
||||
"name": "pqt-gcc"
|
||||
},
|
||||
{
|
||||
"packager": "rp2040",
|
||||
"version": "1.4.0-c-0196c06",
|
||||
"version": "1.5.0-a-5007782",
|
||||
"name": "pqt-mklittlefs"
|
||||
},
|
||||
{
|
||||
"packager": "rp2040",
|
||||
"version": "1.4.0-c-0196c06",
|
||||
"version": "1.5.0-a-5007782",
|
||||
"name": "pqt-elf2uf2"
|
||||
},
|
||||
{
|
||||
"packager": "rp2040",
|
||||
"version": "1.4.0-c-0196c06",
|
||||
"version": "1.5.0-a-5007782",
|
||||
"name": "pqt-pioasm"
|
||||
},
|
||||
{
|
||||
@@ -187,8 +196,13 @@
|
||||
},
|
||||
{
|
||||
"packager": "rp2040",
|
||||
"version": "1.4.0-c-0196c06",
|
||||
"version": "1.5.0-a-5007782",
|
||||
"name": "pqt-openocd"
|
||||
},
|
||||
{
|
||||
"packager": "rp2040",
|
||||
"version": "1.5.0-a-03f2812",
|
||||
"name": "pqt-picotool"
|
||||
}
|
||||
],
|
||||
"help": {
|
||||
@@ -198,57 +212,112 @@
|
||||
],
|
||||
"tools": [
|
||||
{
|
||||
"version": "1.4.0-c-0196c06",
|
||||
"version": "1.5.0-a-03f2812",
|
||||
"name": "pqt-picotool",
|
||||
"systems": [
|
||||
{
|
||||
"host": "aarch64-linux-gnu",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/aarch64-linux-gnu.picotool-03f2812.230113.tar.gz",
|
||||
"archiveFileName": "aarch64-linux-gnu.picotool-03f2812.230113.tar.gz",
|
||||
"checksum": "SHA-256:3816240a6aef8da61895227df3b1b5d7a7f95456a9e7737e25252886f649d2f3",
|
||||
"size": "171807"
|
||||
},
|
||||
{
|
||||
"host": "arm-linux-gnueabihf",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/arm-linux-gnueabihf.picotool-03f2812.230113.tar.gz",
|
||||
"archiveFileName": "arm-linux-gnueabihf.picotool-03f2812.230113.tar.gz",
|
||||
"checksum": "SHA-256:454e1a0ce9a546626bca73cdff74dfb0e06bf6603b13d5a3a4a4f0c0968fd65d",
|
||||
"size": "152809"
|
||||
},
|
||||
{
|
||||
"host": "i686-pc-linux-gnu",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/i686-linux-gnu.picotool-03f2812.230113.tar.gz",
|
||||
"archiveFileName": "i686-linux-gnu.picotool-03f2812.230113.tar.gz",
|
||||
"checksum": "SHA-256:fee7f93d65bb3db42a9fcf5683e2b038ab13875b1d5bedb304facb92e6469a37",
|
||||
"size": "183571"
|
||||
},
|
||||
{
|
||||
"host": "i686-mingw32",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/i686-w64-mingw32.picotool-03f2812.230112.zip",
|
||||
"archiveFileName": "i686-w64-mingw32.picotool-03f2812.230112.zip",
|
||||
"checksum": "SHA-256:8a37063d44e306b9315618962cd8dc14b815e998ba960a38099e45bb14bcda90",
|
||||
"size": "352766"
|
||||
},
|
||||
{
|
||||
"host": "x86_64-apple-darwin",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/x86_64-apple-darwin14.picotool-03f2812.230112.tar.gz",
|
||||
"archiveFileName": "x86_64-apple-darwin14.picotool-03f2812.230112.tar.gz",
|
||||
"checksum": "SHA-256:5b2f77ee33a7a757546ab526d3e477300bf01e787e645f64aa86daac6b19a510",
|
||||
"size": "852449"
|
||||
},
|
||||
{
|
||||
"host": "x86_64-pc-linux-gnu",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/x86_64-linux-gnu.picotool-03f2812.230113.tar.gz",
|
||||
"archiveFileName": "x86_64-linux-gnu.picotool-03f2812.230113.tar.gz",
|
||||
"checksum": "SHA-256:c6168c6948ec781ead9637ecddc357004aa0fbaca19a634a48fbfc4e48860d1a",
|
||||
"size": "131509"
|
||||
},
|
||||
{
|
||||
"host": "x86_64-mingw32",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/x86_64-w64-mingw32.picotool-03f2812.230112.zip",
|
||||
"archiveFileName": "x86_64-w64-mingw32.picotool-03f2812.230112.zip",
|
||||
"checksum": "SHA-256:a5bded215b886bc8dc35520d2ae696c56e370ef6e92ca7d3a47fe82db362afa7",
|
||||
"size": "337118"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.5.0-a-5007782",
|
||||
"name": "pqt-openocd",
|
||||
"systems": [
|
||||
{
|
||||
"host": "aarch64-linux-gnu",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/aarch64-linux-gnu.openocd-e3428fadb.220714.tar.gz",
|
||||
"archiveFileName": "aarch64-linux-gnu.openocd-e3428fadb.220714.tar.gz",
|
||||
"checksum": "SHA-256:8da4fd922fd7392e87c0057b193e67962606cd13450af935c9846992f2bce916",
|
||||
"size": "5607131"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/aarch64-linux-gnu.openocd-228ede43d.221223.tar.gz",
|
||||
"archiveFileName": "aarch64-linux-gnu.openocd-228ede43d.221223.tar.gz",
|
||||
"checksum": "SHA-256:aa3f7a24af6afca5bacd9172f943f59d1f7ca9a3525e7e9c445dba9f8c6c8b68",
|
||||
"size": "5807317"
|
||||
},
|
||||
{
|
||||
"host": "arm-linux-gnueabihf",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/arm-linux-gnueabihf.openocd-e3428fadb.220714.tar.gz",
|
||||
"archiveFileName": "arm-linux-gnueabihf.openocd-e3428fadb.220714.tar.gz",
|
||||
"checksum": "SHA-256:ce7650961c8200fe2cc2d4a88efc952fe1bb7b4175f2cf650a9a7d992ae08298",
|
||||
"size": "5344149"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/arm-linux-gnueabihf.openocd-228ede43d.221223.tar.gz",
|
||||
"archiveFileName": "arm-linux-gnueabihf.openocd-228ede43d.221223.tar.gz",
|
||||
"checksum": "SHA-256:40a623e198eccafaec210e3f1b425369e6fe79cef0d94b7a910d63223c37cc50",
|
||||
"size": "5538300"
|
||||
},
|
||||
{
|
||||
"host": "i686-pc-linux-gnu",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/i686-linux-gnu.openocd-e3428fadb.220714.tar.gz",
|
||||
"archiveFileName": "i686-linux-gnu.openocd-e3428fadb.220714.tar.gz",
|
||||
"checksum": "SHA-256:9391ae74c6474f685f45ccefb82e18f382f1a59f3e13e2b22fe00967264d2482",
|
||||
"size": "5168334"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/i686-linux-gnu.openocd-228ede43d.221223.tar.gz",
|
||||
"archiveFileName": "i686-linux-gnu.openocd-228ede43d.221223.tar.gz",
|
||||
"checksum": "SHA-256:b80eb819e302130bde5ed303f23645e615bc2440303223d924742ee89dc6d477",
|
||||
"size": "5341706"
|
||||
},
|
||||
{
|
||||
"host": "i686-mingw32",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/i686-w64-mingw32.openocd-e3428fadb.220714.zip",
|
||||
"archiveFileName": "i686-w64-mingw32.openocd-e3428fadb.220714.zip",
|
||||
"checksum": "SHA-256:f4b7803527a0a587fe95e52bcf056fcbe58d64b78705edd38810655d3abee1fe",
|
||||
"size": "2156831"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/i686-w64-mingw32.openocd-228ede43d.221223.zip",
|
||||
"archiveFileName": "i686-w64-mingw32.openocd-228ede43d.221223.zip",
|
||||
"checksum": "SHA-256:02d9fba9d4e66de31cd99af0ade3a6150d258d034aa5f85f7f4601c849b8bc80",
|
||||
"size": "2207879"
|
||||
},
|
||||
{
|
||||
"host": "x86_64-apple-darwin",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/x86_64-apple-darwin14.openocd-e3428fadb.220714.tar.gz",
|
||||
"archiveFileName": "x86_64-apple-darwin14.openocd-e3428fadb.220714.tar.gz",
|
||||
"checksum": "SHA-256:4e6a8da43758941478e6740e023ea5be051f09bf92e49d65c2b54a1ca4a2660c",
|
||||
"size": "2002508"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/x86_64-apple-darwin14.openocd-228ede43d.221223.tar.gz",
|
||||
"archiveFileName": "x86_64-apple-darwin14.openocd-228ede43d.221223.tar.gz",
|
||||
"checksum": "SHA-256:f92b87bd2ec81260c4c76e9e74ede41888d943ac0a97c11ec5f579d450ba72b1",
|
||||
"size": "2599689"
|
||||
},
|
||||
{
|
||||
"host": "x86_64-pc-linux-gnu",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/x86_64-linux-gnu.openocd-e3428fadb.220714.tar.gz",
|
||||
"archiveFileName": "x86_64-linux-gnu.openocd-e3428fadb.220714.tar.gz",
|
||||
"checksum": "SHA-256:2518c6451c0e4c148c8c6cb9330e216075177003b064c509b168f42a44a6eb5a",
|
||||
"size": "5540574"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/x86_64-linux-gnu.openocd-228ede43d.221223.tar.gz",
|
||||
"archiveFileName": "x86_64-linux-gnu.openocd-228ede43d.221223.tar.gz",
|
||||
"checksum": "SHA-256:cf4c30151c9f4583964579f11ffbc66229508b21d83d2f1d7c87eb28d1f0cca9",
|
||||
"size": "5727971"
|
||||
},
|
||||
{
|
||||
"host": "x86_64-mingw32",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/x86_64-w64-mingw32.openocd-e3428fadb.220714.zip",
|
||||
"archiveFileName": "x86_64-w64-mingw32.openocd-e3428fadb.220714.zip",
|
||||
"checksum": "SHA-256:c2da993d74152ec8ca80b350ede77ee94942518081a48dafd7a3b49631778e75",
|
||||
"size": "2156831"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/x86_64-w64-mingw32.openocd-228ede43d.221223.zip",
|
||||
"archiveFileName": "x86_64-w64-mingw32.openocd-228ede43d.221223.zip",
|
||||
"checksum": "SHA-256:5f0c4f67f0bba6cf3b9968ba27796fe98ef8787e56f0a160dede8d05d578586c",
|
||||
"size": "2168940"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -315,222 +384,222 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.4.0-c-0196c06",
|
||||
"version": "1.5.0-a-5007782",
|
||||
"name": "pqt-gcc",
|
||||
"systems": [
|
||||
{
|
||||
"host": "aarch64-linux-gnu",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/aarch64-linux-gnu.arm-none-eabi-0196c06.220714.tar.gz",
|
||||
"archiveFileName": "aarch64-linux-gnu.arm-none-eabi-0196c06.220714.tar.gz",
|
||||
"checksum": "SHA-256:0bc906c2eb4b3999285ea8a71ec0c95a20a21f2ff46b1bdd264f2fd3c30fe580",
|
||||
"size": "77889379"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/aarch64-linux-gnu.arm-none-eabi-5007782.221223.tar.gz",
|
||||
"archiveFileName": "aarch64-linux-gnu.arm-none-eabi-5007782.221223.tar.gz",
|
||||
"checksum": "SHA-256:baff843fce38393565babf81ecfddfab17df8046d901faad3c5f93ee072d395b",
|
||||
"size": "81474410"
|
||||
},
|
||||
{
|
||||
"host": "arm-linux-gnueabihf",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/arm-linux-gnueabihf.arm-none-eabi-0196c06.220714.tar.gz",
|
||||
"archiveFileName": "arm-linux-gnueabihf.arm-none-eabi-0196c06.220714.tar.gz",
|
||||
"checksum": "SHA-256:2bf9f691d1fa97cea3b835a364fc24d06102181725001a00fe3e77526f90e2bd",
|
||||
"size": "73342377"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/arm-linux-gnueabihf.arm-none-eabi-5007782.221223.tar.gz",
|
||||
"archiveFileName": "arm-linux-gnueabihf.arm-none-eabi-5007782.221223.tar.gz",
|
||||
"checksum": "SHA-256:35144f4aded62ab45ab7ddabf63c189af70b57534438cf00281c11b5ef90d316",
|
||||
"size": "76138344"
|
||||
},
|
||||
{
|
||||
"host": "i686-pc-linux-gnu",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/i686-linux-gnu.arm-none-eabi-0196c06.220714.tar.gz",
|
||||
"archiveFileName": "i686-linux-gnu.arm-none-eabi-0196c06.220714.tar.gz",
|
||||
"checksum": "SHA-256:e0fe347f2602247ffbc16bb7b74653fdb78c4f75b90037f2e82fc5498b5d1cb9",
|
||||
"size": "79511866"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/i686-linux-gnu.arm-none-eabi-5007782.221223.tar.gz",
|
||||
"archiveFileName": "i686-linux-gnu.arm-none-eabi-5007782.221223.tar.gz",
|
||||
"checksum": "SHA-256:d1d5121c4a5e4fa42047daf58c112a90beeb3533d1f133f4928db4b337030351",
|
||||
"size": "83712710"
|
||||
},
|
||||
{
|
||||
"host": "i686-mingw32",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/i686-w64-mingw32.arm-none-eabi-0196c06.220714.zip",
|
||||
"archiveFileName": "i686-w64-mingw32.arm-none-eabi-0196c06.220714.zip",
|
||||
"checksum": "SHA-256:d1ee0f00c97dc50dc06487d6fb6b5dd94bbb3cf1579c975bcd23d7a7922e53b7",
|
||||
"size": "82626868"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/i686-w64-mingw32.arm-none-eabi-5007782.221223.zip",
|
||||
"archiveFileName": "i686-w64-mingw32.arm-none-eabi-5007782.221223.zip",
|
||||
"checksum": "SHA-256:71e0858689b644e5b0aaa07ec5257919ab292ff9404bfc9c52dc31593828f391",
|
||||
"size": "85628083"
|
||||
},
|
||||
{
|
||||
"host": "x86_64-apple-darwin",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/x86_64-apple-darwin14.arm-none-eabi-0196c06.220714.tar.gz",
|
||||
"archiveFileName": "x86_64-apple-darwin14.arm-none-eabi-0196c06.220714.tar.gz",
|
||||
"checksum": "SHA-256:1b030451a5a6ddf42ac796c46b2f077f90f6d56586f4537a3486ce47670996cd",
|
||||
"size": "82119254"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/x86_64-apple-darwin14.arm-none-eabi-5007782.221223.tar.gz",
|
||||
"archiveFileName": "x86_64-apple-darwin14.arm-none-eabi-5007782.221223.tar.gz",
|
||||
"checksum": "SHA-256:ad984927785d17ddd34cfc7bc76d4d1371d6f6905d8708d3535a261b244b32d9",
|
||||
"size": "84968911"
|
||||
},
|
||||
{
|
||||
"host": "x86_64-pc-linux-gnu",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/x86_64-linux-gnu.arm-none-eabi-0196c06.220714.tar.gz",
|
||||
"archiveFileName": "x86_64-linux-gnu.arm-none-eabi-0196c06.220714.tar.gz",
|
||||
"checksum": "SHA-256:73d032ba42d33f6e9c1d5de79b73bf5e9740b8e6b7fee37de3db03814c553694",
|
||||
"size": "80363125"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/x86_64-linux-gnu.arm-none-eabi-5007782.221223.tar.gz",
|
||||
"archiveFileName": "x86_64-linux-gnu.arm-none-eabi-5007782.221223.tar.gz",
|
||||
"checksum": "SHA-256:591af0c27b08b31d11b3135a56bbd3c447cd9e6cac2987cdb3ab25a86d7eb3e3",
|
||||
"size": "83660106"
|
||||
},
|
||||
{
|
||||
"host": "x86_64-mingw32",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/x86_64-w64-mingw32.arm-none-eabi-0196c06.220714.zip",
|
||||
"archiveFileName": "x86_64-w64-mingw32.arm-none-eabi-0196c06.220714.zip",
|
||||
"checksum": "SHA-256:3f67e15adbe49ebaee72734e9d469bd6b34fd1be30ccdb6e55a1e05a0be4e1db",
|
||||
"size": "86064664"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/x86_64-w64-mingw32.arm-none-eabi-5007782.221223.zip",
|
||||
"archiveFileName": "x86_64-w64-mingw32.arm-none-eabi-5007782.221223.zip",
|
||||
"checksum": "SHA-256:f2cf872ce5bd07efa2cfa044de28458034be9466f5ccae4e7e9cd229aabefa11",
|
||||
"size": "88351413"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.4.0-c-0196c06",
|
||||
"version": "1.5.0-a-5007782",
|
||||
"name": "pqt-elf2uf2",
|
||||
"systems": [
|
||||
{
|
||||
"host": "aarch64-linux-gnu",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/aarch64-linux-gnu.elf2uf2-2e6142b.220714.tar.gz",
|
||||
"archiveFileName": "aarch64-linux-gnu.elf2uf2-2e6142b.220714.tar.gz",
|
||||
"checksum": "SHA-256:14d7c1be08151bcddcc19fdb5741bf52b435f08f52daff2d498a07c5ebfb5bfb",
|
||||
"size": "82682"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/aarch64-linux-gnu.elf2uf2-2e6142b.221223.tar.gz",
|
||||
"archiveFileName": "aarch64-linux-gnu.elf2uf2-2e6142b.221223.tar.gz",
|
||||
"checksum": "SHA-256:266151dc2523ec707b88256e7220a5e89c819ad1e134a7f492cf9fbe07ccf8a1",
|
||||
"size": "82684"
|
||||
},
|
||||
{
|
||||
"host": "arm-linux-gnueabihf",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/arm-linux-gnueabihf.elf2uf2-2e6142b.220714.tar.gz",
|
||||
"archiveFileName": "arm-linux-gnueabihf.elf2uf2-2e6142b.220714.tar.gz",
|
||||
"checksum": "SHA-256:31b790b5a19bbb5950212098aa42be43702fb4db50aa541c9f40b2320e2280be",
|
||||
"size": "55991"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/arm-linux-gnueabihf.elf2uf2-2e6142b.221223.tar.gz",
|
||||
"archiveFileName": "arm-linux-gnueabihf.elf2uf2-2e6142b.221223.tar.gz",
|
||||
"checksum": "SHA-256:e30f1243259361a635a21d69e81bf2d913e194882159eba8c652d5b45cf2591b",
|
||||
"size": "55998"
|
||||
},
|
||||
{
|
||||
"host": "i686-pc-linux-gnu",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/i686-linux-gnu.elf2uf2-2e6142b.220714.tar.gz",
|
||||
"archiveFileName": "i686-linux-gnu.elf2uf2-2e6142b.220714.tar.gz",
|
||||
"checksum": "SHA-256:a3dd93861857fd81f8c9ed01509a0af012f446f23f405bc696f6a4d2f344262c",
|
||||
"size": "91386"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/i686-linux-gnu.elf2uf2-2e6142b.221223.tar.gz",
|
||||
"archiveFileName": "i686-linux-gnu.elf2uf2-2e6142b.221223.tar.gz",
|
||||
"checksum": "SHA-256:17ebb211916bc74a9cd3263f6d5e9721bb91d54a836c4369c1e01e9147e4f477",
|
||||
"size": "91387"
|
||||
},
|
||||
{
|
||||
"host": "i686-mingw32",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/i686-w64-mingw32.elf2uf2-2e6142b.220714.zip",
|
||||
"archiveFileName": "i686-w64-mingw32.elf2uf2-2e6142b.220714.zip",
|
||||
"checksum": "SHA-256:25d6a934643074590c3006468ec051f62d02298abbff27d7e3643ee147e818e0",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/i686-w64-mingw32.elf2uf2-2e6142b.221223.zip",
|
||||
"archiveFileName": "i686-w64-mingw32.elf2uf2-2e6142b.221223.zip",
|
||||
"checksum": "SHA-256:80f740ffdb0a1812212f631598060216361b1a8e55d2092e729afee3b3b1ad95",
|
||||
"size": "71949"
|
||||
},
|
||||
{
|
||||
"host": "x86_64-apple-darwin",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/x86_64-apple-darwin14.elf2uf2-2e6142b.220714.tar.gz",
|
||||
"archiveFileName": "x86_64-apple-darwin14.elf2uf2-2e6142b.220714.tar.gz",
|
||||
"checksum": "SHA-256:f2743d086d1992599d656aaa9a38a736d443bdc96619fd5bcabdc7fcb05225c8",
|
||||
"size": "86600"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/x86_64-apple-darwin14.elf2uf2-2e6142b.221223.tar.gz",
|
||||
"archiveFileName": "x86_64-apple-darwin14.elf2uf2-2e6142b.221223.tar.gz",
|
||||
"checksum": "SHA-256:bebf3f984c59b36cfdc1a5b41bc3368c674c03eea54586419872c0ce2ad71430",
|
||||
"size": "86604"
|
||||
},
|
||||
{
|
||||
"host": "x86_64-pc-linux-gnu",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/x86_64-linux-gnu.elf2uf2-2e6142b.220714.tar.gz",
|
||||
"archiveFileName": "x86_64-linux-gnu.elf2uf2-2e6142b.220714.tar.gz",
|
||||
"checksum": "SHA-256:5b0e79bfd9d7e5f4d5e5cb0e6e2b685e10fdf3071139c00bf6274a5ca9b9717e",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/x86_64-linux-gnu.elf2uf2-2e6142b.221223.tar.gz",
|
||||
"archiveFileName": "x86_64-linux-gnu.elf2uf2-2e6142b.221223.tar.gz",
|
||||
"checksum": "SHA-256:d7d0e77a8b4c5cf879bb92d20bbfb213d568fec86fb96fc08c60bd9a3fb38712",
|
||||
"size": "82339"
|
||||
},
|
||||
{
|
||||
"host": "x86_64-mingw32",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/x86_64-w64-mingw32.elf2uf2-2e6142b.220714.zip",
|
||||
"archiveFileName": "x86_64-w64-mingw32.elf2uf2-2e6142b.220714.zip",
|
||||
"checksum": "SHA-256:f09e022cbb1fe73315ccf16a2af6bc687b64d0c44066f801fb9dee8326e941b1",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/x86_64-w64-mingw32.elf2uf2-2e6142b.221223.zip",
|
||||
"archiveFileName": "x86_64-w64-mingw32.elf2uf2-2e6142b.221223.zip",
|
||||
"checksum": "SHA-256:25840973640fd28817a37a4213dfbad81da91a7b75a1ecb14cdee56c2bd1a8ee",
|
||||
"size": "80399"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.4.0-c-0196c06",
|
||||
"version": "1.5.0-a-5007782",
|
||||
"name": "pqt-pioasm",
|
||||
"systems": [
|
||||
{
|
||||
"host": "aarch64-linux-gnu",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/aarch64-linux-gnu.pioasm-2e6142b.220714.tar.gz",
|
||||
"archiveFileName": "aarch64-linux-gnu.pioasm-2e6142b.220714.tar.gz",
|
||||
"checksum": "SHA-256:d4ac4b39841984178babd75f5c09cad87e06bd2fb18cf04e40f561d72e604a5d",
|
||||
"size": "453440"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/aarch64-linux-gnu.pioasm-2e6142b.221223.tar.gz",
|
||||
"archiveFileName": "aarch64-linux-gnu.pioasm-2e6142b.221223.tar.gz",
|
||||
"checksum": "SHA-256:38ae80646805781c1df2264ae6b8ff41cc0dbfcefd4dfaae9f5e355bce4890c7",
|
||||
"size": "453442"
|
||||
},
|
||||
{
|
||||
"host": "arm-linux-gnueabihf",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/arm-linux-gnueabihf.pioasm-2e6142b.220714.tar.gz",
|
||||
"archiveFileName": "arm-linux-gnueabihf.pioasm-2e6142b.220714.tar.gz",
|
||||
"checksum": "SHA-256:568ebdd7b1a47c6dbf45793f9bf019855a1b14182fbeb7ed7e342979c6632eef",
|
||||
"size": "360675"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/arm-linux-gnueabihf.pioasm-2e6142b.221223.tar.gz",
|
||||
"archiveFileName": "arm-linux-gnueabihf.pioasm-2e6142b.221223.tar.gz",
|
||||
"checksum": "SHA-256:3ab67c8b0f40806e8e2a53fe22b274f3b79ccc12189ab80dc2db3b4e80e34de2",
|
||||
"size": "360686"
|
||||
},
|
||||
{
|
||||
"host": "i686-pc-linux-gnu",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/i686-linux-gnu.pioasm-2e6142b.220714.tar.gz",
|
||||
"archiveFileName": "i686-linux-gnu.pioasm-2e6142b.220714.tar.gz",
|
||||
"checksum": "SHA-256:2abac2bef690f7ffadfcf74af4832bfcc0e212606efbed21b385bda0517554ef",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/i686-linux-gnu.pioasm-2e6142b.221223.tar.gz",
|
||||
"archiveFileName": "i686-linux-gnu.pioasm-2e6142b.221223.tar.gz",
|
||||
"checksum": "SHA-256:ed5d190b3726caa58a63b08785aff17f6f412810cc7bef21405a6342717e6c6b",
|
||||
"size": "511442"
|
||||
},
|
||||
{
|
||||
"host": "i686-mingw32",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/i686-w64-mingw32.pioasm-2e6142b.220714.zip",
|
||||
"archiveFileName": "i686-w64-mingw32.pioasm-2e6142b.220714.zip",
|
||||
"checksum": "SHA-256:0b1336b999a31d164248248d37fa550aa14a06dae8ef5b215818c5f0597ca80b",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/i686-w64-mingw32.pioasm-2e6142b.221223.zip",
|
||||
"archiveFileName": "i686-w64-mingw32.pioasm-2e6142b.221223.zip",
|
||||
"checksum": "SHA-256:713be116f8fbdd7a77ae294f2b8a72fcfcb0bd43f013124bde6e569dd284ad43",
|
||||
"size": "385882"
|
||||
},
|
||||
{
|
||||
"host": "x86_64-apple-darwin",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/x86_64-apple-darwin14.pioasm-2e6142b.220714.tar.gz",
|
||||
"archiveFileName": "x86_64-apple-darwin14.pioasm-2e6142b.220714.tar.gz",
|
||||
"checksum": "SHA-256:7057ba222a54ff9bd43a6a4232a6355100f29e0cc20d3facb5d600180f418e68",
|
||||
"size": "480538"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/x86_64-apple-darwin14.pioasm-2e6142b.221223.tar.gz",
|
||||
"archiveFileName": "x86_64-apple-darwin14.pioasm-2e6142b.221223.tar.gz",
|
||||
"checksum": "SHA-256:16feb332e66b8ed7f911eceb153484565d1947a87eb7c9db774bd38a9a7b0043",
|
||||
"size": "480539"
|
||||
},
|
||||
{
|
||||
"host": "x86_64-pc-linux-gnu",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/x86_64-linux-gnu.pioasm-2e6142b.220714.tar.gz",
|
||||
"archiveFileName": "x86_64-linux-gnu.pioasm-2e6142b.220714.tar.gz",
|
||||
"checksum": "SHA-256:30b367e8d2cd4ccdf151e728b4137bad1d993c42f0d640307e27d5fc55fce2c0",
|
||||
"size": "458691"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/x86_64-linux-gnu.pioasm-2e6142b.221223.tar.gz",
|
||||
"archiveFileName": "x86_64-linux-gnu.pioasm-2e6142b.221223.tar.gz",
|
||||
"checksum": "SHA-256:367b14d59bb5394c068bacd1ab3dfd77116208ad28213af01372d5a93bb0ead3",
|
||||
"size": "458693"
|
||||
},
|
||||
{
|
||||
"host": "x86_64-mingw32",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/x86_64-w64-mingw32.pioasm-2e6142b.220714.zip",
|
||||
"archiveFileName": "x86_64-w64-mingw32.pioasm-2e6142b.220714.zip",
|
||||
"checksum": "SHA-256:b7b340862e673f9e57287f02219b2d56094a497fe9d8c451acf28aaef721e038",
|
||||
"size": "409101"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/x86_64-w64-mingw32.pioasm-2e6142b.221223.zip",
|
||||
"archiveFileName": "x86_64-w64-mingw32.pioasm-2e6142b.221223.zip",
|
||||
"checksum": "SHA-256:c18d5499308c4938b1c2230724f3adb2de8071583b9185b77705747555c9a50f",
|
||||
"size": "409100"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.4.0-c-0196c06",
|
||||
"version": "1.5.0-a-5007782",
|
||||
"name": "pqt-mklittlefs",
|
||||
"systems": [
|
||||
{
|
||||
"host": "aarch64-linux-gnu",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/aarch64-linux-gnu.mklittlefs-affa497.220714.tar.gz",
|
||||
"archiveFileName": "aarch64-linux-gnu.mklittlefs-affa497.220714.tar.gz",
|
||||
"checksum": "SHA-256:ad0af70d568e10a9a2a74c6e0a3cf8659a47b80fbf594b01731579bc21a5c212",
|
||||
"size": "47287"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/aarch64-linux-gnu.mklittlefs-30b7fc1.221223.tar.gz",
|
||||
"archiveFileName": "aarch64-linux-gnu.mklittlefs-30b7fc1.221223.tar.gz",
|
||||
"checksum": "SHA-256:6e20ef3f0c8f4eedd18377cd8e7626a724b3c11ce914e66e23fe021493b519b6",
|
||||
"size": "60993"
|
||||
},
|
||||
{
|
||||
"host": "arm-linux-gnueabihf",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/arm-linux-gnueabihf.mklittlefs-affa497.220714.tar.gz",
|
||||
"archiveFileName": "arm-linux-gnueabihf.mklittlefs-affa497.220714.tar.gz",
|
||||
"checksum": "SHA-256:f8f1a1dd7b02220f11cf0f6a6c88692f7e056eb245fbc41017c33d333751980e",
|
||||
"size": "40826"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/arm-linux-gnueabihf.mklittlefs-30b7fc1.221223.tar.gz",
|
||||
"archiveFileName": "arm-linux-gnueabihf.mklittlefs-30b7fc1.221223.tar.gz",
|
||||
"checksum": "SHA-256:092501f939d78625f876644a5c1d3264f2310e1798ea6d12a2845274a50a0b54",
|
||||
"size": "51573"
|
||||
},
|
||||
{
|
||||
"host": "i686-pc-linux-gnu",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/i686-linux-gnu.mklittlefs-affa497.220714.tar.gz",
|
||||
"archiveFileName": "i686-linux-gnu.mklittlefs-affa497.220714.tar.gz",
|
||||
"checksum": "SHA-256:4d10a2bc6a8fa41a7e5642aa30128f797f84b099e1bb2282c6c2d62a06e81e21",
|
||||
"size": "50925"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/i686-linux-gnu.mklittlefs-30b7fc1.221223.tar.gz",
|
||||
"archiveFileName": "i686-linux-gnu.mklittlefs-30b7fc1.221223.tar.gz",
|
||||
"checksum": "SHA-256:c2d8b5d8c903b024ae88bd5a97d9759049fbc1e7ab84ea19ea6ea2546f0aa496",
|
||||
"size": "67720"
|
||||
},
|
||||
{
|
||||
"host": "i686-mingw32",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/i686-w64-mingw32.mklittlefs-affa497.220714.zip",
|
||||
"archiveFileName": "i686-w64-mingw32.mklittlefs-affa497.220714.zip",
|
||||
"checksum": "SHA-256:71117412351ea2486848848d76f4d1e3c7a507a4d7546f6b0646bbc10c96d557",
|
||||
"size": "334062"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/i686-w64-mingw32.mklittlefs-30b7fc1.221223.zip",
|
||||
"archiveFileName": "i686-w64-mingw32.mklittlefs-30b7fc1.221223.zip",
|
||||
"checksum": "SHA-256:9f66d5b6bdfce13b1c246ae2d8abfcab623828337a16a1f2571eeed1b71096b9",
|
||||
"size": "345497"
|
||||
},
|
||||
{
|
||||
"host": "x86_64-apple-darwin",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/x86_64-apple-darwin14.mklittlefs-affa497.220714.tar.gz",
|
||||
"archiveFileName": "x86_64-apple-darwin14.mklittlefs-affa497.220714.tar.gz",
|
||||
"checksum": "SHA-256:b3d0c30f942982337cd4c0e4f70855f8eabcf1d378eac78465c7fee39ab5915f",
|
||||
"size": "365780"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/x86_64-apple-darwin14.mklittlefs-30b7fc1.221223.tar.gz",
|
||||
"archiveFileName": "x86_64-apple-darwin14.mklittlefs-30b7fc1.221223.tar.gz",
|
||||
"checksum": "SHA-256:1f7e675700ff4b5de21d1ea37a0969683ad54685d94bf0b42de13c26aa42f13f",
|
||||
"size": "377123"
|
||||
},
|
||||
{
|
||||
"host": "x86_64-pc-linux-gnu",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/x86_64-linux-gnu.mklittlefs-affa497.220714.tar.gz",
|
||||
"archiveFileName": "x86_64-linux-gnu.mklittlefs-affa497.220714.tar.gz",
|
||||
"checksum": "SHA-256:9241e8e642748048bd32c7ccf4a8200e8bc104d6e328fe7437e8727866f29d94",
|
||||
"size": "49787"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/x86_64-linux-gnu.mklittlefs-30b7fc1.221223.tar.gz",
|
||||
"archiveFileName": "x86_64-linux-gnu.mklittlefs-30b7fc1.221223.tar.gz",
|
||||
"checksum": "SHA-256:2773b0e7a5af33052b84959893cd9e7f3223236730f2109546399ec52d6a7a70",
|
||||
"size": "62741"
|
||||
},
|
||||
{
|
||||
"host": "x86_64-mingw32",
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.4.0-c/x86_64-w64-mingw32.mklittlefs-affa497.220714.zip",
|
||||
"archiveFileName": "x86_64-w64-mingw32.mklittlefs-affa497.220714.zip",
|
||||
"checksum": "SHA-256:2f39dd727e9cb61dbc631d00f543a99b6224fb24ffde0dff0cb2b70c9561ae69",
|
||||
"size": "347612"
|
||||
"url": "https://github.com/earlephilhower/pico-quick-toolchain/releases/download/1.5.0-a/x86_64-w64-mingw32.mklittlefs-30b7fc1.221223.zip",
|
||||
"archiveFileName": "x86_64-w64-mingw32.mklittlefs-30b7fc1.221223.zip",
|
||||
"checksum": "SHA-256:ee9a6caad229cd9530465926c755a4dc68a89bd5a6ff642c060dfa05f0c01a2a",
|
||||
"size": "357313"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+10
-2
@@ -20,7 +20,7 @@
|
||||
# https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5---3rd-party-Hardware-specification
|
||||
|
||||
name=Raspberry Pi RP2040 Boards
|
||||
version=2.6.4
|
||||
version=2.7.1
|
||||
|
||||
runtime.tools.pqt-gcc.path={runtime.platform.path}/system/arm-none-eabi
|
||||
runtime.tools.pqt-python3.path={runtime.platform.path}/system/python3
|
||||
@@ -28,6 +28,7 @@ runtime.tools.pqt-mklittlefs.path={runtime.platform.path}/system/mklittlefs
|
||||
runtime.tools.pqt-pioasm.path={runtime.platform.path}/system/pioasm
|
||||
runtime.tools.pqt-elf2uf2.path={runtime.platform.path}/system/elf2uf2
|
||||
runtime.tools.pqt-openocd.path={runtime.platform.path}/system/openocd
|
||||
runtime.tools.pqt-picotool.path={runtime.platform.path}/system/picotool
|
||||
compiler.path={runtime.tools.pqt-gcc.path}/bin/
|
||||
|
||||
compiler.libraries.ldflags=
|
||||
@@ -44,7 +45,7 @@ compiler.warning_flags.all=-Wall -Wextra -Werror=return-type -Wno-ignored-qualif
|
||||
compiler.netdefines=-DPICO_CYW43_ARCH_THREADSAFE_BACKGROUND=1 -DCYW43_LWIP=0 {build.lwipdefs} -DLWIP_IGMP=1 -DLWIP_CHECKSUM_CTRL_PER_NETIF=1
|
||||
compiler.defines={build.led} {build.usbstack_flags} -DCFG_TUSB_MCU=OPT_MCU_RP2040 -DUSB_VID={build.vid} -DUSB_PID={build.pid} '-DUSB_MANUFACTURER={build.usb_manufacturer}' '-DUSB_PRODUCT={build.usb_product}' {compiler.netdefines} -DARDUINO_VARIANT="{build.variant}" -DTARGET_RP2040
|
||||
compiler.includes="-iprefix{runtime.platform.path}/" "@{runtime.platform.path}/lib/platform_inc.txt" "-I{runtime.platform.path}/include"
|
||||
compiler.flags=-march=armv6-m -mcpu=cortex-m0plus -mthumb -ffunction-sections -fdata-sections {build.flags.exceptions} {build.flags.stackprotect} {build.flags.cmsis}
|
||||
compiler.flags=-march=armv6-m -mcpu=cortex-m0plus -mthumb -ffunction-sections -fdata-sections {build.flags.exceptions} {build.flags.stackprotect} {build.flags.cmsis} {build.picodebugflags}
|
||||
compiler.wrap="@{runtime.platform.path}/lib/platform_wrap.txt"
|
||||
compiler.libbearssl="{runtime.platform.path}/lib/libbearssl.a"
|
||||
|
||||
@@ -97,6 +98,7 @@ build.boot2=boot2_generic_03h_4_padded_checksum
|
||||
build.lwipdefs=-DLWIP_IPV6=0 -DLWIP_IPV4=1
|
||||
build.wificc=-DWIFICC=CYW43_COUNTRY_WORLDWIDE
|
||||
build.debugscript=picoprobe.tcl
|
||||
build.picodebugflags=
|
||||
|
||||
# Allow Pico boards do be auto-discovered by the IDE
|
||||
#discovery.rp2040.pattern={runtime.tools.pqt-python3.path}/python3 -I "{runtime.platform.path}/tools/pluggable_discovery.py"
|
||||
@@ -187,6 +189,12 @@ tools.uf2conv-network.upload.params.verbose=
|
||||
tools.uf2conv-network.upload.params.quiet=
|
||||
tools.uf2conv-network.upload.pattern="{cmd}" -I "{runtime.platform.path}/tools/espota.py" -i "{upload.port.address}" -p "{upload.port.properties.port}" "--auth={upload.field.password}" -f "{build.path}/{build.project_name}.bin"
|
||||
|
||||
#tools.picotool.cmd={runtime.tools.pqt-picotool.path}
|
||||
tools.picotool.cmd={runtime.platform.path}/system/picotool
|
||||
tools.picotool.upload.protocol=picotool
|
||||
tools.picotool.upload.params.verbose=
|
||||
tools.picotool.upload.params.quiet=
|
||||
tools.picotool.upload.pattern="{cmd}/picotool" load "{build.path}/{build.project_name}.uf2" -f
|
||||
|
||||
#tools.picoprobe.cmd={runtime.tools.pqt-openocd.path}
|
||||
tools.picoprobe.cmd={runtime.platform.path}/system/openocd
|
||||
|
||||
+2
-1
@@ -80,7 +80,8 @@ function build_sketches()
|
||||
continue # Not ours to do
|
||||
fi
|
||||
|
||||
if [ -e $cache_dir/core/*.a ]; then
|
||||
cacheas=( $cache_dir/core/*.a )
|
||||
if [ -e ${cacheas[0]} ]; then
|
||||
# We need to preserve the build.options.json file and replace the last .ino
|
||||
# with this sketch's ino file, or builder will throw everything away.
|
||||
jq '."sketchLocation" = "'$sketch'"' $build_dir/build.options.json > $build_dir/build.options.json.tmp
|
||||
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
for dir in ./cores/rp2040 ./libraries/EEPROM ./libraries/I2S \
|
||||
./libraries/LittleFS/src ./libraries/LittleFS/examples \
|
||||
./libraries/rp2040 ./libraries/SD ./libraries/ESP8266SdFat \
|
||||
for dir in ./cores/rp2040 ./libraries/EEPROM ./libraries/I2S ./libraries/SingleFileDrive \
|
||||
./libraries/LittleFS/src ./libraries/LittleFS/examples ./libraries/PWMAudio \
|
||||
./libraries/rp2040 ./libraries/SD ./libraries/ESP8266SdFat ./libraries/ADCInput \
|
||||
./libraries/Servo ./libraries/SPI ./libraries/Wire ./libraries/PDM \
|
||||
./libraries/WiFi ./libraries/lwIP_Ethernet ./libraries/lwIP_CYW43 \
|
||||
./libraries/FreeRTOS/src ./libraries/LEAmDNS ./libraries/MD5Builder \
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"earlephilhower": {
|
||||
"boot2_source": "boot2_w25q080_2_padded_checksum.S",
|
||||
"usb_vid": "0x1209",
|
||||
"usb_pid": "0xCB74"
|
||||
}
|
||||
},
|
||||
"core": "earlephilhower",
|
||||
"cpu": "cortex-m0plus",
|
||||
"extra_flags": "-D ARDUINO_0XCB_HELIOS -DARDUINO_ARCH_RP2040 -DUSBD_MAX_POWER_MA=500",
|
||||
"f_cpu": "133000000L",
|
||||
"hwids": [
|
||||
[
|
||||
"0x2E8A",
|
||||
"0x00C0"
|
||||
],
|
||||
[
|
||||
"0x1209",
|
||||
"0xCB74"
|
||||
]
|
||||
],
|
||||
"mcu": "rp2040",
|
||||
"variant": "0xcb_helios"
|
||||
},
|
||||
"debug": {
|
||||
"jlink_device": "RP2040_M0_0",
|
||||
"openocd_target": "rp2040.cfg",
|
||||
"svd_path": "rp2040.svd"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino"
|
||||
],
|
||||
"name": "Helios",
|
||||
"upload": {
|
||||
"maximum_ram_size": 270336,
|
||||
"maximum_size": 16777216,
|
||||
"require_upload_port": true,
|
||||
"native_usb": true,
|
||||
"use_1200bps_touch": true,
|
||||
"wait_for_upload_port": false,
|
||||
"protocol": "picotool",
|
||||
"protocols": [
|
||||
"cmsis-dap",
|
||||
"jlink",
|
||||
"raspberrypi-swd",
|
||||
"picotool",
|
||||
"picoprobe"
|
||||
]
|
||||
},
|
||||
"url": "https://www.raspberrypi.org/products/raspberry-pi-pico/",
|
||||
"vendor": "0xCB"
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"earlephilhower": {
|
||||
"boot2_source": "boot2_w25x10cl_4_padded_checksum.S",
|
||||
"usb_vid": "0x2E8A",
|
||||
"usb_pid": "0x6E61"
|
||||
}
|
||||
},
|
||||
"core": "earlephilhower",
|
||||
"cpu": "cortex-m0plus",
|
||||
"extra_flags": "-D ARDUINO_NULLBITS_BIT_C_PRO -DARDUINO_ARCH_RP2040 -DUSBD_MAX_POWER_MA=500",
|
||||
"f_cpu": "133000000L",
|
||||
"hwids": [
|
||||
[
|
||||
"0x2E8A",
|
||||
"0x00C0"
|
||||
],
|
||||
[
|
||||
"0x2E8A",
|
||||
"0x6E61"
|
||||
]
|
||||
],
|
||||
"mcu": "rp2040",
|
||||
"variant": "nullbits_bit_c_pro"
|
||||
},
|
||||
"debug": {
|
||||
"jlink_device": "RP2040_M0_0",
|
||||
"openocd_target": "rp2040.cfg",
|
||||
"svd_path": "rp2040.svd"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino"
|
||||
],
|
||||
"name": "Bit-C PRO",
|
||||
"upload": {
|
||||
"maximum_ram_size": 270336,
|
||||
"maximum_size": 4194304,
|
||||
"require_upload_port": true,
|
||||
"native_usb": true,
|
||||
"use_1200bps_touch": true,
|
||||
"wait_for_upload_port": false,
|
||||
"protocol": "picotool",
|
||||
"protocols": [
|
||||
"cmsis-dap",
|
||||
"jlink",
|
||||
"raspberrypi-swd",
|
||||
"picotool",
|
||||
"picoprobe"
|
||||
]
|
||||
},
|
||||
"url": "https://www.raspberrypi.org/products/raspberry-pi-pico/",
|
||||
"vendor": "nullbits"
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
},
|
||||
"core": "earlephilhower",
|
||||
"cpu": "cortex-m0plus",
|
||||
"extra_flags": "-D ARDUINO_DEGZ_MIZU -DARDUINO_ARCH_RP2040 -DUSBD_MAX_POWER_MA=250",
|
||||
"extra_flags": "-D ARDUINO_VIYALAB_MIZU_RP2040 -DARDUINO_ARCH_RP2040 -DUSBD_MAX_POWER_MA=250",
|
||||
"f_cpu": "133000000L",
|
||||
"hwids": [
|
||||
[
|
||||
@@ -22,7 +22,7 @@
|
||||
]
|
||||
],
|
||||
"mcu": "rp2040",
|
||||
"variant": "degz_mizu"
|
||||
"variant": "viyalab_mizu"
|
||||
},
|
||||
"debug": {
|
||||
"jlink_device": "RP2040_M0_0",
|
||||
@@ -32,7 +32,7 @@
|
||||
"frameworks": [
|
||||
"arduino"
|
||||
],
|
||||
"name": "Mizu",
|
||||
"name": "Mizu RP2040",
|
||||
"upload": {
|
||||
"maximum_ram_size": 270336,
|
||||
"maximum_size": 8388608,
|
||||
@@ -50,5 +50,5 @@
|
||||
]
|
||||
},
|
||||
"url": "https://www.raspberrypi.org/products/raspberry-pi-pico/",
|
||||
"vendor": "Degz"
|
||||
"vendor": "Viyalab"
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"earlephilhower": {
|
||||
"boot2_source": "boot2_w25q16jvxq_4_padded_checksum.S",
|
||||
"usb_vid": "0x2E8A",
|
||||
"usb_pid": "0x1021"
|
||||
}
|
||||
},
|
||||
"core": "earlephilhower",
|
||||
"cpu": "cortex-m0plus",
|
||||
"extra_flags": "-D ARDUINO_WAVESHARE_RP2040_LCD_0_96 -DARDUINO_ARCH_RP2040 -DUSBD_MAX_POWER_MA=500",
|
||||
"f_cpu": "133000000L",
|
||||
"hwids": [
|
||||
[
|
||||
"0x2E8A",
|
||||
"0x00C0"
|
||||
],
|
||||
[
|
||||
"0x2E8A",
|
||||
"0x1021"
|
||||
]
|
||||
],
|
||||
"mcu": "rp2040",
|
||||
"variant": "waveshare_rp2040_lcd_0_96"
|
||||
},
|
||||
"debug": {
|
||||
"jlink_device": "RP2040_M0_0",
|
||||
"openocd_target": "rp2040.cfg",
|
||||
"svd_path": "rp2040.svd"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino"
|
||||
],
|
||||
"name": "RP2040 LCD 0.96",
|
||||
"upload": {
|
||||
"maximum_ram_size": 270336,
|
||||
"maximum_size": 2097152,
|
||||
"require_upload_port": true,
|
||||
"native_usb": true,
|
||||
"use_1200bps_touch": true,
|
||||
"wait_for_upload_port": false,
|
||||
"protocol": "picotool",
|
||||
"protocols": [
|
||||
"cmsis-dap",
|
||||
"jlink",
|
||||
"raspberrypi-swd",
|
||||
"picotool",
|
||||
"picoprobe"
|
||||
]
|
||||
},
|
||||
"url": "https://www.raspberrypi.org/products/raspberry-pi-pico/",
|
||||
"vendor": "Waveshare"
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"earlephilhower": {
|
||||
"boot2_source": "boot2_w25q16jvxq_4_padded_checksum.S",
|
||||
"usb_vid": "0x2E8A",
|
||||
"usb_pid": "0x1039"
|
||||
}
|
||||
},
|
||||
"core": "earlephilhower",
|
||||
"cpu": "cortex-m0plus",
|
||||
"extra_flags": "-D ARDUINO_WAVESHARE_RP2040_LCD_1_28 -DARDUINO_ARCH_RP2040 -DUSBD_MAX_POWER_MA=500",
|
||||
"f_cpu": "133000000L",
|
||||
"hwids": [
|
||||
[
|
||||
"0x2E8A",
|
||||
"0x00C0"
|
||||
],
|
||||
[
|
||||
"0x2E8A",
|
||||
"0x1039"
|
||||
]
|
||||
],
|
||||
"mcu": "rp2040",
|
||||
"variant": "waveshare_rp2040_lcd_1_28"
|
||||
},
|
||||
"debug": {
|
||||
"jlink_device": "RP2040_M0_0",
|
||||
"openocd_target": "rp2040.cfg",
|
||||
"svd_path": "rp2040.svd"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino"
|
||||
],
|
||||
"name": "RP2040 LCD 1.28",
|
||||
"upload": {
|
||||
"maximum_ram_size": 270336,
|
||||
"maximum_size": 2097152,
|
||||
"require_upload_port": true,
|
||||
"native_usb": true,
|
||||
"use_1200bps_touch": true,
|
||||
"wait_for_upload_port": false,
|
||||
"protocol": "picotool",
|
||||
"protocols": [
|
||||
"cmsis-dap",
|
||||
"jlink",
|
||||
"raspberrypi-swd",
|
||||
"picotool",
|
||||
"picoprobe"
|
||||
]
|
||||
},
|
||||
"url": "https://www.raspberrypi.org/products/raspberry-pi-pico/",
|
||||
"vendor": "Waveshare"
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"earlephilhower": {
|
||||
"boot2_source": "boot2_w25q16jvxq_4_padded_checksum.S",
|
||||
"usb_vid": "0x2E8A",
|
||||
"usb_pid": "0x103A"
|
||||
}
|
||||
},
|
||||
"core": "earlephilhower",
|
||||
"cpu": "cortex-m0plus",
|
||||
"extra_flags": "-D ARDUINO_WAVESHARE_RP2040_ONE -DARDUINO_ARCH_RP2040 -DUSBD_MAX_POWER_MA=500",
|
||||
"f_cpu": "133000000L",
|
||||
"hwids": [
|
||||
[
|
||||
"0x2E8A",
|
||||
"0x00C0"
|
||||
],
|
||||
[
|
||||
"0x2E8A",
|
||||
"0x103A"
|
||||
]
|
||||
],
|
||||
"mcu": "rp2040",
|
||||
"variant": "waveshare_rp2040_one"
|
||||
},
|
||||
"debug": {
|
||||
"jlink_device": "RP2040_M0_0",
|
||||
"openocd_target": "rp2040.cfg",
|
||||
"svd_path": "rp2040.svd"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino"
|
||||
],
|
||||
"name": "RP2040 One",
|
||||
"upload": {
|
||||
"maximum_ram_size": 270336,
|
||||
"maximum_size": 4194304,
|
||||
"require_upload_port": true,
|
||||
"native_usb": true,
|
||||
"use_1200bps_touch": true,
|
||||
"wait_for_upload_port": false,
|
||||
"protocol": "picotool",
|
||||
"protocols": [
|
||||
"cmsis-dap",
|
||||
"jlink",
|
||||
"raspberrypi-swd",
|
||||
"picotool",
|
||||
"picoprobe"
|
||||
]
|
||||
},
|
||||
"url": "https://www.raspberrypi.org/products/raspberry-pi-pico/",
|
||||
"vendor": "Waveshare"
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"earlephilhower": {
|
||||
"boot2_source": "boot2_w25q080_2_padded_checksum.S",
|
||||
"usb_vid": "0x2E8A",
|
||||
"usb_pid": "0x1020"
|
||||
}
|
||||
},
|
||||
"core": "earlephilhower",
|
||||
"cpu": "cortex-m0plus",
|
||||
"extra_flags": "-D ARDUINO_WAVESHARE_RP2040_PLUS -DARDUINO_ARCH_RP2040 -DUSBD_MAX_POWER_MA=500",
|
||||
"f_cpu": "133000000L",
|
||||
"hwids": [
|
||||
[
|
||||
"0x2E8A",
|
||||
"0x00C0"
|
||||
],
|
||||
[
|
||||
"0x2E8A",
|
||||
"0x1020"
|
||||
]
|
||||
],
|
||||
"mcu": "rp2040",
|
||||
"variant": "waveshare_rp2040_plus_16mb"
|
||||
},
|
||||
"debug": {
|
||||
"jlink_device": "RP2040_M0_0",
|
||||
"openocd_target": "rp2040.cfg",
|
||||
"svd_path": "rp2040.svd"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino"
|
||||
],
|
||||
"name": "RP2040 Plus 16MB",
|
||||
"upload": {
|
||||
"maximum_ram_size": 270336,
|
||||
"maximum_size": 16777216,
|
||||
"require_upload_port": true,
|
||||
"native_usb": true,
|
||||
"use_1200bps_touch": true,
|
||||
"wait_for_upload_port": false,
|
||||
"protocol": "picotool",
|
||||
"protocols": [
|
||||
"cmsis-dap",
|
||||
"jlink",
|
||||
"raspberrypi-swd",
|
||||
"picotool",
|
||||
"picoprobe"
|
||||
]
|
||||
},
|
||||
"url": "https://www.raspberrypi.org/products/raspberry-pi-pico/",
|
||||
"vendor": "Waveshare"
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"earlephilhower": {
|
||||
"boot2_source": "boot2_w25q080_2_padded_checksum.S",
|
||||
"usb_vid": "0x2E8A",
|
||||
"usb_pid": "0x1020"
|
||||
}
|
||||
},
|
||||
"core": "earlephilhower",
|
||||
"cpu": "cortex-m0plus",
|
||||
"extra_flags": "-D ARDUINO_WAVESHARE_RP2040_PLUS -DARDUINO_ARCH_RP2040 -DUSBD_MAX_POWER_MA=500",
|
||||
"f_cpu": "133000000L",
|
||||
"hwids": [
|
||||
[
|
||||
"0x2E8A",
|
||||
"0x00C0"
|
||||
],
|
||||
[
|
||||
"0x2E8A",
|
||||
"0x1020"
|
||||
]
|
||||
],
|
||||
"mcu": "rp2040",
|
||||
"variant": "waveshare_rp2040_plus_4mb"
|
||||
},
|
||||
"debug": {
|
||||
"jlink_device": "RP2040_M0_0",
|
||||
"openocd_target": "rp2040.cfg",
|
||||
"svd_path": "rp2040.svd"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino"
|
||||
],
|
||||
"name": "RP2040 Plus 4MB",
|
||||
"upload": {
|
||||
"maximum_ram_size": 270336,
|
||||
"maximum_size": 4194304,
|
||||
"require_upload_port": true,
|
||||
"native_usb": true,
|
||||
"use_1200bps_touch": true,
|
||||
"wait_for_upload_port": false,
|
||||
"protocol": "picotool",
|
||||
"protocols": [
|
||||
"cmsis-dap",
|
||||
"jlink",
|
||||
"raspberrypi-swd",
|
||||
"picotool",
|
||||
"picoprobe"
|
||||
]
|
||||
},
|
||||
"url": "https://www.raspberrypi.org/products/raspberry-pi-pico/",
|
||||
"vendor": "Waveshare"
|
||||
}
|
||||
@@ -72,15 +72,14 @@
|
||||
//------------- CLASS -------------//
|
||||
#define CFG_TUD_HID (2)
|
||||
#define CFG_TUD_CDC (1)
|
||||
#define CFG_TUD_MSC (0)
|
||||
#define CFG_TUD_MSC (1)
|
||||
#define CFG_TUD_MIDI (0)
|
||||
#define CFG_TUD_VENDOR (0)
|
||||
|
||||
#define CFG_TUD_CDC_RX_BUFSIZE (256)
|
||||
#define CFG_TUD_CDC_TX_BUFSIZE (256)
|
||||
|
||||
#define CFG_TUD_MIDI_RX_BUFSIZE (64)
|
||||
#define CFG_TUD_MIDI_TX_BUFSIZE (64)
|
||||
#define CFG_TUD_MSC_EP_BUFSIZE (64)
|
||||
|
||||
// HID buffer size Should be sufficient to hold ID (if any) + Data
|
||||
#define CFG_TUD_HID_EP_BUFSIZE (64)
|
||||
|
||||
+63
-58
@@ -79,8 +79,6 @@ def BuildUSBStack(name):
|
||||
print('%s.menu.usbstack.picosdk.build.usbstack_flags=' % (name))
|
||||
print("%s.menu.usbstack.tinyusb=Adafruit TinyUSB" % (name))
|
||||
print('%s.menu.usbstack.tinyusb.build.usbstack_flags=-DUSE_TINYUSB "-I{runtime.platform.path}/libraries/Adafruit_TinyUSB_Arduino/src/arduino"' % (name))
|
||||
|
||||
def BuildWithoutUSBStack(name):
|
||||
print("%s.menu.usbstack.nousb=No USB" % (name))
|
||||
print('%s.menu.usbstack.nousb.build.usbstack_flags="-DNO_USB -DDISABLE_USB_SERIAL -I{runtime.platform.path}/tools/libpico"' % (name))
|
||||
|
||||
@@ -103,7 +101,26 @@ def BuildIPStack(name):
|
||||
print('%s.menu.ipstack.ipv4ipv6.build.libpico=libpico-ipv6.a' % (name))
|
||||
print('%s.menu.ipstack.ipv4ipv6.build.lwipdefs=-DLWIP_IPV6=1 -DLWIP_IPV4=1' % (name))
|
||||
|
||||
def BuildHeader(name, vendor_name, product_name, vidtouse, pidtouse, vid, pid, pwr, boarddefine, variant, uploadtool, networkuploadtool, flashsize, ramsize, boot2, dbg, extra):
|
||||
def BuildUploadMethodMenu(name):
|
||||
for a, b, c, d, e, f in [ ["default", "Default (UF2)", 256, "picoprobe.tcl", "uf2conv", "uf2conv-network"],
|
||||
["picotool", "Picotool", 256, "picoprobe.tcl", "picotool", None],
|
||||
["picoprobe", "Picoprobe", 256, "picoprobe.tcl", "picoprobe", None],
|
||||
["picodebug", "Pico-Debug", 240, "picodebug.tcl", "picodebug", None] ]:
|
||||
print("%s.menu.uploadmethod.%s=%s" % (name, a, b))
|
||||
print("%s.menu.uploadmethod.%s.build.ram_length=%dk" % (name, a, c))
|
||||
print("%s.menu.uploadmethod.%s.build.debugscript=%s" % (name, a, d))
|
||||
# For pico-debug, need to disable USB unconditionally
|
||||
if a == "picodebug":
|
||||
print("%s.menu.uploadmethod.%s.build.picodebugflags=-UUSE_TINYUSB -DNO_USB -DDISABLE_USB_SERIAL -I{runtime.platform.path}/tools/libpico" % (name, a))
|
||||
elif a == "picotool":
|
||||
print("%s.menu.uploadmethod.%s.build.picodebugflags=-DENABLE_PICOTOOL_USB" % (name, a))
|
||||
print("%s.menu.uploadmethod.%s.upload.maximum_data_size=%d" % (name, a, c * 1024))
|
||||
print("%s.menu.uploadmethod.%s.upload.tool=%s" % (name, a, e))
|
||||
print("%s.menu.uploadmethod.%s.upload.tool.default=%s" % (name, a, e))
|
||||
if f != None:
|
||||
print("%s.menu.uploadmethod.%s.upload.tool.network=%s" % (name, a, f))
|
||||
|
||||
def BuildHeader(name, vendor_name, product_name, vid, pid, pwr, boarddefine, variant, flashsize, boot2, extra):
|
||||
prettyname = vendor_name + " " + product_name
|
||||
print()
|
||||
print("# -----------------------------------")
|
||||
@@ -111,17 +128,17 @@ def BuildHeader(name, vendor_name, product_name, vidtouse, pidtouse, vid, pid, p
|
||||
print("# -----------------------------------")
|
||||
print("%s.name=%s" % (name, prettyname))
|
||||
usb = 0
|
||||
if type(pidtouse) == list:
|
||||
if type(pid) == list:
|
||||
for tp in pid:
|
||||
print("%s.vid.%d=%s" % (name, usb, vidtouse))
|
||||
print("%s.vid.%d=%s" % (name, usb, vid))
|
||||
print("%s.pid.%d=0x%04x" % (name, usb, int(tp, 16)))
|
||||
usb = usb + 1
|
||||
else:
|
||||
for kb in [ "0", "0x8000" ]:
|
||||
for ms in [ "0", "0x4000" ]:
|
||||
for jy in [ "0", "0x0100" ]:
|
||||
thispid = int(pidtouse, 16) | int(kb, 16) | int(ms, 16) | int(jy, 16)
|
||||
print("%s.vid.%d=%s" % (name, usb, vidtouse))
|
||||
thispid = int(pid, 16) | int(kb, 16) | int(ms, 16) | int(jy, 16)
|
||||
print("%s.vid.%d=%s" % (name, usb, vid))
|
||||
print("%s.pid.%d=0x%04x" % (name, usb, thispid))
|
||||
usb = usb + 1
|
||||
if type(pid) == list:
|
||||
@@ -132,12 +149,7 @@ def BuildHeader(name, vendor_name, product_name, vidtouse, pidtouse, vid, pid, p
|
||||
print("%s.build.board=%s" % (name, boarddefine))
|
||||
print("%s.build.mcu=cortex-m0plus" % (name))
|
||||
print("%s.build.variant=%s" % (name, variant))
|
||||
print("%s.upload.tool=%s" % (name, uploadtool))
|
||||
print("%s.upload.tool.default=%s" % (name, uploadtool))
|
||||
if networkuploadtool != None:
|
||||
print("%s.upload.tool.network=%s" % (name, networkuploadtool))
|
||||
print("%s.upload.maximum_size=%d" % (name, flashsize))
|
||||
print("%s.upload.maximum_data_size=%d" % (name, ramsize))
|
||||
print("%s.upload.wait_for_upload_port=true" % (name))
|
||||
print("%s.upload.erase_cmd=" % (name))
|
||||
print("%s.serial.disableDTR=false" % (name))
|
||||
@@ -146,8 +158,6 @@ def BuildHeader(name, vendor_name, product_name, vidtouse, pidtouse, vid, pid, p
|
||||
print("%s.build.led=" % (name))
|
||||
print("%s.build.core=rp2040" % (name))
|
||||
print("%s.build.ldscript=memmap_default.ld" % (name))
|
||||
print("%s.build.ram_length=%dk" % (name, ramsize / 1024))
|
||||
print("%s.build.debugscript=%s" % (name, dbg))
|
||||
print("%s.build.boot2=%s" % (name, boot2))
|
||||
print("%s.build.vid=%s" % (name, vid))
|
||||
if type(pid) == list:
|
||||
@@ -182,49 +192,34 @@ def BuildGlobalMenuList():
|
||||
print("menu.wificountry=WiFi Region")
|
||||
print("menu.usbstack=USB Stack")
|
||||
print("menu.ipstack=IP Stack")
|
||||
print("menu.uploadmethod=Upload Method")
|
||||
|
||||
def MakeBoard(name, vendor_name, product_name, vid, pid, pwr, boarddefine, flashsizemb, boot2, extra = None):
|
||||
for a, b, c, d in [ ["", "", "uf2conv", "uf2conv-network"], ["picoprobe", " (Picoprobe)", "picoprobe", None], ["picodebug", " (pico-debug)", "picodebug", None]]:
|
||||
n = name + a
|
||||
p = product_name + b
|
||||
fssizelist = [ 0, 64 * 1024, 128 * 1024, 256 * 1024, 512 * 1024 ]
|
||||
for i in range(1, flashsizemb):
|
||||
fssizelist.append(i * 1024 * 1024)
|
||||
vidtouse = vid;
|
||||
ramsizekb = 256;
|
||||
dbg = "picoprobe.tcl"
|
||||
if a == "picoprobe":
|
||||
pidtouse = '0x0004'
|
||||
elif a == "picodebug":
|
||||
vidtouse = '0x1209'
|
||||
pidtouse = '0x2488'
|
||||
ramsizekb = 240;
|
||||
dbg = "picodebug.tcl"
|
||||
else:
|
||||
pidtouse = pid
|
||||
BuildHeader(n, vendor_name, p, vidtouse, pidtouse, vid, pid, pwr, boarddefine, name, c, d, flashsizemb * 1024 * 1024, ramsizekb * 1024, boot2, dbg, extra)
|
||||
if name == "generic":
|
||||
BuildFlashMenu(n, 2*1024*1024, [0, 1*1024*1024])
|
||||
BuildFlashMenu(n, 4*1024*1024, [0, 2*1024*1024])
|
||||
BuildFlashMenu(n, 8*1024*1024, [0, 4*1024*1024])
|
||||
BuildFlashMenu(n, 16*1024*1024, [0, 8*1024*1024])
|
||||
else:
|
||||
BuildFlashMenu(n, flashsizemb * 1024 * 1024, fssizelist)
|
||||
BuildFreq(n)
|
||||
BuildOptimize(n)
|
||||
BuildRTTI(n)
|
||||
BuildStackProtect(n)
|
||||
BuildExceptions(n)
|
||||
BuildDebugPort(n)
|
||||
BuildDebugLevel(n)
|
||||
if a != "picodebug":
|
||||
BuildUSBStack(n)
|
||||
BuildWithoutUSBStack(n)
|
||||
if name == "rpipicow":
|
||||
BuildCountry(n)
|
||||
BuildIPStack(n)
|
||||
if name == "generic":
|
||||
BuildBoot(n)
|
||||
fssizelist = [ 0, 64 * 1024, 128 * 1024, 256 * 1024, 512 * 1024 ]
|
||||
for i in range(1, flashsizemb):
|
||||
fssizelist.append(i * 1024 * 1024)
|
||||
BuildHeader(name, vendor_name, product_name, vid, pid, pwr, boarddefine, name, flashsizemb * 1024 * 1024, boot2, extra)
|
||||
if name == "generic":
|
||||
BuildFlashMenu(name, 2*1024*1024, [0, 1*1024*1024])
|
||||
BuildFlashMenu(name, 4*1024*1024, [0, 2*1024*1024])
|
||||
BuildFlashMenu(name, 8*1024*1024, [0, 4*1024*1024])
|
||||
BuildFlashMenu(name, 16*1024*1024, [0, 8*1024*1024])
|
||||
else:
|
||||
BuildFlashMenu(name, flashsizemb * 1024 * 1024, fssizelist)
|
||||
BuildFreq(name)
|
||||
BuildOptimize(name)
|
||||
BuildRTTI(name)
|
||||
BuildStackProtect(name)
|
||||
BuildExceptions(name)
|
||||
BuildDebugPort(name)
|
||||
BuildDebugLevel(name)
|
||||
BuildUSBStack(name)
|
||||
if name == "rpipicow":
|
||||
BuildCountry(name)
|
||||
BuildIPStack(name)
|
||||
if name == "generic":
|
||||
BuildBoot(name)
|
||||
BuildUploadMethodMenu(name)
|
||||
MakeBoardJSON(name, vendor_name, product_name, vid, pid, pwr, boarddefine, flashsizemb, boot2, extra)
|
||||
global pkgjson
|
||||
thisbrd = {}
|
||||
@@ -324,6 +319,9 @@ BuildGlobalMenuList()
|
||||
MakeBoard("rpipico", "Raspberry Pi", "Pico", "0x2e8a", "0x000a", 250, "RASPBERRY_PI_PICO", 2, "boot2_w25q080_2_padded_checksum")
|
||||
MakeBoard("rpipicow", "Raspberry Pi", "Pico W", "0x2e8a", "0xf00a", 250, "RASPBERRY_PI_PICO_W", 2, "boot2_w25q080_2_padded_checksum")
|
||||
|
||||
# 0xCB
|
||||
MakeBoard("0xcb_helios", "0xCB", "Helios", "0x1209", "0xCB74", 500, "0XCB_HELIOS", 16, "boot2_w25q080_2_padded_checksum")
|
||||
|
||||
# Adafruit
|
||||
MakeBoard("adafruit_feather", "Adafruit", "Feather RP2040", "0x239a", "0x80f1", 250, "ADAFRUIT_FEATHER_RP2040", 8, "boot2_w25x10cl_4_padded_checksum")
|
||||
MakeBoard("adafruit_feather_scorpio", "Adafruit", "Feather RP2040 SCORPIO", "0x239a", "0x8121", 250, "ADAFRUIT_FEATHER_RP2040_SCORPIO", 8, "boot2_w25q080_2_padded_checksum")
|
||||
@@ -347,9 +345,6 @@ MakeBoard("cytron_maker_pi_rp2040", "Cytron", "Maker Pi RP2040", "0x2e8a", "0x10
|
||||
# DatanoiseTV
|
||||
MakeBoard("datanoisetv_picoadk", "DatanoiseTV", "PicoADK", "0x2e8a", "0x000a", 250, "DATANOISETV_PICOADK", 2, "boot2_w25q080_2_padded_checksum")
|
||||
|
||||
# Degz
|
||||
MakeBoard("degz_mizu", "Degz", "Mizu", "0x2e8a", "0x000a", 250, "DEGZ_MIZU", 8, "boot2_generic_03h_4_padded_checksum")
|
||||
|
||||
# DeRuiLab
|
||||
MakeBoard("flyboard2040_core", "DeRuiLab", "FlyBoard2040Core", "0x2e8a", "0x008a", 500, "FLYBOARD2040_CORE", 4, "boot2_w25q080_2_padded_checksum")
|
||||
|
||||
@@ -377,6 +372,9 @@ MakeBoard("ilabs_rpico32", "iLabs", "RPICO32", "0x2e8a", "0x1010", 250, "ILABS_2
|
||||
MakeBoard("melopero_cookie_rp2040", "Melopero", "Cookie RP2040", "0x2e8a", "0x1011", 250, "MELOPERO_COOKIE_RP2040", 8, "boot2_w25q080_2_padded_checksum")
|
||||
MakeBoard("melopero_shake_rp2040", "Melopero", "Shake RP2040", "0x2e8a", "0x1005", 250, "MELOPERO_SHAKE_RP2040", 16, "boot2_w25q080_2_padded_checksum")
|
||||
|
||||
# nullbits
|
||||
MakeBoard("nullbits_bit_c_pro", "nullbits", "Bit-C PRO", "0x2e8a", "0x6e61", 500, "NULLBITS_BIT_C_PRO", 4, "boot2_w25x10cl_4_padded_checksum")
|
||||
|
||||
# Pimoroni
|
||||
MakeBoard("pimoroni_pga2040", "Pimoroni", "PGA2040", "0x2e8a", "0x1008", 250, "PIMORONI_PGA2040", 8, "boot2_w25q64jv_4_padded_checksum")
|
||||
|
||||
@@ -393,6 +391,12 @@ MakeBoard("upesy_rp2040_devkit", "uPesy", "RP2040 DevKit", "0x2e8a", "0x1007", 2
|
||||
# Seeed
|
||||
MakeBoard("seeed_xiao_rp2040", "Seeed", "XIAO RP2040", "0x2e8a", "0x000a", 250, "SEEED_XIAO_RP2040", 2, "boot2_w25q080_2_padded_checksum")
|
||||
|
||||
# VCC-GND YD-2040 - Use generic SPI/4 because boards seem to come with varied flash modules but same name
|
||||
MakeBoard('vccgnd_yd_rp2040', "VCC-GND", "YD RP2040", "0x2e8a", "0x800a", 500, "YD_RP2040", 16, "boot2_generic_03h_4_padded_checksum")
|
||||
|
||||
# Viyalab
|
||||
MakeBoard("viyalab_mizu", "Viyalab", "Mizu RP2040", "0x2e8a", "0x000a", 250, "VIYALAB_MIZU_RP2040", 8, "boot2_generic_03h_4_padded_checksum")
|
||||
|
||||
# Waveshare
|
||||
MakeBoard("waveshare_rp2040_zero", "Waveshare", "RP2040 Zero", "0x2e8a", "0x0003", 500, "WAVESHARE_RP2040_ZERO", 2, "boot2_w25q16jvxq_4_padded_checksum")
|
||||
MakeBoard("waveshare_rp2040_one", "Waveshare", "RP2040 One", "0x2e8a", "0x103a", 500, "WAVESHARE_RP2040_ONE", 4, "boot2_w25q16jvxq_4_padded_checksum")
|
||||
@@ -400,6 +404,7 @@ MakeBoard("waveshare_rp2040_plus_4mb", "Waveshare", "RP2040 Plus 4MB", "0x2e8a",
|
||||
MakeBoard("waveshare_rp2040_plus_16mb", "Waveshare", "RP2040 Plus 16MB", "0x2e8a", "0x1020", 500, "WAVESHARE_RP2040_PLUS", 16, "boot2_w25q080_2_padded_checksum")
|
||||
MakeBoard("waveshare_rp2040_lcd_0_96", "Waveshare", "RP2040 LCD 0.96", "0x2e8a", "0x1021", 500, "WAVESHARE_RP2040_LCD_0_96", 2, "boot2_w25q16jvxq_4_padded_checksum")
|
||||
MakeBoard("waveshare_rp2040_lcd_1_28", "Waveshare", "RP2040 LCD 1.28", "0x2e8a", "0x1039", 500, "WAVESHARE_RP2040_LCD_1_28", 2, "boot2_w25q16jvxq_4_padded_checksum")
|
||||
|
||||
# WIZnet
|
||||
MakeBoard("wiznet_5100s_evb_pico", "WIZnet", "W5100S-EVB-Pico", "0x2e8a", "0x1027", 250, "WIZNET_5100S_EVB_PICO", 2, "boot2_w25q080_2_padded_checksum")
|
||||
MakeBoard("wiznet_wizfi360_evb_pico", "WIZnet", "WizFi360-EVB-Pico", "0x2e8a", "0x1028", 250, "WIZNET_WIZFI360_EVB_PICO", 2, "boot2_w25q080_2_padded_checksum")
|
||||
|
||||
@@ -54,6 +54,7 @@ def scanner():
|
||||
scannerGo = True
|
||||
|
||||
def main():
|
||||
global scannerGo
|
||||
while True:
|
||||
cmdline = input()
|
||||
cmd = cmdline.split()[0]
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
// Pin definitions taken from: https://raw.githubusercontent.com/0xCB-dev/0xCB-Helios/main/rev1.0/helios.webp
|
||||
|
||||
// LEDs
|
||||
#define PIN_LED (17u)
|
||||
|
||||
// Serial
|
||||
#define PIN_SERIAL1_TX (0u)
|
||||
#define PIN_SERIAL1_RX (1u)
|
||||
|
||||
// Not pinned out
|
||||
#define PIN_SERIAL2_TX (31u)
|
||||
#define PIN_SERIAL2_RX (31u)
|
||||
|
||||
// SPI
|
||||
#define PIN_SPI0_MISO (20u)
|
||||
#define PIN_SPI0_MOSI (23u)
|
||||
#define PIN_SPI0_SCK (22u)
|
||||
#define PIN_SPI0_SS (21u)
|
||||
|
||||
// Not pinned out
|
||||
#define PIN_SPI1_MISO (31u)
|
||||
#define PIN_SPI1_MOSI (31u)
|
||||
#define PIN_SPI1_SCK (31u)
|
||||
#define PIN_SPI1_SS (31u)
|
||||
|
||||
// Not pinned out
|
||||
#define PIN_WIRE0_SDA (31u)
|
||||
#define PIN_WIRE0_SCL (31u)
|
||||
|
||||
// Wire
|
||||
#define PIN_WIRE1_SDA (2u)
|
||||
#define PIN_WIRE1_SCL (3u)
|
||||
|
||||
#define SERIAL_HOWMANY (1u)
|
||||
#define SPI_HOWMANY (1u)
|
||||
#define WIRE_HOWMANY (1u)
|
||||
|
||||
#define PIN_NEOPIXEL (25u)
|
||||
#include "../generic/common.h"
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
// Taken from pinout at https://nullbits.co/static/img/BIt-C_PRO_pinout.png
|
||||
|
||||
// LEDs
|
||||
#define PIN_LED (18u)
|
||||
|
||||
// UARTs
|
||||
#define PIN_SERIAL1_TX (0u)
|
||||
#define PIN_SERIAL1_RX (1u)
|
||||
|
||||
#define PIN_SERIAL2_TX (8u)
|
||||
#define PIN_SERIAL2_RX (9u)
|
||||
|
||||
// SPI
|
||||
#define PIN_SPI0_MISO (20u)
|
||||
#define PIN_SPI0_MOSI (23u)
|
||||
#define PIN_SPI0_SCK (22u)
|
||||
#define PIN_SPI0_SS (21u)
|
||||
|
||||
#define PIN_SPI1_MISO (12u)
|
||||
#define PIN_SPI1_MOSI (11u)
|
||||
#define PIN_SPI1_SCK (14u)
|
||||
#define PIN_SPI1_SS (13u)
|
||||
|
||||
// Wire
|
||||
#define PIN_WIRE0_SDA (2u)
|
||||
#define PIN_WIRE0_SCL (3u)
|
||||
|
||||
#define PIN_WIRE1_SDA (4u)
|
||||
#define PIN_WIRE1_SCL (5u)
|
||||
|
||||
#define SERIAL_HOWMANY (3u)
|
||||
#define SPI_HOWMANY (2u)
|
||||
#define WIRE_HOWMANY (2u)
|
||||
|
||||
#include "../generic/common.h"
|
||||
Reference in New Issue
Block a user