Compare commits

..
14 Commits
Author SHA1 Message Date
Earle F. Philhower, III 20cabe824f Update version 2023-06-17 19:40:40 -07:00
Earle F. Philhower, III 0847d3dbb0 Update to Pico-SDK 1.5.1 (#1539)
This should reduce the `git submodule update` space required as well as
avoid the max-path-len errors under Windows in most cases.
2023-06-15 17:05:04 -07:00
Paint Your Dragon fa7c287f07 Linker tweaks for RAM-resident PicoDVI library functions (#1541) 2023-06-15 15:01:02 -07:00
hreintke 9b3032cd58 WebServer: Solve HTTP request delay by dropping idle connections (#1537)
Serve next wificlient (http_request) if current client does not have data
2023-06-15 07:39:02 -07:00
Earle F. Philhower, III 50646b9e70 Update httpclient.rst (#1538) 2023-06-15 07:20:15 -07:00
Earle F. Philhower, III 3d6a1c7b41 Enable proper reuse of PIO programs (#1526)
* Enable proper reuse of PIO programs

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

Supersedes #1524

* Less copy-pasta
2023-06-13 04:42:37 -07:00
Earle F. Philhower, III 5b76b0668b Addition ABM checks in PWMAudio and ADCInput (#1530)
Handle the case where the DMA manager is unable to completely allocate
needed resources (DMA channels or memory) and return `false` in ::begin()
2023-06-12 15:20:52 -07:00
Earle F. Philhower, III d18f8dce2f I2S check for failure of ARB and PIO allocation (#1528)
Per https://github.com/earlephilhower/arduino-pico/pull/1524#issuecomment-1587885054
2023-06-12 12:24:08 -07:00
Earle F. Philhower, III c64cdc14b6 Call I2S::end() in I2S destructor (#1527)
Per https://github.com/earlephilhower/arduino-pico/pull/1524#issuecomment-1587562257
2023-06-12 12:12:50 -07:00
Earle F. Philhower, III cc800713bd Minor clean up includes (#1520) 2023-06-10 20:42:28 -07:00
Linar Yusupov c6a0d6ecfe Fix for invalid __channelCount in ~AudioBufferManager() (#1519) 2023-06-10 10:51:19 -07:00
Earle F. Philhower, III c6426ae461 Update RP2040Support.h (#1518) 2023-06-09 07:59:05 -07:00
Earle F. Philhower, III 7b04a033b3 Update rp2040.rst (#1515) 2023-06-08 09:49:57 -07:00
madias123 8e4008bf12 Add rebootToBootloader to reboot to bootloader from code (#1514) 2023-06-08 09:48:35 -07:00
29 changed files with 275 additions and 164 deletions
+82
View File
@@ -0,0 +1,82 @@
/*
RP2040 PIO utility class
Copyright (c) 2023 Earle F. Philhower, III <earlephilhower@yahoo.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <Arduino.h>
#include "PIOProgram.h"
#include <map>
static std::map<const pio_program_t *, int> __pioMap[2];
auto_init_mutex(_pioMutex);
PIOProgram::PIOProgram(const pio_program_t *pgm) {
_pgm = pgm;
_pio = nullptr;
_sm = -1;
}
// We leave the INSN loaded in INSN RAM
PIOProgram::~PIOProgram() {
if (_pio) {
pio_sm_unclaim(_pio, _sm);
}
}
// Possibly load into a PIO and allocate a SM
bool PIOProgram::prepare(PIO *pio, int *sm, int *offset) {
CoreMutex m(&_pioMutex);
PIO pi[2] = { pio0, pio1 };
// If it's already loaded into PIO IRAM, try and allocate in that specific PIO
for (int o = 0; o < 2; o++) {
auto p = __pioMap[o].find(_pgm);
if (p != __pioMap[o].end()) {
int idx = pio_claim_unused_sm(pi[o], false);
if (idx >= 0) {
_pio = pi[o];
_sm = idx;
*pio = pi[o];
*sm = idx;
*offset = p->second;
return true;
}
}
}
// Not in any PIO IRAM, so try and add
for (int o = 0; o < 2; o++) {
if (pio_can_add_program(pi[o], _pgm)) {
int idx = pio_claim_unused_sm(pi[o], false);
if (idx >= 0) {
int off = pio_add_program(pi[o], _pgm);
__pioMap[o].insert({_pgm, off});
_pio = pi[o];
_sm = idx;
*pio = pi[o];
*sm = idx;
*offset = off;
return true;
}
}
}
// Nope, no room either for SMs or INSNs
return false;
}
+37
View File
@@ -0,0 +1,37 @@
/*
RP2040 PIO utility class
Copyright (c) 2023 Earle F. Philhower, III <earlephilhower@yahoo.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include <hardware/pio.h>
// Wrapper class for PIO programs, abstracting common operations out
class PIOProgram {
public:
PIOProgram(const pio_program_t *pgm);
~PIOProgram();
// Possibly load into a PIO and allocate a SM
bool prepare(PIO *pio, int *sm, int *offset);
private:
const pio_program_t *_pgm;
PIO _pio;
int _sm;
};
+9 -56
View File
@@ -29,7 +29,9 @@
#include <pico/multicore.h>
#include <pico/rand.h>
#include <pico/util/queue.h>
#include <pico/bootrom.h>
#include "CoreMutex.h"
#include "PIOProgram.h"
#include "ccount.pio.h"
#include <malloc.h>
@@ -153,65 +155,9 @@ private:
class RP2040;
extern RP2040 rp2040;
extern "C" void main1();
class PIOProgram;
extern "C" char __StackLimit;
extern "C" char __bss_end__;
// Wrapper class for PIO programs, abstracting common operations out
// TODO - Add unload/destructor
class PIOProgram {
public:
PIOProgram(const pio_program_t *pgm) {
_pgm = pgm;
}
// Possibly load into a PIO and allocate a SM
bool prepare(PIO *pio, int *sm, int *offset) {
extern mutex_t _pioMutex;
CoreMutex m(&_pioMutex);
// Is there an open slot to run in, first?
if (!_findFreeSM(pio, sm)) {
return false;
}
// Is it loaded on that PIO?
if (_offset[pio_get_index(*pio)] < 0) {
// Nope, need to load it
if (!pio_can_add_program(*pio, _pgm)) {
return false;
}
_offset[pio_get_index(*pio)] = pio_add_program(*pio, _pgm);
}
// Here it's guaranteed loaded, return values
// PIO and SM already set
*offset = _offset[pio_get_index(*pio)];
return true;
}
private:
// Find an unused PIO state machine to grab, returns false when none available
static bool _findFreeSM(PIO *pio, int *sm) {
int idx = pio_claim_unused_sm(pio0, false);
if (idx >= 0) {
*pio = pio0;
*sm = idx;
return true;
}
idx = pio_claim_unused_sm(pio1, false);
if (idx >= 0) {
*pio = pio1;
*sm = idx;
return true;
}
return false;
}
private:
int _offset[2] = { -1, -1 };
const pio_program_t *_pgm;
};
class RP2040 {
public:
RP2040() { /* noop */ }
@@ -317,6 +263,13 @@ public:
reboot();
}
inline void rebootToBootloader() {
reset_usb_boot(0, 0);
while (1) {
continue;
}
}
static void enableDoubleResetBootloader();
void wdt_begin(uint32_t delay_ms) {
+3 -3
View File
@@ -1,5 +1,5 @@
#pragma once
#define ARDUINO_PICO_MAJOR 3
#define ARDUINO_PICO_MINOR 2
#define ARDUINO_PICO_REVISION 2
#define ARDUINO_PICO_VERSION_STR "3.2.2"
#define ARDUINO_PICO_MINOR 3
#define ARDUINO_PICO_REVISION 0
#define ARDUINO_PICO_VERSION_STR "3.3.0"
-4
View File
@@ -29,8 +29,6 @@ extern "C" {
volatile bool __otherCoreIdled = false;
};
mutex_t _pioMutex;
extern void setup();
extern void loop();
@@ -91,8 +89,6 @@ extern "C" int main() {
_REENT_INIT_PTR(_impure_ptr1);
}
mutex_init(&_pioMutex);
rp2040.begin();
initVariant();
@@ -6,10 +6,9 @@
SPDX-License-Identifier: BSD-3-Clause
*/
#include "pico.h"
#include "pico/time.h"
#include "pico/bootrom.h"
#include "pico/binary_info.h"
#include <pico/time.h>
#include <pico/bootrom.h>
#include <pico/binary_info.h>
// PICO_CONFIG: PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS, Window of opportunity for a second press of a reset button to enter BOOTSEL mode (milliseconds), type=int, default=200, group=pico_bootsel_via_double_reset
#ifndef PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS
+2 -2
View File
@@ -54,9 +54,9 @@ author = u'Earle F. Philhower, III'
# built documents.
#
# The short X.Y version.
version = u'3.2.2'
version = u'3.3.0'
# The full version, including alpha/beta/rc tags.
release = u'3.2.2'
release = u'3.3.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
+1 -1
View File
@@ -1,7 +1,7 @@
HTTPClient Library
==================
A simple HTTP requestor that can handle both HTTP and HTTPS requests is
A simple HTTP requester that can handle both HTTP and HTTPS requests is
included as the ``HTTPClient`` library.
Check the examples for use under HTTP and HTTPS configurations. In general,
+4
View File
@@ -95,3 +95,7 @@ void rp2040.enableDoubleResetBootloader()
Add a call anywhere in the sketch to ``rp2040.enableDoubleResetBootloader()`` and
the core will check for a double-tap on reset, and if found will start the USB
bootloader.
void rp2040.rebootToBootloader()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Will reboot the RP2040 into USB UF2 upload mode.
+2 -2
View File
@@ -13,7 +13,7 @@
#define PICO_SDK_VERSION_MAJOR 1
#define PICO_SDK_VERSION_MINOR 5
#define PICO_SDK_VERSION_REVISION 0
#define PICO_SDK_VERSION_STRING "1.5.0"
#define PICO_SDK_VERSION_REVISION 1
#define PICO_SDK_VERSION_STRING "1.5.1"
#endif
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+3 -1
View File
@@ -94,7 +94,9 @@ SECTIONS
/* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from
* FLASH ... we will include any thing excluded here in .data below by default */
*(.init)
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*)
/* Some of these excludes required for PicoDVI library, won't affect most code */
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a: *interp.c.obj *divider.S.obj *PicoDVI.cpp.o *dvi.c.o) .text*)
*(.fini)
/* Pull all c'tors into .text */
*crtbegin.o(.ctors)
+9 -1
View File
@@ -79,6 +79,10 @@ void ADCInput::onReceive(void(*fn)(void)) {
}
bool ADCInput::begin() {
if (_running) {
return false;
}
_running = true;
_isHolding = 0;
@@ -105,7 +109,11 @@ bool ADCInput::begin() {
setFrequency(_freq);
_arb = new AudioBufferManager(_buffers, _bufferWords, 0, INPUT, DMA_SIZE_16);
_arb->begin(DREQ_ADC, (volatile void*)&adc_hw->fifo);
if (!_arb->begin(DREQ_ADC, (volatile void*)&adc_hw->fifo)) {
delete _arb;
_arb = nullptr;
return false;
}
_arb->setCallback(_cb);
adc_fifo_drain();
@@ -20,8 +20,8 @@
*/
#include <Arduino.h>
#include "hardware/dma.h"
#include "hardware/irq.h"
#include <hardware/dma.h>
#include <hardware/irq.h>
#include "AudioBufferManager.h"
static int __channelCount = 0; // # of channels left. When we hit 0, then remove our handler
@@ -78,8 +78,8 @@ AudioBufferManager::~AudioBufferManager() {
dma_channel_abort(_channelDMA[i]);
dma_channel_unclaim(_channelDMA[i]);
dma_channel_acknowledge_irq0(_channelDMA[i]);
__channelCount--;
}
__channelCount--;
if (!__channelCount) {
irq_set_enabled(DMA_IRQ_0, false);
// TODO - how can we know if there are no other parts of the core using DMA0 IRQ??
@@ -21,7 +21,7 @@
#pragma once
#include <Arduino.h>
#include "hardware/dma.h"
#include <hardware/dma.h>
class AudioBufferManager {
public:
+15 -2
View File
@@ -56,6 +56,7 @@ I2S::I2S(PinMode direction) {
}
I2S::~I2S() {
end();
}
bool I2S::setBCLK(pin_size_t pin) {
@@ -145,7 +146,12 @@ bool I2S::begin() {
} else {
_i2s = new PIOProgram(_isOutput ? (_isLSBJ ? &pio_lsbj_out_swap_program : &pio_i2s_out_swap_program) : &pio_i2s_in_swap_program);
}
_i2s->prepare(&_pio, &_sm, &off);
if (!_i2s->prepare(&_pio, &_sm, &off)) {
_running = false;
delete _i2s;
_i2s = nullptr;
return false;
}
if (_isOutput) {
if (_isLSBJ) {
pio_lsbj_out_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps, _swapClocks);
@@ -167,7 +173,14 @@ bool I2S::begin() {
_bufferWords = 64 * (_bps == 32 ? 2 : 1);
}
_arb = new AudioBufferManager(_buffers, _bufferWords, _silenceSample, _isOutput ? OUTPUT : INPUT);
_arb->begin(pio_get_dreq(_pio, _sm, _isOutput), _isOutput ? &_pio->txf[_sm] : (volatile void*)&_pio->rxf[_sm]);
if (!_arb->begin(pio_get_dreq(_pio, _sm, _isOutput), _isOutput ? &_pio->txf[_sm] : (volatile void*)&_pio->rxf[_sm])) {
_running = false;
delete _arb;
_arb = nullptr;
delete _i2s;
_i2s = nullptr;
return false;
}
_arb->setCallback(_cb);
pio_sm_set_enabled(_pio, _sm, true);
+4 -4
View File
@@ -5,11 +5,11 @@
#include "OpenPDMFilter.h"
extern "C" {
#include "hardware/pio.h"
#include "hardware/dma.h"
#include "hardware/clocks.h"
#include <hardware/pio.h>
#include <hardware/dma.h>
#include <hardware/clocks.h>
}
#include "hardware/sync.h"
#include <hardware/sync.h>
#include "pdm.pio.h"
static PIOProgram _pdmPgm(&pdm_pio_program);
+11 -1
View File
@@ -100,6 +100,10 @@ void PWMAudio::onTransmit(void(*fn)(void)) {
}
bool PWMAudio::begin() {
if (_running) {
return false;
}
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");
@@ -118,7 +122,13 @@ bool PWMAudio::begin() {
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);
if (!_arb->begin(pwm_get_dreq(pwm_gpio_to_slice_num(_pin)), (volatile void*)ccAddr)) {
_running = false;
delete _arb;
_arb = nullptr;
return false;
}
_arb->setCallback(_cb);
return true;
-67
View File
@@ -237,73 +237,6 @@ void HTTPServer::serveStatic(const char* uri, FS& fs, const char* path, const ch
_addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header));
}
void HTTPServer::httpHandleClient() {
bool keepCurrentClient = false;
bool callYield = false;
if (_currentClient->connected()) {
switch (_currentStatus) {
case HC_NONE:
// No-op to avoid C++ compiler warning
break;
case HC_WAIT_READ:
// Wait for data from client to become available
if (_currentClient->available()) {
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();
/* 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;
}
callYield = true;
}
break;
case HC_WAIT_CLOSE:
// Wait for client to close the connection
if (millis() - _statusChange <= HTTP_MAX_CLOSE_WAIT) {
keepCurrentClient = true;
callYield = true;
}
}
}
if (!keepCurrentClient) {
if (_currentClient) {
delete _currentClient;
_currentClient = nullptr;
}
_currentStatus = HC_NONE;
_currentUpload.reset();
}
if (callYield) {
yield();
}
}
void HTTPServer::httpClose() {
_currentStatus = HC_NONE;
+1 -1
View File
@@ -41,6 +41,7 @@ enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH };
#endif
#define HTTP_MAX_DATA_WAIT 5000 //ms to wait for the client to send the request
#define HTTP_MAX_DATA_AVAILABLE_WAIT 30 //ms to wait for the client to send the request when there is another client with data available
#define HTTP_MAX_POST_WAIT 5000 //ms to wait for POST data to arrive
#define HTTP_MAX_SEND_WAIT 5000 //ms to wait for data chunk to be ACKed
#define HTTP_MAX_CLOSE_WAIT 2000 //ms to wait for the client to close the connection
@@ -74,7 +75,6 @@ public:
virtual ~HTTPServer();
virtual void httpClose();
virtual void httpHandleClient();
bool authenticate(const char * username, const char * password);
void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char* realm = nullptr, const String& authFailMsg = String(""));
+79 -5
View File
@@ -90,19 +90,93 @@ void WebServerTemplate<ServerType, DefaultPort>::handleClient() {
_currentClient = nullptr;
}
ClientType client = _server.available();
if (!client) {
_currentClient = new ClientType(_server.available());
if (!_currentClient) {
if (_nullDelay) {
delay(1);
}
return;
}
_currentClient = new ClientType(client);
_currentStatus = HC_WAIT_READ;
_statusChange = millis();
}
httpHandleClient();
bool keepCurrentClient = false;
bool callYield = false;
if (_currentClient->connected()) {
switch (_currentStatus) {
case HC_NONE:
// No-op to avoid C++ compiler warning
break;
case HC_WAIT_READ:
// Wait for data from client to become available
if (_currentClient->available()) {
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();
/* 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(): waiting for more data
unsigned long timeSinceChange = millis() - _statusChange;
// Use faster connection drop timeout if any other client has data
// or the buffer of pending clients is full
if ((_server.hasClientData() || _server.hasMaxPendingClients())
&& timeSinceChange > HTTP_MAX_DATA_AVAILABLE_WAIT) {
//log_v("webserver: closing since there's another connection to read from\r\n");
} else {
if (timeSinceChange > HTTP_MAX_DATA_WAIT) {
//log_v("webserver: closing after read timeout\r\n");
} else {
keepCurrentClient = true;
}
}
callYield = true;
}
break;
case HC_WAIT_CLOSE:
// Wait for client to close the connection
if (millis() - _statusChange <= HTTP_MAX_CLOSE_WAIT) {
keepCurrentClient = true;
callYield = true;
}
}
}
if (!keepCurrentClient) {
if (_currentClient) {
delete _currentClient;
_currentClient = nullptr;
}
_currentStatus = HC_NONE;
_currentUpload.reset();
}
if (callYield) {
yield();
}
}
template <typename ServerType, int DefaultPort>
@@ -20,10 +20,10 @@
#include "CYW43shim.h"
extern "C" {
#include "cyw43.h"
#include "cyw43_stats.h"
#include <cyw43.h>
#include <cyw43_stats.h>
}
#include "pico/cyw43_arch.h"
#include <pico/cyw43_arch.h>
#include <Arduino.h>
// From cyw43_ctrl.c
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "framework-arduinopico",
"version": "1.30202.0",
"version": "1.30300.0",
"description": "Arduino Wiring-based Framework (RPi Pico RP2040)",
"keywords": [
"framework",
+1 -1
View File
@@ -20,7 +20,7 @@
# https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5---3rd-party-Hardware-specification
name=Raspberry Pi RP2040 Boards
version=3.2.2
version=3.3.0
runtime.tools.pqt-gcc.path={runtime.platform.path}/system/arm-none-eabi
runtime.tools.pqt-python3.path={runtime.platform.path}/system/python3