Add Arduino Nano ESP32 target (#8417)
* USB: enable DFU interface and stub
* nano_nora: add Arduino Nano ESP32 board support
* [pin_remap 1/3] platform: define ARDUINO_CORE_BUILD when building core files
* [pin_remap 2/3] core,libs: add pin remap hooks
* [pin_remap 3/3] nano_nora: implement and enable pin remap
* nano_nora: fix: reset all matrix connections at boot
* nano_nora: add recovery image for release/v2.x
* nano_nora: use official Arduino branding
* nano_nora: core split + recovery mode rework
Use an absolute address in SPIRAM to store the magic tokens, almost at the
end of the memory, to avoid the markers from being overwritten on any kind
of sketch and core combination.
Also, only start the recovery once if a valid binary is present in the
Flash, by immediately setting that for the next boot when recovery
starts.
* platform: fix: use {compiler.sdk.path} for sdk path
In preparation for the sdk -> tool transition
* package_index: remove dfu-util from template
The tool is already available in mainline package_index.json
* on_release: allow single board packages
---------
Co-authored-by: Luca Burelli <l.burelli@arduino.cc>
This commit is contained in:
parent
1577701a71
commit
9b4622dc40
22 changed files with 886 additions and 21 deletions
29
.github/scripts/on-release.sh
vendored
29
.github/scripts/on-release.sh
vendored
|
|
@ -36,6 +36,12 @@ echo "Event: $GITHUB_EVENT_NAME, Repo: $GITHUB_REPOSITORY, Path: $GITHUB_WORKSPA
|
|||
echo "Action: $action, Branch: $RELEASE_BRANCH, ID: $RELEASE_ID"
|
||||
echo "Tag: $RELEASE_TAG, Draft: $draft, Pre-Release: $RELEASE_PRE"
|
||||
|
||||
# Try extracting something like a JSON with a "boards" array/element and "vendor" fields
|
||||
BOARDS=`echo $RELEASE_BODY | grep -Pzo '(?s){.*}' | jq -r '.boards[]? // .boards? // empty' | xargs echo -n 2>/dev/null`
|
||||
VENDOR=`echo $RELEASE_BODY | grep -Pzo '(?s){.*}' | jq -r '.vendor? // empty' | xargs echo -n 2>/dev/null`
|
||||
if ! [ -z "${BOARDS}" ]; then echo "Releasing board(s): $BOARDS" ; fi
|
||||
if ! [ -z "${VENDOR}" ]; then echo "Setting packager: $VENDOR" ; fi
|
||||
|
||||
function get_file_size(){
|
||||
local file="$1"
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
|
|
@ -171,12 +177,26 @@ mkdir -p "$PKG_DIR/tools"
|
|||
|
||||
# Copy all core files to the package folder
|
||||
echo "Copying files for packaging ..."
|
||||
cp -f "$GITHUB_WORKSPACE/boards.txt" "$PKG_DIR/"
|
||||
if [ -z "${BOARDS}" ]; then
|
||||
# Copy all variants
|
||||
cp -f "$GITHUB_WORKSPACE/boards.txt" "$PKG_DIR/"
|
||||
cp -Rf "$GITHUB_WORKSPACE/variants" "$PKG_DIR/"
|
||||
else
|
||||
# Remove all entries not starting with any board code or "menu." from boards.txt
|
||||
cat "$GITHUB_WORKSPACE/boards.txt" | grep "^menu\." > "$PKG_DIR/boards.txt"
|
||||
for board in ${BOARDS} ; do
|
||||
cat "$GITHUB_WORKSPACE/boards.txt" | grep "^${board}\." >> "$PKG_DIR/boards.txt"
|
||||
done
|
||||
# Copy only relevant variant files
|
||||
mkdir "$PKG_DIR/variants/"
|
||||
for variant in `cat ${PKG_DIR}/boards.txt | grep "\.variant=" | cut -d= -f2` ; do
|
||||
cp -Rf "$GITHUB_WORKSPACE/variants/${variant}" "$PKG_DIR/variants/"
|
||||
done
|
||||
fi
|
||||
cp -f "$GITHUB_WORKSPACE/package.json" "$PKG_DIR/"
|
||||
cp -f "$GITHUB_WORKSPACE/programmers.txt" "$PKG_DIR/"
|
||||
cp -Rf "$GITHUB_WORKSPACE/cores" "$PKG_DIR/"
|
||||
cp -Rf "$GITHUB_WORKSPACE/libraries" "$PKG_DIR/"
|
||||
cp -Rf "$GITHUB_WORKSPACE/variants" "$PKG_DIR/"
|
||||
cp -f "$GITHUB_WORKSPACE/tools/espota.exe" "$PKG_DIR/tools/"
|
||||
cp -f "$GITHUB_WORKSPACE/tools/espota.py" "$PKG_DIR/tools/"
|
||||
cp -f "$GITHUB_WORKSPACE/tools/gen_esp32part.py" "$PKG_DIR/tools/"
|
||||
|
|
@ -209,6 +229,11 @@ sed 's/debug.server.openocd.scripts_dir={runtime.platform.path}\/tools\/openocd-
|
|||
sed 's/debug.server.openocd.scripts_dir.windows={runtime.platform.path}\\tools\\openocd-esp32\\share\\openocd\\scripts\\/debug.server.openocd.scripts_dir.windows=\{runtime.tools.openocd-esp32.path\}\\share\\openocd\\scripts\\/g' \
|
||||
> "$PKG_DIR/platform.txt"
|
||||
|
||||
if ! [ -z ${VENDOR} ]; then
|
||||
# Append vendor name to platform.txt to create a separate section
|
||||
sed -i "/^name=.*/s/$/ ($VENDOR)/" "$PKG_DIR/platform.txt"
|
||||
fi
|
||||
|
||||
# Add header with version information
|
||||
echo "Generating core_version.h ..."
|
||||
ver_define=`echo $RELEASE_TAG | tr "[:lower:].\055" "[:upper:]_"`
|
||||
|
|
|
|||
51
boards.txt
51
boards.txt
|
|
@ -24112,3 +24112,54 @@ nebulas3.menu.EraseFlash.all=Enabled
|
|||
nebulas3.menu.EraseFlash.all.upload.erase_cmd=-e
|
||||
|
||||
##############################################################
|
||||
|
||||
nano_nora.name=Arduino Nano ESP32
|
||||
nano_nora.vid.0=0x2341
|
||||
nano_nora.pid.0=0x0070
|
||||
nano_nora.upload_port.0.vid=0x2341
|
||||
nano_nora.upload_port.0.pid=0x0070
|
||||
|
||||
nano_nora.bootloader.tool=esptool_py
|
||||
nano_nora.bootloader.tool.default=esptool_py
|
||||
|
||||
nano_nora.upload.tool=dfu-util
|
||||
nano_nora.upload.tool.default=dfu-util
|
||||
nano_nora.upload.tool.network=esp_ota
|
||||
nano_nora.upload.protocol=serial
|
||||
nano_nora.upload.maximum_size=3145728
|
||||
nano_nora.upload.maximum_data_size=327680
|
||||
nano_nora.upload.use_1200bps_touch=false
|
||||
nano_nora.upload.wait_for_upload_port=false
|
||||
|
||||
nano_nora.serial.disableDTR=false
|
||||
nano_nora.serial.disableRTS=false
|
||||
|
||||
nano_nora.build.tarch=xtensa
|
||||
nano_nora.build.bootloader_addr=0x0
|
||||
nano_nora.build.target=esp32s3
|
||||
nano_nora.build.mcu=esp32s3
|
||||
nano_nora.build.core=esp32
|
||||
nano_nora.build.variant=arduino_nano_nora
|
||||
nano_nora.build.board=NANO_ESP32
|
||||
nano_nora.build.code_debug=0
|
||||
|
||||
nano_nora.build.usb_mode=0
|
||||
nano_nora.build.cdc_on_boot=1
|
||||
nano_nora.build.msc_on_boot=0
|
||||
nano_nora.build.dfu_on_boot=1
|
||||
nano_nora.build.f_cpu=240000000L
|
||||
nano_nora.build.flash_size=16MB
|
||||
nano_nora.build.flash_freq=80m
|
||||
nano_nora.build.flash_mode=dio
|
||||
nano_nora.build.boot=qio
|
||||
nano_nora.build.boot_freq=80m
|
||||
nano_nora.build.partitions=app3M_fat9M_fact512k_16MB
|
||||
nano_nora.build.defines=-DBOARD_HAS_PIN_REMAP -DBOARD_HAS_PSRAM '-DUSB_MANUFACTURER="Arduino"' '-DUSB_PRODUCT="Nano ESP32"'
|
||||
nano_nora.build.loop_core=-DARDUINO_RUNNING_CORE=1
|
||||
nano_nora.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=1
|
||||
nano_nora.build.psram_type=opi
|
||||
nano_nora.build.memory_type={build.boot}_{build.psram_type}
|
||||
|
||||
nano_nora.tools.esptool_py.program.pattern_args=--chip {build.mcu} --port "{serial.port}" --before default_reset --after hard_reset write_flash -z --flash_mode {build.flash_mode} --flash_freq {build.flash_freq} --flash_size {build.flash_size} {build.bootloader_addr} "{build.path}/{build.project_name}.bootloader.bin" 0x8000 "{build.path}/{build.project_name}.partitions.bin" 0xe000 "{runtime.platform.path}/tools/partitions/boot_app0.bin" 0xf70000 "{build.variant.path}/extra/nora_recovery/nora_recovery.ino.bin" 0x10000 "{build.path}/{build.project_name}.bin"
|
||||
|
||||
##############################################################
|
||||
|
|
|
|||
|
|
@ -110,13 +110,13 @@
|
|||
#define analogInPinToBit(P) (P)
|
||||
#if SOC_GPIO_PIN_COUNT <= 32
|
||||
#define digitalPinToPort(pin) (0)
|
||||
#define digitalPinToBitMask(pin) (1UL << (pin))
|
||||
#define digitalPinToBitMask(pin) (1UL << digitalPinToGPIONumber(pin))
|
||||
#define portOutputRegister(port) ((volatile uint32_t*)GPIO_OUT_REG)
|
||||
#define portInputRegister(port) ((volatile uint32_t*)GPIO_IN_REG)
|
||||
#define portModeRegister(port) ((volatile uint32_t*)GPIO_ENABLE_REG)
|
||||
#elif SOC_GPIO_PIN_COUNT <= 64
|
||||
#define digitalPinToPort(pin) (((pin)>31)?1:0)
|
||||
#define digitalPinToBitMask(pin) (1UL << (((pin)>31)?((pin)-32):(pin)))
|
||||
#define digitalPinToPort(pin) ((digitalPinToGPIONumber(pin)>31)?1:0)
|
||||
#define digitalPinToBitMask(pin) (1UL << (digitalPinToGPIONumber(pin)&31))
|
||||
#define portOutputRegister(port) ((volatile uint32_t*)((port)?GPIO_OUT1_REG:GPIO_OUT_REG))
|
||||
#define portInputRegister(port) ((volatile uint32_t*)((port)?GPIO_IN1_REG:GPIO_IN_REG))
|
||||
#define portModeRegister(port) ((volatile uint32_t*)((port)?GPIO_ENABLE1_REG:GPIO_ENABLE_REG))
|
||||
|
|
@ -220,5 +220,6 @@ void noTone(uint8_t _pin);
|
|||
#endif /* __cplusplus */
|
||||
|
||||
#include "pins_arduino.h"
|
||||
#include "io_pin_remap.h"
|
||||
|
||||
#endif /* _ESP32_CORE_ARDUINO_H_ */
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ void ARDUINO_ISR_ATTR interruptFunctional(void* arg)
|
|||
void attachInterrupt(uint8_t pin, std::function<void(void)> intRoutine, int mode)
|
||||
{
|
||||
// use the local interrupt routine which takes the ArgStructure as argument
|
||||
__attachInterruptFunctionalArg (pin, (voidFuncPtrArg)interruptFunctional, new InterruptArgStructure{intRoutine}, mode, true);
|
||||
__attachInterruptFunctionalArg (digitalPinToGPIONumber(pin), (voidFuncPtrArg)interruptFunctional, new InterruptArgStructure{intRoutine}, mode, true);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <inttypes.h>
|
||||
|
||||
#include "pins_arduino.h"
|
||||
#include "io_pin_remap.h"
|
||||
#include "HardwareSerial.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "driver/uart.h"
|
||||
|
|
@ -370,6 +371,10 @@ void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, in
|
|||
}
|
||||
}
|
||||
|
||||
// map logical pins to GPIO numbers
|
||||
rxPin = digitalPinToGPIONumber(rxPin);
|
||||
txPin = digitalPinToGPIONumber(txPin);
|
||||
|
||||
if(_uart) {
|
||||
// in this case it is a begin() over a previous begin() - maybe to change baud rate
|
||||
// thus do not disable debug output
|
||||
|
|
@ -554,6 +559,12 @@ bool HardwareSerial::setPins(int8_t rxPin, int8_t txPin, int8_t ctsPin, int8_t r
|
|||
return false;
|
||||
}
|
||||
|
||||
// map logical pins to GPIO numbers
|
||||
rxPin = digitalPinToGPIONumber(rxPin);
|
||||
txPin = digitalPinToGPIONumber(txPin);
|
||||
ctsPin = digitalPinToGPIONumber(ctsPin);
|
||||
rtsPin = digitalPinToGPIONumber(rtsPin);
|
||||
|
||||
// uartSetPins() checks if pins are valid for each function and for the SoC
|
||||
bool retCode = uartSetPins(_uart, rxPin, txPin, ctsPin, rtsPin);
|
||||
if (retCode) {
|
||||
|
|
|
|||
|
|
@ -47,8 +47,14 @@
|
|||
#define USB_WEBUSB_URL "https://espressif.github.io/arduino-esp32/webusb.html"
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_DFU
|
||||
__attribute__((weak, unused)) uint16_t load_dfu_ota_descriptor(uint8_t * dst, uint8_t * itf) {
|
||||
return 0;
|
||||
}
|
||||
#endif /* CFG_TUD_DFU */
|
||||
|
||||
#if CFG_TUD_DFU_RUNTIME
|
||||
static uint16_t load_dfu_descriptor(uint8_t * dst, uint8_t * itf)
|
||||
__attribute__((unused)) static uint16_t load_dfu_descriptor(uint8_t * dst, uint8_t * itf)
|
||||
{
|
||||
#define DFU_ATTRS (DFU_ATTR_CAN_DOWNLOAD | DFU_ATTR_CAN_UPLOAD | DFU_ATTR_MANIFESTATION_TOLERANT)
|
||||
|
||||
|
|
@ -185,7 +191,7 @@ bool ESPUSB::begin(){
|
|||
.webusb_enabled = webusb_enabled,
|
||||
.webusb_url = webusb_url.c_str()
|
||||
};
|
||||
_started = tinyusb_init(&tinyusb_device_config) == ESP_OK;
|
||||
_started = tinyusb_init(&tinyusb_device_config) == ESP_OK;
|
||||
}
|
||||
return _started;
|
||||
}
|
||||
|
|
@ -203,7 +209,9 @@ ESPUSB::operator bool() const
|
|||
}
|
||||
|
||||
bool ESPUSB::enableDFU(){
|
||||
#if CFG_TUD_DFU_RUNTIME
|
||||
#if CFG_TUD_DFU
|
||||
return tinyusb_enable_interface(USB_INTERFACE_DFU, TUD_DFU_DESC_LEN(1), load_dfu_ota_descriptor) == ESP_OK;
|
||||
#elif CFG_TUD_DFU_RUNTIME
|
||||
return tinyusb_enable_interface(USB_INTERFACE_DFU, TUD_DFU_RT_DESC_LEN, load_dfu_descriptor) == ESP_OK;
|
||||
#endif /* CFG_TUD_DFU_RUNTIME */
|
||||
return false;
|
||||
|
|
|
|||
110
cores/esp32/io_pin_remap.h
Normal file
110
cores/esp32/io_pin_remap.h
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
#ifndef __IO_PIN_REMAP_H__
|
||||
#define __IO_PIN_REMAP_H__
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#if defined(BOARD_HAS_PIN_REMAP) && !defined(BOARD_USES_HW_GPIO_NUMBERS)
|
||||
|
||||
// Pin remapping functions
|
||||
int8_t digitalPinToGPIONumber(int8_t digitalPin);
|
||||
int8_t digitalPinFromGPIONumber(int8_t gpioPin);
|
||||
|
||||
// Apply pin remapping to API only when building libraries and user sketch
|
||||
#ifndef ARDUINO_CORE_BUILD
|
||||
|
||||
// Override APIs requiring pin remapping
|
||||
|
||||
// cores/esp32/Arduino.h
|
||||
#define pulseInLong(pin, state, timeout) pulseInLong(digitalPinToGPIONumber(pin), state, timeout)
|
||||
#define pulseIn(pin, state, timeout) pulseIn(digitalPinToGPIONumber(pin), state, timeout)
|
||||
#define noTone(_pin) noTone(digitalPinToGPIONumber(_pin))
|
||||
#define tone(_pin, frequency, duration) tone(digitalPinToGPIONumber(_pin), frequency, duration)
|
||||
|
||||
// cores/esp32/esp32-hal.h
|
||||
#define analogGetChannel(pin) analogGetChannel(digitalPinToGPIONumber(pin))
|
||||
#define analogWrite(pin, value) analogWrite(digitalPinToGPIONumber(pin), value)
|
||||
|
||||
// cores/esp32/esp32-hal-adc.h
|
||||
#define adcAttachPin(pin) adcAttachPin(digitalPinToGPIONumber(pin))
|
||||
#define analogRead(pin) analogRead(digitalPinToGPIONumber(pin))
|
||||
#define analogReadMilliVolts(pin) analogReadMilliVolts(digitalPinToGPIONumber(pin))
|
||||
#define analogSetPinAttenuation(pin, attenuation) analogSetPinAttenuation(digitalPinToGPIONumber(pin), attenuation)
|
||||
#define analogSetVRefPin(pin) analogSetVRefPin(digitalPinToGPIONumber(pin))
|
||||
|
||||
// cores/esp32/esp32-hal-dac.h
|
||||
#define dacDisable(pin) dacDisable(digitalPinToGPIONumber(pin))
|
||||
#define dacWrite(pin, value) dacWrite(digitalPinToGPIONumber(pin), value)
|
||||
|
||||
// cores/esp32/esp32-hal-gpio.h
|
||||
#define analogChannelToDigitalPin(channel) gpioNumberToDigitalPin(analogChannelToDigitalPin(channel))
|
||||
#define digitalPinToAnalogChannel(pin) digitalPinToAnalogChannel(digitalPinToGPIONumber(pin))
|
||||
#define digitalPinToTouchChannel(pin) digitalPinToTouchChannel(digitalPinToGPIONumber(pin))
|
||||
#define digitalRead(pin) digitalRead(digitalPinToGPIONumber(pin))
|
||||
#define attachInterruptArg(pin, fcn, arg, mode) attachInterruptArg(digitalPinToGPIONumber(pin), fcn, arg, mode)
|
||||
#define attachInterrupt(pin, fcn, mode) attachInterrupt(digitalPinToGPIONumber(pin), fcn, mode)
|
||||
#define detachInterrupt(pin) detachInterrupt(digitalPinToGPIONumber(pin))
|
||||
#define digitalWrite(pin, val) digitalWrite(digitalPinToGPIONumber(pin), val)
|
||||
#define pinMode(pin, mode) pinMode(digitalPinToGPIONumber(pin), mode)
|
||||
|
||||
// cores/esp32/esp32-hal-i2c.h
|
||||
#define i2cInit(i2c_num, sda, scl, clk_speed) i2cInit(i2c_num, digitalPinToGPIONumber(sda), digitalPinToGPIONumber(scl), clk_speed)
|
||||
|
||||
// cores/esp32/esp32-hal-i2c-slave.h
|
||||
#define i2cSlaveInit(num, sda, scl, slaveID, frequency, rx_len, tx_len) i2cSlaveInit(num, digitalPinToGPIONumber(sda), digitalPinToGPIONumber(scl), slaveID, frequency, rx_len, tx_len)
|
||||
|
||||
// cores/esp32/esp32-hal-ledc.h
|
||||
#define ledcAttachPin(pin, channel) ledcAttachPin(digitalPinToGPIONumber(pin), channel)
|
||||
#define ledcDetachPin(pin) ledcDetachPin(digitalPinToGPIONumber(pin))
|
||||
|
||||
// cores/esp32/esp32-hal-matrix.h
|
||||
#define pinMatrixInAttach(pin, signal, inverted) pinMatrixInAttach(digitalPinToGPIONumber(pin), signal, inverted)
|
||||
#define pinMatrixOutAttach(pin, function, invertOut, invertEnable) pinMatrixOutAttach(digitalPinToGPIONumber(pin), function, invertOut, invertEnable)
|
||||
#define pinMatrixOutDetach(pin, invertOut, invertEnable) pinMatrixOutDetach(digitalPinToGPIONumber(pin), invertOut, invertEnable)
|
||||
|
||||
// cores/esp32/esp32-hal-rgb-led.h
|
||||
#define neopixelWrite(pin, red_val, green_val, blue_val) neopixelWrite(digitalPinToGPIONumber(pin), red_val, green_val, blue_val)
|
||||
|
||||
// cores/esp32/esp32-hal-rmt.h
|
||||
#define rmtInit(pin, tx_not_rx, memsize) rmtInit(digitalPinToGPIONumber(pin), tx_not_rx, memsize)
|
||||
|
||||
// cores/esp32/esp32-hal-sigmadelta.h
|
||||
#define sigmaDeltaSetup(pin, channel, freq) sigmaDeltaSetup(digitalPinToGPIONumber(pin), channel, freq)
|
||||
#define sigmaDeltaDetachPin(pin) sigmaDeltaDetachPin(digitalPinToGPIONumber(pin))
|
||||
|
||||
// cores/esp32/esp32-hal-spi.h
|
||||
#define spiAttachSCK(spi, sck) spiAttachSCK(spi, digitalPinToGPIONumber(sck))
|
||||
#define spiAttachMISO(spi, miso) spiAttachMISO(spi, digitalPinToGPIONumber(miso))
|
||||
#define spiAttachMOSI(spi, mosi) spiAttachMOSI(spi, digitalPinToGPIONumber(mosi))
|
||||
#define spiDetachSCK(spi, sck) spiDetachSCK(spi, digitalPinToGPIONumber(sck))
|
||||
#define spiDetachMISO(spi, miso) spiDetachMISO(spi, digitalPinToGPIONumber(miso))
|
||||
#define spiDetachMOSI(spi, mosi) spiDetachMOSI(spi, digitalPinToGPIONumber(mosi))
|
||||
#define spiAttachSS(spi, cs_num, ss) spiAttachSS(spi, cs_num, digitalPinToGPIONumber(ss))
|
||||
#define spiDetachSS(spi, ss) spiDetachSS(spi, digitalPinToGPIONumber(ss))
|
||||
|
||||
// cores/esp32/esp32-hal-touch.h
|
||||
#define touchInterruptGetLastStatus(pin) touchInterruptGetLastStatus(digitalPinToGPIONumber(pin))
|
||||
#define touchRead(pin) touchRead(digitalPinToGPIONumber(pin))
|
||||
#define touchAttachInterruptArg(pin, userFunc, arg, threshold) touchAttachInterruptArg(digitalPinToGPIONumber(pin), userFunc, arg, threshold)
|
||||
#define touchAttachInterrupt(pin, userFunc, threshold) touchAttachInterrupt(digitalPinToGPIONumber(pin), userFunc, threshold)
|
||||
#define touchDetachInterrupt(pin) touchDetachInterrupt(digitalPinToGPIONumber(pin))
|
||||
#define touchSleepWakeUpEnable(pin, threshold) touchSleepWakeUpEnable(digitalPinToGPIONumber(pin), threshold)
|
||||
|
||||
// cores/esp32/esp32-hal-uart.h
|
||||
#define uartBegin(uart_nr, baudrate, config, rxPin, txPin, rx_buffer_size, tx_buffer_size, inverted, rxfifo_full_thrhd) \
|
||||
uartBegin(uart_nr, baudrate, config, digitalPinToGPIONumber(rxPin), digitalPinToGPIONumber(txPin), rx_buffer_size, tx_buffer_size, inverted, rxfifo_full_thrhd)
|
||||
#define uartSetPins(uart, rxPin, txPin, ctsPin, rtsPin) \
|
||||
uartSetPins(uart, digitalPinToGPIONumber(rxPin), digitalPinToGPIONumber(txPin), digitalPinToGPIONumber(ctsPin), digitalPinToGPIONumber(rtsPin))
|
||||
#define uartDetachPins(uart, rxPin, txPin, ctsPin, rtsPin) \
|
||||
uartDetachPins(uart, digitalPinToGPIONumber(rxPin), digitalPinToGPIONumber(txPin), digitalPinToGPIONumber(ctsPin), digitalPinToGPIONumber(rtsPin))
|
||||
|
||||
#endif // ARDUINO_CORE_BUILD
|
||||
|
||||
#else
|
||||
|
||||
// pin remapping disabled: use stubs
|
||||
#define digitalPinToGPIONumber(digitalPin) (digitalPin)
|
||||
#define gpioNumberToDigitalPin(gpioNumber) (gpioNumber)
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __GPIO_PIN_REMAP_H__ */
|
||||
|
|
@ -317,24 +317,24 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock
|
|||
int I2SClass::_applyPinSetting(){
|
||||
if(_driverInstalled){
|
||||
esp_i2s::i2s_pin_config_t pin_config = {
|
||||
.bck_io_num = _sckPin,
|
||||
.ws_io_num = _fsPin,
|
||||
.bck_io_num = digitalPinToGPIONumber(_sckPin),
|
||||
.ws_io_num = digitalPinToGPIONumber(_fsPin),
|
||||
.data_out_num = I2S_PIN_NO_CHANGE,
|
||||
.data_in_num = I2S_PIN_NO_CHANGE
|
||||
};
|
||||
if (_state == I2S_STATE_DUPLEX){ // duplex
|
||||
pin_config.data_out_num = _outSdPin;
|
||||
pin_config.data_in_num = _inSdPin;
|
||||
pin_config.data_out_num = digitalPinToGPIONumber(_outSdPin);
|
||||
pin_config.data_in_num = digitalPinToGPIONumber(_inSdPin);
|
||||
}else{ // simplex
|
||||
if(_state == I2S_STATE_RECEIVER){
|
||||
pin_config.data_out_num = I2S_PIN_NO_CHANGE;
|
||||
pin_config.data_in_num = _sdPin;
|
||||
pin_config.data_in_num = digitalPinToGPIONumber(_sdPin);
|
||||
}else if(_state == I2S_STATE_TRANSMITTER){
|
||||
pin_config.data_out_num = _sdPin;
|
||||
pin_config.data_out_num = digitalPinToGPIONumber(_sdPin);
|
||||
pin_config.data_in_num = I2S_PIN_NO_CHANGE;
|
||||
}else{
|
||||
pin_config.data_out_num = I2S_PIN_NO_CHANGE;
|
||||
pin_config.data_in_num = _sdPin;
|
||||
pin_config.data_in_num = digitalPinToGPIONumber(_sdPin);
|
||||
}
|
||||
}
|
||||
if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
// limitations under the License.
|
||||
|
||||
#include "pins_arduino.h"
|
||||
#include "io_pin_remap.h"
|
||||
#include "SD_MMC.h"
|
||||
#ifdef SOC_SDMMC_HOST_SUPPORTED
|
||||
#include "vfs_api.h"
|
||||
|
|
@ -54,6 +55,15 @@ bool SDMMCFS::setPins(int clk, int cmd, int d0, int d1, int d2, int d3)
|
|||
log_e("SD_MMC.setPins must be called before SD_MMC.begin");
|
||||
return false;
|
||||
}
|
||||
|
||||
// map logical pins to GPIO numbers
|
||||
clk = digitalPinToGPIONumber(clk);
|
||||
cmd = digitalPinToGPIONumber(cmd);
|
||||
d0 = digitalPinToGPIONumber(d0);
|
||||
d1 = digitalPinToGPIONumber(d1);
|
||||
d2 = digitalPinToGPIONumber(d2);
|
||||
d3 = digitalPinToGPIONumber(d3);
|
||||
|
||||
#ifdef SOC_SDMMC_USE_GPIO_MATRIX
|
||||
// SoC supports SDMMC pin configuration via GPIO matrix. Save the pins for later use in SDMMCFS::begin.
|
||||
_pin_clk = (int8_t) clk;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
*/
|
||||
|
||||
#include "SPI.h"
|
||||
#include "io_pin_remap.h"
|
||||
#include "esp32-hal-log.h"
|
||||
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@
|
|||
},
|
||||
{
|
||||
"name": "ESP32-C3 Dev Board"
|
||||
},
|
||||
{
|
||||
"name": "Arduino Nano ESP32"
|
||||
}
|
||||
],
|
||||
"toolsDependencies": [
|
||||
|
|
@ -85,6 +88,11 @@
|
|||
"packager": "esp32",
|
||||
"name": "mklittlefs",
|
||||
"version": "3.0.0-gnu12-dc7f933"
|
||||
},
|
||||
{
|
||||
"packager": "arduino",
|
||||
"name": "dfu-util",
|
||||
"version": "0.11.0-arduino5"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
29
platform.txt
29
platform.txt
|
|
@ -175,8 +175,8 @@ recipe.hooks.prebuild.3.pattern.windows=cmd /c if not exist "{build.path}\partit
|
|||
|
||||
# Check if custom bootloader exist: source > variant > build.boot
|
||||
recipe.hooks.prebuild.4.pattern_args=--chip {build.mcu} elf2image --flash_mode {build.flash_mode} --flash_freq {build.flash_freq} --flash_size {build.flash_size} -o
|
||||
recipe.hooks.prebuild.4.pattern=bash -c "[ -f "{build.source.path}"/bootloader.bin ] && cp -f "{build.source.path}"/bootloader.bin "{build.path}"/{build.project_name}.bootloader.bin || ( [ -f "{build.variant.path}"/{build.custom_bootloader}.bin ] && cp "{build.variant.path}"/{build.custom_bootloader}.bin "{build.path}"/{build.project_name}.bootloader.bin || "{tools.esptool_py.path}"/{tools.esptool_py.cmd} {recipe.hooks.prebuild.4.pattern_args} "{build.path}"/{build.project_name}.bootloader.bin "{runtime.platform.path}"/tools/sdk/{build.mcu}/bin/bootloader_{build.boot}_{build.boot_freq}.elf )"
|
||||
recipe.hooks.prebuild.4.pattern.linux=bash -c "[ -f "{build.source.path}"/bootloader.bin ] && cp -f "{build.source.path}"/bootloader.bin "{build.path}"/{build.project_name}.bootloader.bin || ( [ -f "{build.variant.path}"/{build.custom_bootloader}.bin ] && cp "{build.variant.path}"/{build.custom_bootloader}.bin "{build.path}"/{build.project_name}.bootloader.bin || python3 "{tools.esptool_py.path}"/{tools.esptool_py.cmd} {recipe.hooks.prebuild.4.pattern_args} "{build.path}"/{build.project_name}.bootloader.bin "{runtime.platform.path}"/tools/sdk/{build.mcu}/bin/bootloader_{build.boot}_{build.boot_freq}.elf )"
|
||||
recipe.hooks.prebuild.4.pattern=bash -c "[ -f "{build.source.path}"/bootloader.bin ] && cp -f "{build.source.path}"/bootloader.bin "{build.path}"/{build.project_name}.bootloader.bin || ( [ -f "{build.variant.path}"/{build.custom_bootloader}.bin ] && cp "{build.variant.path}"/{build.custom_bootloader}.bin "{build.path}"/{build.project_name}.bootloader.bin || "{tools.esptool_py.path}"/{tools.esptool_py.cmd} {recipe.hooks.prebuild.4.pattern_args} "{build.path}"/{build.project_name}.bootloader.bin "{compiler.sdk.path}"/bin/bootloader_{build.boot}_{build.boot_freq}.elf )"
|
||||
recipe.hooks.prebuild.4.pattern.linux=bash -c "[ -f "{build.source.path}"/bootloader.bin ] && cp -f "{build.source.path}"/bootloader.bin "{build.path}"/{build.project_name}.bootloader.bin || ( [ -f "{build.variant.path}"/{build.custom_bootloader}.bin ] && cp "{build.variant.path}"/{build.custom_bootloader}.bin "{build.path}"/{build.project_name}.bootloader.bin || python3 "{tools.esptool_py.path}"/{tools.esptool_py.cmd} {recipe.hooks.prebuild.4.pattern_args} "{build.path}"/{build.project_name}.bootloader.bin "{compiler.sdk.path}"/bin/bootloader_{build.boot}_{build.boot_freq}.elf )"
|
||||
recipe.hooks.prebuild.4.pattern.windows=cmd /c IF EXIST "{build.source.path}\bootloader.bin" ( COPY /y "{build.source.path}\bootloader.bin" "{build.path}\{build.project_name}.bootloader.bin" ) ELSE ( IF EXIST "{build.variant.path}\{build.custom_bootloader}.bin" ( COPY "{build.variant.path}\{build.custom_bootloader}.bin" "{build.path}\{build.project_name}.bootloader.bin" ) ELSE ( "{tools.esptool_py.path}/{tools.esptool_py.cmd}" {recipe.hooks.prebuild.4.pattern_args} "{build.path}\{build.project_name}.bootloader.bin" "{runtime.platform.path}\tools\sdk\{build.mcu}\bin\bootloader_{build.boot}_{build.boot_freq}.elf" ) )
|
||||
|
||||
# Check if custom build options exist in the sketch folder
|
||||
|
|
@ -186,6 +186,16 @@ recipe.hooks.prebuild.6.pattern=bash -c "[ -f "{build.path}"/build_opt.h ] || to
|
|||
recipe.hooks.prebuild.5.pattern.windows=cmd /c if exist "{build.source.path}\build_opt.h" COPY /y "{build.source.path}\build_opt.h" "{build.path}\build_opt.h"
|
||||
recipe.hooks.prebuild.6.pattern.windows=cmd /c if not exist "{build.path}\build_opt.h" type nul > "{build.path}\build_opt.h"
|
||||
|
||||
# Set -DARDUINO_CORE_BUILD only on core file compilation
|
||||
file_opts.path={build.path}/file_opts
|
||||
recipe.hooks.prebuild.set_core_build_flag.pattern=bash -c ": > {file_opts.path}"
|
||||
recipe.hooks.core.prebuild.set_core_build_flag.pattern=bash -c "echo '-DARDUINO_CORE_BUILD' > {file_opts.path}"
|
||||
recipe.hooks.core.postbuild.set_core_build_flag.pattern=bash -c ": > {file_opts.path}"
|
||||
|
||||
recipe.hooks.prebuild.set_core_build_flag.pattern.windows=cmd /c type nul > {file_opts.path}
|
||||
recipe.hooks.core.prebuild.set_core_build_flag.pattern.windows=cmd /c echo "-DARDUINO_CORE_BUILD" > {file_opts.path}
|
||||
recipe.hooks.core.postbuild.set_core_build_flag.pattern.windows=cmd /c type nul > {file_opts.path}
|
||||
|
||||
# Generate debug.cfg (must be postbuild)
|
||||
recipe.hooks.postbuild.1.pattern=bash -c "[ {build.copy_jtag_files} -eq 0 ] || cp -f "{debug.server.openocd.scripts_dir}"board/{build.openocdscript} "{build.source.path}"/debug.cfg"
|
||||
recipe.hooks.postbuild.1.pattern.windows=cmd /c IF {build.copy_jtag_files}==1 COPY /y "{debug.server.openocd.scripts_dir}board\{build.openocdscript}" "{build.source.path}\debug.cfg"
|
||||
|
|
@ -199,13 +209,13 @@ recipe.hooks.postbuild.3.pattern=bash -c "[ {build.copy_jtag_files} -eq 0 ] || c
|
|||
recipe.hooks.postbuild.3.pattern.windows=cmd /c IF {build.copy_jtag_files}==1 COPY /y "{runtime.platform.path}\tools\ide-debug\svd\{build.mcu}.svd" "{build.source.path}\debug.svd"
|
||||
|
||||
## Compile c files
|
||||
recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.flags} {compiler.c.flags} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" -DARDUINO_VARIANT="{build.variant}" -DARDUINO_PARTITION_{build.partitions} {compiler.c.extra_flags} {build.extra_flags} "@{build.opt.path}" {includes} "{source_file}" -o "{object_file}"
|
||||
recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.flags} {compiler.c.flags} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" -DARDUINO_VARIANT="{build.variant}" -DARDUINO_PARTITION_{build.partitions} {compiler.c.extra_flags} {build.extra_flags} "@{build.opt.path}" "@{file_opts.path}" {includes} "{source_file}" -o "{object_file}"
|
||||
|
||||
## Compile c++ files
|
||||
recipe.cpp.o.pattern="{compiler.path}{compiler.cpp.cmd}" {compiler.cpreprocessor.flags} {compiler.cpp.flags} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" -DARDUINO_VARIANT="{build.variant}" -DARDUINO_PARTITION_{build.partitions} {compiler.cpp.extra_flags} {build.extra_flags} "@{build.opt.path}" {includes} "{source_file}" -o "{object_file}"
|
||||
recipe.cpp.o.pattern="{compiler.path}{compiler.cpp.cmd}" {compiler.cpreprocessor.flags} {compiler.cpp.flags} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" -DARDUINO_VARIANT="{build.variant}" -DARDUINO_PARTITION_{build.partitions} {compiler.cpp.extra_flags} {build.extra_flags} "@{build.opt.path}" "@{file_opts.path}" {includes} "{source_file}" -o "{object_file}"
|
||||
|
||||
## Compile S files
|
||||
recipe.S.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.flags} {compiler.S.flags} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" -DARDUINO_VARIANT="{build.variant}" -DARDUINO_PARTITION_{build.partitions} {compiler.S.extra_flags} {build.extra_flags} "@{build.opt.path}" {includes} "{source_file}" -o "{object_file}"
|
||||
recipe.S.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.flags} {compiler.S.flags} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" -DARDUINO_VARIANT="{build.variant}" -DARDUINO_PARTITION_{build.partitions} {compiler.S.extra_flags} {build.extra_flags} "@{build.opt.path}" "@{file_opts.path}" {includes} "{source_file}" -o "{object_file}"
|
||||
|
||||
## Create archives
|
||||
recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compiler.ar.extra_flags} "{archive_file_path}" "{object_file}"
|
||||
|
|
@ -239,6 +249,7 @@ recipe.size.regex.data=^(?:\.dram0\.data|\.dram0\.bss|\.noinit)\s+([0-9]+).*
|
|||
## ---------------------------------
|
||||
pluggable_discovery.required.0=builtin:serial-discovery
|
||||
pluggable_discovery.required.1=builtin:mdns-discovery
|
||||
pluggable_discovery.required.2=builtin:dfu-discovery
|
||||
pluggable_monitor.required.serial=builtin:serial-monitor
|
||||
|
||||
## ------------------
|
||||
|
|
@ -305,3 +316,11 @@ tools.esp_ota.upload.protocol=network
|
|||
tools.esp_ota.upload.field.password=Password
|
||||
tools.esp_ota.upload.field.password.secret=true
|
||||
tools.esp_ota.upload.pattern={cmd} -i {upload.port.address} -p {upload.port.properties.port} --auth={upload.field.password} -f "{build.path}/{build.project_name}.bin"
|
||||
|
||||
## Upload Sketch Through DFU OTA
|
||||
## -------------------------------------------
|
||||
tools.dfu-util.path={runtime.tools.dfu-util-0.11.0-arduino5.path}
|
||||
tools.dfu-util.cmd=dfu-util
|
||||
tools.dfu-util.upload.params.verbose=-d
|
||||
tools.dfu-util.upload.params.quiet=
|
||||
tools.dfu-util.upload.pattern="{path}/{cmd}" --device {vid.0}:{pid.0} -D "{build.path}/{build.project_name}.bin" -Q
|
||||
|
|
|
|||
9
tools/partitions/app3M_fat9M_fact512k_16MB.csv
Normal file
9
tools/partitions/app3M_fat9M_fact512k_16MB.csv
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x300000,
|
||||
app1, app, ota_1, 0x310000, 0x300000,
|
||||
ffat, data, fat, 0x610000, 0x960000,
|
||||
factory, app, factory, 0xF70000, 0x80000,
|
||||
coredump, data, coredump, 0xFF0000, 0x10000,
|
||||
# to create/use ffat, see https://github.com/marcmerlin/esp32_fatfsimage
|
||||
|
116
variants/arduino_nano_nora/dfu_callbacks.cpp
Normal file
116
variants/arduino_nano_nora/dfu_callbacks.cpp
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
#include "Arduino.h"
|
||||
|
||||
#include <esp32-hal-tinyusb.h>
|
||||
#include <esp_system.h>
|
||||
|
||||
// defines an "Update" object accessed only by this translation unit
|
||||
// (also, the object requires MD5Builder internally)
|
||||
namespace {
|
||||
// ignore '{anonymous}::MD5Builder::...() defined but not used' warnings
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#include "../../libraries/Update/src/Updater.cpp"
|
||||
#include "../../cores/esp32/MD5Builder.cpp"
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
|
||||
#define ALT_COUNT 1
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// DFU callbacks
|
||||
// Note: alt is used as the partition number, in order to support multiple partitions like FLASH, EEPROM, etc.
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
uint16_t load_dfu_ota_descriptor(uint8_t * dst, uint8_t * itf)
|
||||
{
|
||||
#define DFU_ATTRS (DFU_ATTR_CAN_DOWNLOAD | DFU_ATTR_CAN_UPLOAD | DFU_ATTR_MANIFESTATION_TOLERANT)
|
||||
|
||||
uint8_t str_index = tinyusb_add_string_descriptor("Arduino DFU");
|
||||
uint8_t descriptor[TUD_DFU_DESC_LEN(ALT_COUNT)] = {
|
||||
// Interface number, string index, attributes, detach timeout, transfer size */
|
||||
TUD_DFU_DESCRIPTOR(*itf, ALT_COUNT, str_index, DFU_ATTRS, 100, CFG_TUD_DFU_XFER_BUFSIZE),
|
||||
};
|
||||
*itf+=1;
|
||||
memcpy(dst, descriptor, TUD_DFU_DESC_LEN(ALT_COUNT));
|
||||
return TUD_DFU_DESC_LEN(ALT_COUNT);
|
||||
}
|
||||
|
||||
// Invoked right before tud_dfu_download_cb() (state=DFU_DNBUSY) or tud_dfu_manifest_cb() (state=DFU_MANIFEST)
|
||||
// Application return timeout in milliseconds (bwPollTimeout) for the next download/manifest operation.
|
||||
// During this period, USB host won't try to communicate with us.
|
||||
uint32_t tud_dfu_get_timeout_cb(uint8_t alt, uint8_t state)
|
||||
{
|
||||
if ( state == DFU_DNBUSY )
|
||||
{
|
||||
// longest delay for Flash writing
|
||||
return 10;
|
||||
}
|
||||
else if (state == DFU_MANIFEST)
|
||||
{
|
||||
// time for esp32_ota_set_boot_partition to check final image
|
||||
return 100;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Invoked when received DFU_DNLOAD (wLength>0) following by DFU_GETSTATUS (state=DFU_DNBUSY) requests
|
||||
// This callback could be returned before flashing op is complete (async).
|
||||
// Once finished flashing, application must call tud_dfu_finish_flashing()
|
||||
void tud_dfu_download_cb(uint8_t alt, uint16_t block_num, uint8_t const* data, uint16_t length)
|
||||
{
|
||||
if (!Update.isRunning())
|
||||
{
|
||||
// this is the first data block, start update if possible
|
||||
if (!Update.begin())
|
||||
{
|
||||
tud_dfu_finish_flashing(DFU_STATUS_ERR_TARGET);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// write a block of data to Flash
|
||||
// XXX: Update API is needlessly non-const
|
||||
size_t written = Update.write(const_cast<uint8_t*>(data), length);
|
||||
tud_dfu_finish_flashing((written == length) ? DFU_STATUS_OK : DFU_STATUS_ERR_WRITE);
|
||||
}
|
||||
|
||||
// Invoked when download process is complete, received DFU_DNLOAD (wLength=0) following by DFU_GETSTATUS (state=Manifest)
|
||||
// Application can do checksum, or actual flashing if buffered entire image previously.
|
||||
// Once finished flashing, application must call tud_dfu_finish_flashing()
|
||||
void tud_dfu_manifest_cb(uint8_t alt)
|
||||
{
|
||||
(void) alt;
|
||||
bool ok = Update.end(true);
|
||||
|
||||
// flashing op for manifest is complete
|
||||
tud_dfu_finish_flashing(ok? DFU_STATUS_OK : DFU_STATUS_ERR_VERIFY);
|
||||
}
|
||||
|
||||
// Invoked when received DFU_UPLOAD request
|
||||
// Application must populate data with up to length bytes and
|
||||
// Return the number of written bytes
|
||||
uint16_t tud_dfu_upload_cb(uint8_t alt, uint16_t block_num, uint8_t* data, uint16_t length)
|
||||
{
|
||||
(void) alt;
|
||||
(void) block_num;
|
||||
(void) data;
|
||||
(void) length;
|
||||
|
||||
// not implemented
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Invoked when the Host has terminated a download or upload transfer
|
||||
void tud_dfu_abort_cb(uint8_t alt)
|
||||
{
|
||||
(void) alt;
|
||||
// ignore
|
||||
}
|
||||
|
||||
// Invoked when a DFU_DETACH request is received
|
||||
void tud_dfu_detach_cb(void)
|
||||
{
|
||||
// done, reboot
|
||||
esp_restart();
|
||||
}
|
||||
67
variants/arduino_nano_nora/double_tap.c
Normal file
67
variants/arduino_nano_nora/double_tap.c
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
#include <string.h>
|
||||
|
||||
#include <esp_system.h>
|
||||
#include <esp32s3/rom/cache.h>
|
||||
|
||||
#include "double_tap.h"
|
||||
|
||||
#define NUM_TOKENS 3
|
||||
static const uint32_t MAGIC_TOKENS[NUM_TOKENS] = {
|
||||
0xf01681de, 0xbd729b29, 0xd359be7a,
|
||||
};
|
||||
|
||||
static void *magic_area;
|
||||
static uint32_t backup_area[NUM_TOKENS];
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
// Current IDF does not map external RAM to a fixed address.
|
||||
// The actual VMA depends on other enabled devices, so the precise
|
||||
// location must be discovered.
|
||||
#include <esp_psram.h>
|
||||
#include <esp_private/esp_psram_extram.h>
|
||||
static uintptr_t get_extram_data_high(void) {
|
||||
// get a pointer into SRAM area (only the address is useful)
|
||||
void *psram_ptr = heap_caps_malloc(16, MALLOC_CAP_SPIRAM);
|
||||
heap_caps_free(psram_ptr);
|
||||
|
||||
// keep moving backwards until leaving PSRAM area
|
||||
uintptr_t psram_base_addr = (uintptr_t) psram_ptr;
|
||||
psram_base_addr &= ~(CONFIG_MMU_PAGE_SIZE - 1); // align to start of page
|
||||
while (esp_psram_check_ptr_addr((void *) psram_base_addr)) {
|
||||
psram_base_addr -= CONFIG_MMU_PAGE_SIZE;
|
||||
}
|
||||
|
||||
// offset is one page from start of PSRAM
|
||||
return psram_base_addr + CONFIG_MMU_PAGE_SIZE + esp_psram_get_size();
|
||||
}
|
||||
#else
|
||||
#include <soc/soc.h>
|
||||
#define get_extram_data_high() ((uintptr_t) SOC_EXTRAM_DATA_HIGH)
|
||||
#endif
|
||||
|
||||
|
||||
void double_tap_init(void) {
|
||||
// magic location block ends 0x20 bytes from end of PSRAM
|
||||
magic_area = (void *) (get_extram_data_high() - 0x20 - sizeof(MAGIC_TOKENS));
|
||||
}
|
||||
|
||||
void double_tap_mark() {
|
||||
memcpy(backup_area, magic_area, sizeof(MAGIC_TOKENS));
|
||||
memcpy(magic_area, MAGIC_TOKENS, sizeof(MAGIC_TOKENS));
|
||||
Cache_WriteBack_Addr((uintptr_t) magic_area, sizeof(MAGIC_TOKENS));
|
||||
}
|
||||
|
||||
void double_tap_invalidate() {
|
||||
if (memcmp(backup_area, MAGIC_TOKENS, sizeof(MAGIC_TOKENS))) {
|
||||
// different contents: restore backup
|
||||
memcpy(magic_area, backup_area, sizeof(MAGIC_TOKENS));
|
||||
} else {
|
||||
// clear memory
|
||||
memset(magic_area, 0, sizeof(MAGIC_TOKENS));
|
||||
}
|
||||
Cache_WriteBack_Addr((uintptr_t) magic_area, sizeof(MAGIC_TOKENS));
|
||||
}
|
||||
|
||||
bool double_tap_check_match() {
|
||||
return (memcmp(magic_area, MAGIC_TOKENS, sizeof(MAGIC_TOKENS)) == 0);
|
||||
}
|
||||
20
variants/arduino_nano_nora/double_tap.h
Normal file
20
variants/arduino_nano_nora/double_tap.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef __DOUBLE_TAP_H__
|
||||
#define __DOUBLE_TAP_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void double_tap_init(void);
|
||||
void double_tap_mark(void);
|
||||
void double_tap_invalidate(void);
|
||||
bool double_tap_check_match(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __DOUBLE_TAP_H__ */
|
||||
49
variants/arduino_nano_nora/extra/nora_recovery/README.md
Normal file
49
variants/arduino_nano_nora/extra/nora_recovery/README.md
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
|
||||
# Arduino Nano Nora Recovery Sketch
|
||||
|
||||
This sketch implements the DFU recovery mode logic, called by all sketches
|
||||
when a double tap on the RESET button is detected. It should not be uploaded
|
||||
as any other sketch; instead, this should be compiled and then flashed in
|
||||
the module's `factory` partition.
|
||||
|
||||
## Compilation
|
||||
|
||||
The binary can be compiled with the Arduino 2.x IDE or CLI using the
|
||||
`nano_nora` variant. In particular, using the CLI the resulting binary
|
||||
can be exported to the `build` directory with the `-e` switch to
|
||||
`arduino-cli compile`.
|
||||
|
||||
## Automatic installation
|
||||
|
||||
By replacing the binary in the current folder, automatic installation
|
||||
can be performed by running the "Upload with Programmer" action on any
|
||||
sketch in the Arduino 2.x IDE or CLI. In particular, using the CLI the
|
||||
binary can be installed via the command:
|
||||
|
||||
```
|
||||
arduino-cli compile -u --programmer esptool
|
||||
```
|
||||
|
||||
## Manual installation
|
||||
|
||||
Once compiled, the binary can also be installed on a board using `esptool.py`
|
||||
with the following command:
|
||||
|
||||
```
|
||||
esptool.py --chip esp32s3 --port "/dev/ttyACM0" --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 16MB 0xF70000 "nora_recovery.ino.bin"
|
||||
```
|
||||
|
||||
where:
|
||||
- `esptool.py` is located in your core's install path under `tools/esptool_py`;
|
||||
- `/dev/ttyACM0` is the serial port exposed by the board to be used;
|
||||
- `0xF70000` is the factory partition address (make sure it matches the
|
||||
offset in the variant's `{build.partitions}` file);
|
||||
- `nora_recovery.ino.bin` is the compiled sketch image.
|
||||
|
||||
Due to a BSP issue, the first call to `esptool.py` will enter the hardware
|
||||
bootloader for programming, but fail with an "Input/output error". This is
|
||||
a known issue; calling the program again with the same arguments will now
|
||||
work correctly.
|
||||
|
||||
Once flashing is complete, a power cycle (or RESET button tap) is required
|
||||
to leave the `esptool.py` flashing mode and load user sketches.
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
#define USB_TIMEOUT_MS 15000
|
||||
#define POLL_DELAY_MS 60
|
||||
#define FADESTEP 8
|
||||
|
||||
void pulse_led() {
|
||||
static u32_t pulse_width = 0;
|
||||
static u8_t dir = 0;
|
||||
|
||||
if (dir) {
|
||||
pulse_width -= FADESTEP;
|
||||
if (pulse_width < FADESTEP) {
|
||||
dir = 0U;
|
||||
pulse_width = FADESTEP;
|
||||
}
|
||||
} else {
|
||||
pulse_width += FADESTEP;
|
||||
if (pulse_width > 255) {
|
||||
dir = 1U;
|
||||
pulse_width = 255;
|
||||
}
|
||||
}
|
||||
|
||||
analogWrite(LED_GREEN, pulse_width);
|
||||
}
|
||||
|
||||
#include <esp_ota_ops.h>
|
||||
#include <esp_partition.h>
|
||||
#include <esp_flash_partitions.h>
|
||||
#include <esp_image_format.h>
|
||||
const esp_partition_t *find_previous_firmware() {
|
||||
extern bool _recovery_active;
|
||||
if (!_recovery_active) {
|
||||
// user flashed this recovery sketch to an OTA partition
|
||||
// stay here and wait for a proper firmware
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// booting from factory partition, look for a valid OTA image
|
||||
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, NULL);
|
||||
for (; it != NULL; it = esp_partition_next(it)) {
|
||||
const esp_partition_t *part = esp_partition_get(it);
|
||||
if (part->subtype != ESP_PARTITION_SUBTYPE_APP_FACTORY) {
|
||||
esp_partition_pos_t candidate = { part->address, part->size };
|
||||
esp_image_metadata_t meta;
|
||||
if (esp_image_verify(ESP_IMAGE_VERIFY_SILENT, &candidate, &meta) == ESP_OK) {
|
||||
// found, use it
|
||||
return part;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const esp_partition_t *user_part = NULL;
|
||||
|
||||
void setup() {
|
||||
user_part = find_previous_firmware();
|
||||
if (user_part)
|
||||
esp_ota_set_boot_partition(user_part);
|
||||
|
||||
extern bool _recovery_marker_found;
|
||||
if (!_recovery_marker_found && user_part) {
|
||||
// recovery marker not found, probable cold start
|
||||
// try starting previous firmware immediately
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
// recovery marker found, or nothing else to load
|
||||
printf("Recovery firmware started, waiting for USB\r\n");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
static int elapsed_ms = 0;
|
||||
|
||||
pulse_led();
|
||||
delay(POLL_DELAY_MS);
|
||||
if (USB) {
|
||||
// wait indefinitely for DFU to complete
|
||||
elapsed_ms = 0;
|
||||
} else {
|
||||
// wait for USB connection
|
||||
elapsed_ms += POLL_DELAY_MS;
|
||||
}
|
||||
|
||||
if (elapsed_ms > USB_TIMEOUT_MS) {
|
||||
elapsed_ms = 0;
|
||||
// timed out, try loading previous firmware
|
||||
if (user_part) {
|
||||
// there was a valid FW image, load it
|
||||
analogWrite(LED_GREEN, 255);
|
||||
printf("Leaving recovery firmware\r\n");
|
||||
delay(200);
|
||||
esp_restart(); // does not return
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
55
variants/arduino_nano_nora/io_pin_remap.cpp
Normal file
55
variants/arduino_nano_nora/io_pin_remap.cpp
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#ifndef BOARD_USES_HW_GPIO_NUMBERS
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
static const int8_t TO_GPIO_NUMBER[NUM_DIGITAL_PINS] = {
|
||||
[D0] = 44, // RX
|
||||
[D1] = 43, // TX
|
||||
[D2] = 5,
|
||||
[D3] = 6, // CTS
|
||||
[D4] = 7, // DSR
|
||||
[D5] = 8,
|
||||
[D6] = 9,
|
||||
[D7] = 10,
|
||||
[D8] = 17,
|
||||
[D9] = 18,
|
||||
[D10] = 21, // SS
|
||||
[D11] = 38, // MOSI
|
||||
[D12] = 47, // MISO
|
||||
[D13] = 48, // SCK, LED_BUILTIN
|
||||
[LED_RED] = 46,
|
||||
[LED_GREEN] = 0,
|
||||
[LED_BLUE] = 45, // RTS
|
||||
[A0] = 1, // DTR
|
||||
[A1] = 2,
|
||||
[A2] = 3,
|
||||
[A3] = 4,
|
||||
[A4] = 11, // SDA
|
||||
[A5] = 12, // SCL
|
||||
[A6] = 13,
|
||||
[A7] = 14,
|
||||
};
|
||||
|
||||
int8_t digitalPinToGPIONumber(int8_t digitalPin)
|
||||
{
|
||||
if ((digitalPin < 0) || (digitalPin >= NUM_DIGITAL_PINS))
|
||||
return -1;
|
||||
return TO_GPIO_NUMBER[digitalPin];
|
||||
}
|
||||
|
||||
int8_t gpioNumberToDigitalPin(int8_t gpioNumber)
|
||||
{
|
||||
if (gpioNumber < 0)
|
||||
return -1;
|
||||
|
||||
// slow linear table lookup
|
||||
for (int8_t digitalPin = 0; digitalPin < NUM_DIGITAL_PINS; ++digitalPin) {
|
||||
if (TO_GPIO_NUMBER[digitalPin] == gpioNumber)
|
||||
return digitalPin;
|
||||
}
|
||||
|
||||
// not found
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
80
variants/arduino_nano_nora/pins_arduino.h
Normal file
80
variants/arduino_nano_nora/pins_arduino.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#ifndef Pins_Arduino_h
|
||||
#define Pins_Arduino_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#define USB_VID 0x2341
|
||||
#define USB_PID 0x0070
|
||||
|
||||
#define EXTERNAL_NUM_INTERRUPTS 46
|
||||
#define NUM_DIGITAL_PINS 25
|
||||
#define NUM_ANALOG_INPUTS 8
|
||||
|
||||
#define analogInputToDigitalPin(p) (p)
|
||||
#define digitalPinToInterrupt(p) ((((uint8_t)digitalPinToGPIONumber(p)) < 48)? digitalPinToGPIONumber(p) : -1)
|
||||
#define digitalPinHasPWM(p) (((uint8_t)digitalPinToGPIONumber(p)) < 46)
|
||||
|
||||
#ifndef __cplusplus
|
||||
#define constexpr const
|
||||
#endif
|
||||
|
||||
// primary pin names
|
||||
|
||||
static constexpr uint8_t D0 = 0; // also RX
|
||||
static constexpr uint8_t D1 = 1; // also TX
|
||||
static constexpr uint8_t D2 = 2;
|
||||
static constexpr uint8_t D3 = 3; // also CTS
|
||||
static constexpr uint8_t D4 = 4; // also DSR
|
||||
static constexpr uint8_t D5 = 5;
|
||||
static constexpr uint8_t D6 = 6;
|
||||
static constexpr uint8_t D7 = 7;
|
||||
static constexpr uint8_t D8 = 8;
|
||||
static constexpr uint8_t D9 = 9;
|
||||
static constexpr uint8_t D10 = 10; // also SS
|
||||
static constexpr uint8_t D11 = 11; // also MOSI
|
||||
static constexpr uint8_t D12 = 12; // also MISO
|
||||
static constexpr uint8_t D13 = 13; // also SCK, LED_BUILTIN
|
||||
static constexpr uint8_t LED_RED = 14;
|
||||
static constexpr uint8_t LED_GREEN = 15;
|
||||
static constexpr uint8_t LED_BLUE = 16; // also RTS
|
||||
|
||||
static constexpr uint8_t A0 = 17; // also DTR
|
||||
static constexpr uint8_t A1 = 18;
|
||||
static constexpr uint8_t A2 = 19;
|
||||
static constexpr uint8_t A3 = 20;
|
||||
static constexpr uint8_t A4 = 21; // also SDA
|
||||
static constexpr uint8_t A5 = 22; // also SCL
|
||||
static constexpr uint8_t A6 = 23;
|
||||
static constexpr uint8_t A7 = 24;
|
||||
|
||||
// alternate pin functions
|
||||
|
||||
static constexpr uint8_t LED_BUILTIN = D13;
|
||||
|
||||
static constexpr uint8_t TX = D1;
|
||||
static constexpr uint8_t RX = D0;
|
||||
static constexpr uint8_t RTS = LED_BLUE;
|
||||
static constexpr uint8_t CTS = D3;
|
||||
static constexpr uint8_t DTR = A0;
|
||||
static constexpr uint8_t DSR = D4;
|
||||
|
||||
static constexpr uint8_t SS = D10;
|
||||
static constexpr uint8_t MOSI = D11;
|
||||
static constexpr uint8_t MISO = D12;
|
||||
static constexpr uint8_t SCK = D13;
|
||||
|
||||
static constexpr uint8_t SDA = A4;
|
||||
static constexpr uint8_t SCL = A5;
|
||||
|
||||
#define PIN_I2S_SCK D7
|
||||
#define PIN_I2S_FS D8
|
||||
#define PIN_I2S_SD D9
|
||||
#define PIN_I2S_SD_OUT D9 // same as bidir
|
||||
#define PIN_I2S_SD_IN D10
|
||||
|
||||
#ifndef __cplusplus
|
||||
#undef constexpr
|
||||
#endif
|
||||
|
||||
#endif /* Pins_Arduino_h */
|
||||
128
variants/arduino_nano_nora/variant.cpp
Normal file
128
variants/arduino_nano_nora/variant.cpp
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
// Enable pin remapping in this file, so pin constants are meaningful
|
||||
#undef ARDUINO_CORE_BUILD
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#include "double_tap.h"
|
||||
|
||||
#include <esp_system.h>
|
||||
#include <esp_ota_ops.h>
|
||||
#include <esp_partition.h>
|
||||
|
||||
extern "C" {
|
||||
void initVariant() {
|
||||
// FIXME: fix issues with GPIO matrix not being soft reset properly
|
||||
for (int pin = 0; pin<NUM_DIGITAL_PINS; ++pin) {
|
||||
switch (pin) {
|
||||
case LED_RED:
|
||||
case LED_GREEN:
|
||||
case LED_BLUE:
|
||||
// set RGB pins to dig outputs, HIGH=off
|
||||
pinMode(pin, OUTPUT);
|
||||
digitalWrite(pin, HIGH);
|
||||
break;
|
||||
|
||||
case TX:
|
||||
case RX:
|
||||
// leave UART pins alone
|
||||
break;
|
||||
|
||||
default:
|
||||
// initialize other pins to dig inputs
|
||||
pinMode(pin, INPUT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// global, accessible from recovery sketch
|
||||
bool _recovery_marker_found; // double tap detected
|
||||
bool _recovery_active; // running from factory partition
|
||||
|
||||
#define DELAY_US 10000
|
||||
#define FADESTEP 8
|
||||
static void rgb_pulse_delay(void)
|
||||
{
|
||||
// Bv R^ G x
|
||||
int widths[4] = { 192, 64, 0, 0 };
|
||||
int dec_led = 0;
|
||||
|
||||
// initialize RGB signals from weak pinstraps
|
||||
pinMode(LED_RED, OUTPUT);
|
||||
pinMode(LED_GREEN, OUTPUT);
|
||||
pinMode(LED_BLUE, OUTPUT);
|
||||
while (dec_led < 3) {
|
||||
widths[dec_led] -= FADESTEP;
|
||||
widths[dec_led+1] += FADESTEP;
|
||||
if (widths[dec_led] <= 0) {
|
||||
widths[dec_led] = 0;
|
||||
dec_led = dec_led+1;
|
||||
widths[dec_led] = 255;
|
||||
}
|
||||
|
||||
analogWrite(LED_RED, 255-widths[1]);
|
||||
analogWrite(LED_GREEN, 255-widths[2]);
|
||||
analogWrite(LED_BLUE, 255-widths[0]);
|
||||
delayMicroseconds(DELAY_US);
|
||||
}
|
||||
|
||||
// reset pins to digital HIGH before leaving
|
||||
digitalWrite(LED_RED, HIGH);
|
||||
digitalWrite(LED_GREEN, HIGH);
|
||||
digitalWrite(LED_BLUE, HIGH);
|
||||
pinMode(LED_RED, OUTPUT);
|
||||
pinMode(LED_GREEN, OUTPUT);
|
||||
pinMode(LED_BLUE, OUTPUT);
|
||||
}
|
||||
|
||||
static void NANO_ESP32_enter_bootloader(void)
|
||||
{
|
||||
if (!_recovery_active) {
|
||||
// check for valid partition scheme
|
||||
const esp_partition_t *ota_part = esp_ota_get_next_update_partition(NULL);
|
||||
const esp_partition_t *fact_part = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL);
|
||||
if (ota_part && fact_part) {
|
||||
// set tokens so the recovery FW will find them
|
||||
double_tap_mark();
|
||||
// invalidate other OTA image
|
||||
esp_partition_erase_range(ota_part, 0, 4096);
|
||||
// activate factory partition
|
||||
esp_ota_set_boot_partition(fact_part);
|
||||
}
|
||||
}
|
||||
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
static void boot_double_tap_logic()
|
||||
{
|
||||
const esp_partition_t *part = esp_ota_get_running_partition();
|
||||
_recovery_active = (part->subtype == ESP_PARTITION_SUBTYPE_APP_FACTORY);
|
||||
|
||||
double_tap_init();
|
||||
|
||||
_recovery_marker_found = double_tap_check_match();
|
||||
if (_recovery_marker_found && !_recovery_active) {
|
||||
// double tap detected in user application, reboot to factory
|
||||
NANO_ESP32_enter_bootloader();
|
||||
}
|
||||
|
||||
// delay with mark set then proceed
|
||||
// - for normal startup, to detect first double tap
|
||||
// - in recovery mode, to ignore several short presses
|
||||
double_tap_mark();
|
||||
rgb_pulse_delay();
|
||||
double_tap_invalidate();
|
||||
}
|
||||
|
||||
namespace {
|
||||
class DoubleTap {
|
||||
public:
|
||||
DoubleTap() {
|
||||
boot_double_tap_logic();
|
||||
}
|
||||
};
|
||||
|
||||
DoubleTap dt __attribute__ ((init_priority (101)));
|
||||
}
|
||||
Loading…
Reference in a new issue