This commit is contained in:
2026-05-22 21:52:50 +03:00
commit be7c60e4dd
1854 changed files with 583428 additions and 0 deletions
+503
View File
@@ -0,0 +1,503 @@
# Check ESP-IDF version and error out if it is not in the supported range.
#
# Note for arduino-esp32 developers: to bypass the version check locally,
# set ARDUINO_SKIP_IDF_VERSION_CHECK environment variable to 1. For example:
# export ARDUINO_SKIP_IDF_VERSION_CHECK=1
# idf.py build
set(min_supported_idf_version "5.3.0")
set(max_supported_idf_version "5.5.99")
set(idf_version "${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}.${IDF_VERSION_PATCH}")
if ("${idf_version}" AND NOT "$ENV{ARDUINO_SKIP_IDF_VERSION_CHECK}")
if (idf_version VERSION_LESS min_supported_idf_version)
message(FATAL_ERROR "Arduino-esp32 can be used with ESP-IDF versions "
"between ${min_supported_idf_version} and ${max_supported_idf_version}, "
"but an older version is detected: ${idf_version}.")
endif()
if (idf_version VERSION_GREATER max_supported_idf_version)
message(FATAL_ERROR "Arduino-esp32 can be used with ESP-IDF versions "
"between ${min_supported_idf_version} and ${max_supported_idf_version}, "
"but a newer version is detected: ${idf_version}.")
endif()
endif()
set(CORE_SRCS
cores/esp32/base64.cpp
cores/esp32/cbuf.cpp
cores/esp32/ColorFormat.c
cores/esp32/chip-debug-report.cpp
cores/esp32/esp32-hal-adc.c
cores/esp32/esp32-hal-bt.c
cores/esp32/esp32-hal-cpu.c
cores/esp32/esp32-hal-dac.c
cores/esp32/esp32-hal-gpio.c
cores/esp32/esp32-hal-hosted.c
cores/esp32/esp32-hal-i2c.c
cores/esp32/esp32-hal-i2c-ng.c
cores/esp32/esp32-hal-i2c-slave.c
cores/esp32/esp32-hal-ledc.c
cores/esp32/esp32-hal-log-wrapper.c
cores/esp32/esp32-hal-matrix.c
cores/esp32/esp32-hal-misc.c
cores/esp32/esp32-hal-periman.c
cores/esp32/esp32-hal-psram.c
cores/esp32/esp32-hal-rgb-led.c
cores/esp32/esp32-hal-sigmadelta.c
cores/esp32/esp32-hal-spi.c
cores/esp32/esp32-hal-time.c
cores/esp32/esp32-hal-timer.c
cores/esp32/esp32-hal-tinyusb.c
cores/esp32/esp32-hal-touch.c
cores/esp32/esp32-hal-touch-ng.c
cores/esp32/esp32-hal-uart.c
cores/esp32/esp32-hal-rmt.c
cores/esp32/Esp.cpp
cores/esp32/freertos_stats.cpp
cores/esp32/FunctionalInterrupt.cpp
cores/esp32/HardwareSerial.cpp
cores/esp32/HashBuilder.cpp
cores/esp32/HEXBuilder.cpp
cores/esp32/idf_openthread_mpool_wrapper.c
cores/esp32/IPAddress.cpp
cores/esp32/libb64/cdecode.c
cores/esp32/libb64/cencode.c
cores/esp32/MacAddress.cpp
cores/esp32/main.cpp
cores/esp32/MD5Builder.cpp
cores/esp32/Print.cpp
cores/esp32/stdlib_noniso.c
cores/esp32/Stream.cpp
cores/esp32/StreamString.cpp
cores/esp32/Tone.cpp
cores/esp32/HWCDC.cpp
cores/esp32/USB.cpp
cores/esp32/USBCDC.cpp
cores/esp32/USBMSC.cpp
cores/esp32/FirmwareMSC.cpp
cores/esp32/firmware_msc_fat.c
cores/esp32/wiring_pulse.c
cores/esp32/wiring_shift.c
cores/esp32/WMath.cpp
cores/esp32/WString.cpp
)
set(ARDUINO_ALL_LIBRARIES
ArduinoOTA
AsyncUDP
BLE
BluetoothSerial
DNSServer
EEPROM
ESP_I2S
ESP_NOW
ESP_SR
ESP_HostedOTA
ESPmDNS
Ethernet
FFat
FS
Hash
HTTPClient
HTTPUpdate
Insights
LittleFS
Matter
NetBIOS
Network
OpenThread
PPP
Preferences
RainMaker
SD_MMC
SD
SimpleBLE
SPIFFS
SPI
Ticker
Update
USB
WebServer
NetworkClientSecure
WiFi
WiFiProv
Wire
Zigbee
)
set(ARDUINO_LIBRARY_ArduinoOTA_SRCS libraries/ArduinoOTA/src/ArduinoOTA.cpp)
set(ARDUINO_LIBRARY_AsyncUDP_SRCS libraries/AsyncUDP/src/AsyncUDP.cpp)
set(ARDUINO_LIBRARY_BluetoothSerial_SRCS
libraries/BluetoothSerial/src/BluetoothSerial.cpp
libraries/BluetoothSerial/src/BTAddress.cpp
libraries/BluetoothSerial/src/BTAdvertisedDeviceSet.cpp
libraries/BluetoothSerial/src/BTScanResultsSet.cpp)
set(ARDUINO_LIBRARY_DNSServer_SRCS libraries/DNSServer/src/DNSServer.cpp)
set(ARDUINO_LIBRARY_EEPROM_SRCS libraries/EEPROM/src/EEPROM.cpp)
set(ARDUINO_LIBRARY_ESP_I2S_SRCS libraries/ESP_I2S/src/ESP_I2S.cpp)
set(ARDUINO_LIBRARY_ESP_NOW_SRCS
libraries/ESP_NOW/src/ESP32_NOW.cpp
libraries/ESP_NOW/src/ESP32_NOW_Serial.cpp)
set(ARDUINO_LIBRARY_ESP_SR_SRCS
libraries/ESP_SR/src/ESP_SR.cpp
libraries/ESP_SR/src/esp32-hal-sr.c)
set(ARDUINO_LIBRARY_ESP_HostedOTA_SRCS
libraries/ESP_HostedOTA/src/ESP_HostedOTA.cpp)
set(ARDUINO_LIBRARY_ESPmDNS_SRCS libraries/ESPmDNS/src/ESPmDNS.cpp)
set(ARDUINO_LIBRARY_Ethernet_SRCS libraries/Ethernet/src/ETH.cpp)
set(ARDUINO_LIBRARY_FFat_SRCS libraries/FFat/src/FFat.cpp)
set(ARDUINO_LIBRARY_FS_SRCS
libraries/FS/src/FS.cpp
libraries/FS/src/vfs_api.cpp)
set(ARDUINO_LIBRARY_Hash_SRCS
libraries/Hash/src/SHA1Builder.cpp
libraries/Hash/src/SHA2Builder.cpp
libraries/Hash/src/SHA3Builder.cpp
libraries/Hash/src/PBKDF2_HMACBuilder.cpp
)
set(ARDUINO_LIBRARY_HTTPClient_SRCS libraries/HTTPClient/src/HTTPClient.cpp)
set(ARDUINO_LIBRARY_HTTPUpdate_SRCS libraries/HTTPUpdate/src/HTTPUpdate.cpp)
set(ARDUINO_LIBRARY_Insights_SRCS libraries/Insights/src/Insights.cpp)
set(ARDUINO_LIBRARY_LittleFS_SRCS libraries/LittleFS/src/LittleFS.cpp)
set(ARDUINO_LIBRARY_NetBIOS_SRCS libraries/NetBIOS/src/NetBIOS.cpp)
set(ARDUINO_LIBRARY_OpenThread_SRCS
libraries/OpenThread/src/OThread.cpp
libraries/OpenThread/src/OThreadCLI.cpp
libraries/OpenThread/src/OThreadCLI_Util.cpp)
set(ARDUINO_LIBRARY_Matter_SRCS
libraries/Matter/src/MatterEndpoints/MatterGenericSwitch.cpp
libraries/Matter/src/MatterEndpoints/MatterOnOffLight.cpp
libraries/Matter/src/MatterEndpoints/MatterDimmableLight.cpp
libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.cpp
libraries/Matter/src/MatterEndpoints/MatterColorLight.cpp
libraries/Matter/src/MatterEndpoints/MatterEnhancedColorLight.cpp
libraries/Matter/src/MatterEndpoints/MatterFan.cpp
libraries/Matter/src/MatterEndpoints/MatterTemperatureSensor.cpp
libraries/Matter/src/MatterEndpoints/MatterTemperatureControlledCabinet.cpp
libraries/Matter/src/MatterEndpoints/MatterHumiditySensor.cpp
libraries/Matter/src/MatterEndpoints/MatterContactSensor.cpp
libraries/Matter/src/MatterEndpoints/MatterWaterLeakDetector.cpp
libraries/Matter/src/MatterEndpoints/MatterWaterFreezeDetector.cpp
libraries/Matter/src/MatterEndpoints/MatterRainSensor.cpp
libraries/Matter/src/MatterEndpoints/MatterPressureSensor.cpp
libraries/Matter/src/MatterEndpoints/MatterOccupancySensor.cpp
libraries/Matter/src/MatterEndpoints/MatterOnOffPlugin.cpp
libraries/Matter/src/MatterEndpoints/MatterDimmablePlugin.cpp
libraries/Matter/src/MatterEndpoints/MatterThermostat.cpp
libraries/Matter/src/MatterEndpoints/MatterWindowCovering.cpp
libraries/Matter/src/Matter.cpp
libraries/Matter/src/MatterEndPoint.cpp)
set(ARDUINO_LIBRARY_PPP_SRCS libraries/PPP/src/PPP.cpp)
set(ARDUINO_LIBRARY_Preferences_SRCS libraries/Preferences/src/Preferences.cpp)
set(ARDUINO_LIBRARY_RainMaker_SRCS
libraries/RainMaker/src/RMaker.cpp
libraries/RainMaker/src/RMakerNode.cpp
libraries/RainMaker/src/RMakerParam.cpp
libraries/RainMaker/src/RMakerDevice.cpp
libraries/RainMaker/src/RMakerType.cpp
libraries/RainMaker/src/RMakerQR.cpp
libraries/RainMaker/src/RMakerUtils.cpp
libraries/RainMaker/src/AppInsights.cpp)
set(ARDUINO_LIBRARY_SD_MMC_SRCS libraries/SD_MMC/src/SD_MMC.cpp)
set(ARDUINO_LIBRARY_SD_SRCS
libraries/SD/src/SD.cpp
libraries/SD/src/sd_diskio.cpp
libraries/SD/src/sd_diskio_crc.c)
set(ARDUINO_LIBRARY_SimpleBLE_SRCS libraries/SimpleBLE/src/SimpleBLE.cpp)
set(ARDUINO_LIBRARY_SPIFFS_SRCS libraries/SPIFFS/src/SPIFFS.cpp)
set(ARDUINO_LIBRARY_SPI_SRCS libraries/SPI/src/SPI.cpp)
set(ARDUINO_LIBRARY_Ticker_SRCS libraries/Ticker/src/Ticker.cpp)
set(ARDUINO_LIBRARY_Update_SRCS
libraries/Update/src/Updater.cpp
libraries/Update/src/HttpsOTAUpdate.cpp
libraries/Update/src/Updater_Signing.cpp)
set(ARDUINO_LIBRARY_USB_SRCS
libraries/USB/src/USBHID.cpp
libraries/USB/src/USBMIDI.cpp
libraries/USB/src/USBHIDMouse.cpp
libraries/USB/src/USBHIDKeyboard.cpp
libraries/USB/src/keyboardLayout/KeyboardLayout_da_DK.cpp
libraries/USB/src/keyboardLayout/KeyboardLayout_de_DE.cpp
libraries/USB/src/keyboardLayout/KeyboardLayout_en_US.cpp
libraries/USB/src/keyboardLayout/KeyboardLayout_es_ES.cpp
libraries/USB/src/keyboardLayout/KeyboardLayout_fr_FR.cpp
libraries/USB/src/keyboardLayout/KeyboardLayout_hu_HU.cpp
libraries/USB/src/keyboardLayout/KeyboardLayout_it_IT.cpp
libraries/USB/src/keyboardLayout/KeyboardLayout_pt_BR.cpp
libraries/USB/src/keyboardLayout/KeyboardLayout_pt_PT.cpp
libraries/USB/src/keyboardLayout/KeyboardLayout_sv_SE.cpp
libraries/USB/src/USBHIDGamepad.cpp
libraries/USB/src/USBHIDConsumerControl.cpp
libraries/USB/src/USBHIDSystemControl.cpp
libraries/USB/src/USBHIDVendor.cpp
libraries/USB/src/USBVendor.cpp)
set(ARDUINO_LIBRARY_WebServer_SRCS
libraries/WebServer/src/WebServer.cpp
libraries/WebServer/src/Parsing.cpp
libraries/WebServer/src/detail/mimetable.cpp
libraries/WebServer/src/middleware/MiddlewareChain.cpp
libraries/WebServer/src/middleware/AuthenticationMiddleware.cpp
libraries/WebServer/src/middleware/CorsMiddleware.cpp
libraries/WebServer/src/middleware/LoggingMiddleware.cpp)
set(ARDUINO_LIBRARY_NetworkClientSecure_SRCS
libraries/NetworkClientSecure/src/ssl_client.cpp
libraries/NetworkClientSecure/src/NetworkClientSecure.cpp)
set(ARDUINO_LIBRARY_Network_SRCS
libraries/Network/src/NetworkInterface.cpp
libraries/Network/src/NetworkEvents.cpp
libraries/Network/src/NetworkManager.cpp
libraries/Network/src/NetworkClient.cpp
libraries/Network/src/NetworkServer.cpp
libraries/Network/src/NetworkUdp.cpp)
set(ARDUINO_LIBRARY_WiFi_SRCS
libraries/WiFi/src/WiFiAP.cpp
libraries/WiFi/src/WiFi.cpp
libraries/WiFi/src/WiFiGeneric.cpp
libraries/WiFi/src/WiFiMulti.cpp
libraries/WiFi/src/WiFiScan.cpp
libraries/WiFi/src/WiFiSTA.cpp
libraries/WiFi/src/STA.cpp
libraries/WiFi/src/AP.cpp)
set(ARDUINO_LIBRARY_WiFiProv_SRCS libraries/WiFiProv/src/WiFiProv.cpp)
set(ARDUINO_LIBRARY_Wire_SRCS libraries/Wire/src/Wire.cpp)
set(ARDUINO_LIBRARY_Zigbee_SRCS
libraries/Zigbee/src/ZigbeeCore.cpp
libraries/Zigbee/src/ZigbeeEP.cpp
libraries/Zigbee/src/ZigbeeHandlers.cpp
libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.cpp
libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.cpp
libraries/Zigbee/src/ep/ZigbeeDimmableLight.cpp
libraries/Zigbee/src/ep/ZigbeeLight.cpp
libraries/Zigbee/src/ep/ZigbeeSwitch.cpp
libraries/Zigbee/src/ep/ZigbeeTempSensor.cpp
libraries/Zigbee/src/ep/ZigbeeThermostat.cpp
libraries/Zigbee/src/ep/ZigbeeFlowSensor.cpp
libraries/Zigbee/src/ep/ZigbeePressureSensor.cpp
libraries/Zigbee/src/ep/ZigbeeOccupancySensor.cpp
libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp
libraries/Zigbee/src/ep/ZigbeeContactSwitch.cpp
libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.cpp
libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp
libraries/Zigbee/src/ep/ZigbeeVibrationSensor.cpp
libraries/Zigbee/src/ep/ZigbeeAnalog.cpp
libraries/Zigbee/src/ep/ZigbeeRangeExtender.cpp
libraries/Zigbee/src/ep/ZigbeeGateway.cpp
libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.cpp
libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.cpp
libraries/Zigbee/src/ep/ZigbeePM25Sensor.cpp
libraries/Zigbee/src/ep/ZigbeeElectricalMeasurement.cpp
libraries/Zigbee/src/ep/ZigbeeBinary.cpp
libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp
libraries/Zigbee/src/ep/ZigbeeFanControl.cpp
libraries/Zigbee/src/ep/ZigbeeMultistate.cpp
)
set(ARDUINO_LIBRARY_BLE_SRCS
libraries/BLE/src/BLE2901.cpp
libraries/BLE/src/BLE2902.cpp
libraries/BLE/src/BLE2904.cpp
libraries/BLE/src/BLEAddress.cpp
libraries/BLE/src/BLEAdvertisedDevice.cpp
libraries/BLE/src/BLEAdvertising.cpp
libraries/BLE/src/BLEBeacon.cpp
libraries/BLE/src/BLECharacteristic.cpp
libraries/BLE/src/BLECharacteristicMap.cpp
libraries/BLE/src/BLEClient.cpp
libraries/BLE/src/BLEDescriptor.cpp
libraries/BLE/src/BLEDescriptorMap.cpp
libraries/BLE/src/BLEDevice.cpp
libraries/BLE/src/BLEEddystoneTLM.cpp
libraries/BLE/src/BLEEddystoneURL.cpp
libraries/BLE/src/BLEExceptions.cpp
libraries/BLE/src/BLEHIDDevice.cpp
libraries/BLE/src/BLERemoteCharacteristic.cpp
libraries/BLE/src/BLERemoteDescriptor.cpp
libraries/BLE/src/BLERemoteService.cpp
libraries/BLE/src/BLEScan.cpp
libraries/BLE/src/BLESecurity.cpp
libraries/BLE/src/BLEServer.cpp
libraries/BLE/src/BLEService.cpp
libraries/BLE/src/BLEServiceMap.cpp
libraries/BLE/src/BLEUtils.cpp
libraries/BLE/src/BLEUUID.cpp
libraries/BLE/src/BLEValue.cpp
libraries/BLE/src/FreeRTOS.cpp
libraries/BLE/src/GeneralUtils.cpp
)
set(ARDUINO_LIBRARIES_SRCS)
set(ARDUINO_LIBRARIES_REQUIRES)
set(ARDUINO_LIBRARIES_INCLUDEDIRS)
foreach(libname IN LISTS ARDUINO_ALL_LIBRARIES)
if(NOT CONFIG_ARDUINO_SELECTIVE_COMPILATION OR CONFIG_ARDUINO_SELECTIVE_${libname})
if(ARDUINO_LIBRARY_${libname}_SRCS)
list(APPEND ARDUINO_LIBRARIES_SRCS ${ARDUINO_LIBRARY_${libname}_SRCS})
endif()
if(ARDUINO_LIBRARY_${libname}_REQUIRES)
list(APPEND ARDUINO_LIBRARIES_REQUIRES ${ARDUINO_LIBRARY_${libname}_REQUIRES})
endif()
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/libraries/${libname}/src)
list(APPEND ARDUINO_LIBRARIES_INCLUDEDIRS libraries/${libname}/src)
endif()
endif()
endforeach()
set(includedirs variants/${CONFIG_ARDUINO_VARIANT}/ cores/esp32/ ${ARDUINO_LIBRARIES_INCLUDEDIRS})
set(srcs ${CORE_SRCS} ${ARDUINO_LIBRARIES_SRCS})
set(priv_includes cores/esp32/libb64)
set(requires spi_flash esp_partition mbedtls wpa_supplicant esp_adc esp_eth http_parser esp_ringbuf esp_driver_gptimer esp_driver_usb_serial_jtag driver esp_http_client esp_https_ota)
set(priv_requires fatfs nvs_flash app_update spiffs bootloader_support bt esp_hid usb esp_psram ${ARDUINO_LIBRARIES_REQUIRES})
if(NOT CONFIG_ARDUINO_SELECTIVE_COMPILATION OR CONFIG_ARDUINO_SELECTIVE_OpenThread)
#if(CONFIG_SOC_IEEE802154_SUPPORTED) # Does not work!
#if(CONFIG_OPENTHREAD_ENABLED) # Does not work!
if(IDF_TARGET STREQUAL "esp32c6" OR IDF_TARGET STREQUAL "esp32h2" OR IDF_TARGET STREQUAL "esp32c5") # Sadly only this works
list(APPEND requires openthread)
endif()
endif()
if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET STREQUAL "esp32s3" OR IDF_TARGET STREQUAL "esp32p4")
list(APPEND requires esp_driver_touch_sens)
endif()
idf_component_register(INCLUDE_DIRS ${includedirs} PRIV_INCLUDE_DIRS ${priv_includes} SRCS ${srcs} REQUIRES ${requires} PRIV_REQUIRES ${priv_requires})
if(NOT CONFIG_FREERTOS_HZ EQUAL 1000 AND NOT "$ENV{ARDUINO_SKIP_TICK_CHECK}")
# See delay() in cores/esp32/esp32-hal-misc.c.
message(FATAL_ERROR "esp32-arduino requires CONFIG_FREERTOS_HZ=1000 "
"(currently ${CONFIG_FREERTOS_HZ})")
endif()
string(TOUPPER ${CONFIG_ARDUINO_VARIANT} idf_target_caps)
string(REPLACE "-" "_" idf_target_for_macro "${idf_target_caps}")
target_compile_options(${COMPONENT_TARGET} PUBLIC
-DARDUINO=10812
-DARDUINO_${idf_target_for_macro}_DEV
-DARDUINO_ARCH_ESP32
-DARDUINO_BOARD="${idf_target_caps}_DEV"
-DARDUINO_VARIANT="${CONFIG_ARDUINO_VARIANT}"
-DESP32=ESP32)
if(CONFIG_AUTOSTART_ARDUINO)
# in autostart mode, arduino-esp32 contains app_main() function and needs to
# reference setup() and loop() in the main component. If we add main
# component to priv_requires then we create a large circular dependency
# (arduino-esp32 -> main -> arduino-esp32) and can get linker errors, so
# instead we add setup() and loop() to the undefined symbols list so the
# linker will always include them.
#
# (As they are C++ symbol, we need to add the C++ mangled names.)
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u _Z5setupv -u _Z4loopv")
endif()
# Fix for WiFi/Bluetooth linker errors when CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP is not enabled
#
# Problem:
# WiFi and Bluetooth binary libraries reference __wrap_esp_log_write functions.
# When CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP=y, esp_diagnostics provides these wrappers.
# When not set, no component provides them, causing "undefined reference" linker errors.
#
# Solution:
# cores/esp32/esp32-hal-log-wrapper.c provides simple pass-through wrapper implementations
# that forward calls to the real ESP-IDF logging functions without additional processing.
#
# Two linker flags are required:
#
# 1. --wrap=symbol: Sets up the wrapping/redirection mechanism
# Effect: esp_log_write() calls → redirected to → __wrap_esp_log_write()
# __real_esp_log_write() → created as alias → original esp_log_write()
# Note: Applied at build level (idf_build_set_property) so it works when Arduino is a component
#
# 2. -u symbol: Forces inclusion of wrapper symbols from static library
# Why needed: Static library linking is "lazy" - only pulls objects with referenced symbols.
# Without -u, linker sees wrapper as unreferenced and drops it from the library.
# The -u flag marks symbols as "undefined", forcing linker to search and include them.
# Analogy: Same technique used for setup()/loop() symbols (see above)
if(NOT CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP)
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_log_write" APPEND)
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_log_writev" APPEND)
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u __wrap_esp_log_write" "-u __wrap_esp_log_writev")
endif()
# OpenThread message pool: redirect to Arduino wrapper (BOARD_HAS_PSRAM / PSRAM-aware allocation).
# Same pattern as esp_log wrap: --wrap so calls use __wrap_* ; -u so linker keeps our wrapper .o.
# Applied on OpenThread-capable targets (same targets that may add openthread to requires).
if(CONFIG_OPENTHREAD_ENABLED AND CONFIG_SOC_IEEE802154_SUPPORTED)
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=otPlatMessagePoolInit" APPEND)
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=otPlatMessagePoolNew" APPEND)
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=otPlatMessagePoolFree" APPEND)
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=otPlatMessagePoolNumFreeBuffers" APPEND)
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=otPlatMessagePoolDeinit" APPEND)
target_link_libraries(${COMPONENT_LIB} INTERFACE
"-u __wrap_otPlatMessagePoolInit"
"-u __wrap_otPlatMessagePoolNew"
"-u __wrap_otPlatMessagePoolFree"
"-u __wrap_otPlatMessagePoolNumFreeBuffers"
"-u __wrap_otPlatMessagePoolDeinit")
endif()
# This function adds a dependency on the given component if the component is included into the build.
function(maybe_add_component component_name)
idf_build_get_property(components BUILD_COMPONENTS)
if (${component_name} IN_LIST components)
idf_component_get_property(lib_name ${component_name} COMPONENT_LIB)
target_link_libraries(${COMPONENT_LIB} PUBLIC ${lib_name})
endif()
endfunction()
if(IDF_TARGET MATCHES "esp32s2|esp32s3|esp32p4" AND CONFIG_TINYUSB_ENABLED)
maybe_add_component(arduino_tinyusb)
endif()
if(NOT CONFIG_ARDUINO_SELECTIVE_COMPILATION OR CONFIG_ARDUINO_SELECTIVE_ArduinoOTA)
maybe_add_component(esp_https_ota)
endif()
if(NOT CONFIG_ARDUINO_SELECTIVE_COMPILATION OR CONFIG_ARDUINO_SELECTIVE_ESP_SR)
maybe_add_component(espressif__esp_sr)
endif()
if(NOT CONFIG_ARDUINO_SELECTIVE_COMPILATION OR CONFIG_ARDUINO_SELECTIVE_Matter)
maybe_add_component(espressif__esp_matter)
endif()
if(NOT CONFIG_ARDUINO_SELECTIVE_COMPILATION OR CONFIG_ARDUINO_SELECTIVE_LittleFS)
maybe_add_component(joltwallet__littlefs)
endif()
if(NOT CONFIG_ARDUINO_SELECTIVE_COMPILATION OR CONFIG_ARDUINO_SELECTIVE_WiFiProv)
maybe_add_component(espressif__network_provisioning)
endif()
+445
View File
@@ -0,0 +1,445 @@
menu "Arduino Configuration"
config ARDUINO_VARIANT
string "Arduino target variant (board)"
default IDF_TARGET
help
The name of a target variant (e.g., a specific board) in the variants/
folder, e.g. "heltec_wifi_lora_32_V2". The name is case sensitive.
Specifying a variant name different from the target enables additional
customization, for example the definition of GPIO pins.
config ENABLE_ARDUINO_DEPENDS
bool
select LWIP_SO_RCVBUF
select ETHERNET
select WIFI_ENABLED
select ESP32_PHY_CALIBRATION_AND_DATA_STORAGE if IDF_TARGET_ESP32
select MEMMAP_SMP
default "y"
config AUTOSTART_ARDUINO
bool "Autostart Arduino setup and loop on boot"
default "n"
help
Enabling this option will implement app_main and start Arduino.
All you need to implement in your main.cpp is setup() and loop()
and include Arduino.h
If disabled, you can call initArduino() to run any preparations
required by the framework
choice ARDUINO_RUNNING_CORE
bool "Core on which Arduino's setup() and loop() are running"
default ARDUINO_RUN_CORE0 if FREERTOS_UNICORE
default ARDUINO_RUN_CORE1 if !FREERTOS_UNICORE
help
Select on which core Arduino's setup() and loop() functions run
config ARDUINO_RUN_CORE0
bool "CORE 0"
config ARDUINO_RUN_CORE1
bool "CORE 1"
depends on !FREERTOS_UNICORE
config ARDUINO_RUN_NO_AFFINITY
bool "BOTH"
depends on !FREERTOS_UNICORE
endchoice
config ARDUINO_RUNNING_CORE
int
default 0 if ARDUINO_RUN_CORE0
default 1 if ARDUINO_RUN_CORE1
default -1 if ARDUINO_RUN_NO_AFFINITY
config ARDUINO_LOOP_STACK_SIZE
int "Loop thread stack size"
default 8192
help
Amount of stack available for the Arduino task.
choice ARDUINO_EVENT_RUNNING_CORE
bool "Core on which Arduino's event handler is running"
default ARDUINO_EVENT_RUN_CORE0 if FREERTOS_UNICORE
default ARDUINO_EVENT_RUN_CORE1 if !FREERTOS_UNICORE
help
Select on which core Arduino's WiFi.onEvent() run
config ARDUINO_EVENT_RUN_CORE0
bool "CORE 0"
config ARDUINO_EVENT_RUN_CORE1
bool "CORE 1"
depends on !FREERTOS_UNICORE
config ARDUINO_EVENT_RUN_NO_AFFINITY
bool "BOTH"
depends on !FREERTOS_UNICORE
endchoice
config ARDUINO_EVENT_RUNNING_CORE
int
default 0 if ARDUINO_EVENT_RUN_CORE0
default 1 if ARDUINO_EVENT_RUN_CORE1
default -1 if ARDUINO_EVENT_RUN_NO_AFFINITY
choice ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE
bool "Core on which Arduino's Serial Event task is running"
default ARDUINO_SERIAL_EVENT_RUN_CORE0 if FREERTOS_UNICORE
default ARDUINO_SERIAL_EVENT_RUN_NO_AFFINITY if !FREERTOS_UNICORE
help
Select on which core Arduino's Serial Event task run
config ARDUINO_SERIAL_EVENT_RUN_CORE0
bool "CORE 0"
config ARDUINO_SERIAL_EVENT_RUN_CORE1
bool "CORE 1"
depends on !FREERTOS_UNICORE
config ARDUINO_SERIAL_EVENT_RUN_NO_AFFINITY
bool "BOTH"
depends on !FREERTOS_UNICORE
endchoice
config ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE
int
default 0 if ARDUINO_SERIAL_EVENT_RUN_CORE0
default 1 if ARDUINO_SERIAL_EVENT_RUN_CORE1
default -1 if ARDUINO_SERIAL_EVENT_RUN_NO_AFFINITY
config ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE
int "Serial Event task stack size"
default 2048
help
Amount of stack available for the Serial Event task.
config ARDUINO_SERIAL_EVENT_TASK_PRIORITY
int "Priority of the Serial Event task"
default 24
help
Select at what priority you want the Serial Event task to run.
choice ARDUINO_UDP_RUNNING_CORE
bool "Core on which Arduino's UDP is running"
default ARDUINO_UDP_RUN_CORE0
help
Select on which core Arduino's UDP run
config ARDUINO_UDP_RUN_CORE0
bool "CORE 0"
config ARDUINO_UDP_RUN_CORE1
bool "CORE 1"
depends on !FREERTOS_UNICORE
config ARDUINO_UDP_RUN_NO_AFFINITY
bool "BOTH"
depends on !FREERTOS_UNICORE
endchoice
config ARDUINO_UDP_RUNNING_CORE
int
default 0 if ARDUINO_UDP_RUN_CORE0
default 1 if ARDUINO_UDP_RUN_CORE1
default -1 if ARDUINO_UDP_RUN_NO_AFFINITY
config ARDUINO_UDP_TASK_PRIORITY
int "Priority of the UDP task"
default 3
help
Select at what priority you want the UDP task to run.
config ARDUINO_UDP_TASK_STACK_SIZE
int "UDP task stack size"
default 4096
help
Amount of stack available for the UDP task.
config ARDUINO_ISR_IRAM
bool "Run interrupts in IRAM"
default "n"
help
Enabling this option will Attach all interrupts with the IRAm flag.
It will also make some HAL function, like, digitalRead/Write and more
be loaded into IRAM for access inside ISRs.
Beware that this is a very dangerous setting. Enable it only if you
are fully aware of the consequences.
config DISABLE_HAL_LOCKS
bool "Disable mutex locks for HAL"
default "n"
help
Enabling this option will run all hardware abstraction without locks.
While communication with external hardware will be faster, you need to
make sure that there is no option to use the same bus from another thread
or interrupt at the same time. Option is best used with Arduino enabled
and code implemented only in setup/loop and Arduino callbacks
menu "Debug Log Configuration"
choice ARDUHAL_LOG_DEFAULT_LEVEL
bool "Default log level"
default ARDUHAL_LOG_DEFAULT_LEVEL_ERROR
help
Specify how much output to see in logs by default.
config ARDUHAL_LOG_DEFAULT_LEVEL_NONE
bool "No output"
config ARDUHAL_LOG_DEFAULT_LEVEL_ERROR
bool "Error"
config ARDUHAL_LOG_DEFAULT_LEVEL_WARN
bool "Warning"
config ARDUHAL_LOG_DEFAULT_LEVEL_INFO
bool "Info"
config ARDUHAL_LOG_DEFAULT_LEVEL_DEBUG
bool "Debug"
config ARDUHAL_LOG_DEFAULT_LEVEL_VERBOSE
bool "Verbose"
endchoice
config ARDUHAL_LOG_DEFAULT_LEVEL
int
default 0 if ARDUHAL_LOG_DEFAULT_LEVEL_NONE
default 1 if ARDUHAL_LOG_DEFAULT_LEVEL_ERROR
default 2 if ARDUHAL_LOG_DEFAULT_LEVEL_WARN
default 3 if ARDUHAL_LOG_DEFAULT_LEVEL_INFO
default 4 if ARDUHAL_LOG_DEFAULT_LEVEL_DEBUG
default 5 if ARDUHAL_LOG_DEFAULT_LEVEL_VERBOSE
config ARDUHAL_LOG_COLORS
bool "Use ANSI terminal colors in log output"
default "n"
help
Enable ANSI terminal color codes in bootloader output.
In order to view these, your terminal program must support ANSI color codes.
config ARDUHAL_ESP_LOG
bool "Forward ESP_LOGx to Arduino log output"
default "n"
help
This option will redefine the ESP_LOGx macros to Arduino's log_x macros.
To enable for your application, add the following after your includes:
#ifdef ARDUINO_ARCH_ESP32
#include "esp32-hal-log.h"
#endif
endmenu
choice ARDUHAL_PARTITION_SCHEME
bool "Used partition scheme"
default ARDUHAL_PARTITION_SCHEME_DEFAULT
help
Specify which partition scheme to be used.
config ARDUHAL_PARTITION_SCHEME_DEFAULT
bool "Default"
config ARDUHAL_PARTITION_SCHEME_MINIMAL
bool "Minimal (for 2MB FLASH)"
config ARDUHAL_PARTITION_SCHEME_NO_OTA
bool "No OTA (for large apps)"
config ARDUHAL_PARTITION_SCHEME_HUGE_APP
bool "Huge App (for very large apps)"
config ARDUHAL_PARTITION_SCHEME_MIN_SPIFFS
bool "Minimal SPIFFS (for large apps with OTA)"
endchoice
config ARDUHAL_PARTITION_SCHEME
string
default "default" if ARDUHAL_PARTITION_SCHEME_DEFAULT
default "minimal" if ARDUHAL_PARTITION_SCHEME_MINIMAL
default "no_ota" if ARDUHAL_PARTITION_SCHEME_NO_OTA
default "huge_app" if ARDUHAL_PARTITION_SCHEME_HUGE_APP
default "min_spiffs" if ARDUHAL_PARTITION_SCHEME_MIN_SPIFFS
config AUTOCONNECT_WIFI
bool "Autoconnect WiFi on boot"
default "n"
depends on AUTOSTART_ARDUINO
select ARDUINO_SELECTIVE_WiFi
help
If enabled, WiFi will connect to the last used SSID (if station was enabled),
else connection will be started only after calling WiFi.begin(ssid, password)
config ARDUINO_SELECTIVE_COMPILATION
bool "Include only specific Arduino libraries"
default n
config ARDUINO_SELECTIVE_SPI
bool "Enable SPI"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_Wire
bool "Enable Wire"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_ESP_SR
bool "Enable ESP-SR"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_EEPROM
bool "Enable EEPROM"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_Preferences
bool "Enable Preferences"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_Ticker
bool "Enable Ticker"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_Update
bool "Enable Update"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_Zigbee
bool "Enable Zigbee"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_FS
bool "Enable FS"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_SD
bool "Enable SD"
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_FS
default y
config ARDUINO_SELECTIVE_SD_MMC
bool "Enable SD_MMC"
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_FS
default y
config ARDUINO_SELECTIVE_SPIFFS
bool "Enable SPIFFS"
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_FS
default y
config ARDUINO_SELECTIVE_FFat
bool "Enable FFat"
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_FS
default y
config ARDUINO_SELECTIVE_LittleFS
bool "Enable LittleFS"
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_FS
default y
config ARDUINO_SELECTIVE_Network
bool "Enable Networking"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_Ethernet
bool "Enable Ethernet"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_PPP
bool "Enable PPP"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_Hash
bool "Enable Hash"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_ArduinoOTA
bool "Enable ArduinoOTA"
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_Network
select ARDUINO_SELECTIVE_Hash
select ARDUINO_SELECTIVE_ESPmDNS
default y
config ARDUINO_SELECTIVE_AsyncUDP
bool "Enable AsyncUDP"
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_Network
default y
config ARDUINO_SELECTIVE_DNSServer
bool "Enable DNSServer"
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_Network
default y
config ARDUINO_SELECTIVE_ESPmDNS
bool "Enable ESPmDNS"
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_Network
default y
config ARDUINO_SELECTIVE_HTTPClient
bool "Enable HTTPClient"
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_Network
select ARDUINO_SELECTIVE_NetworkClientSecure
default y
config ARDUINO_SELECTIVE_Matter
bool "Enable Matter"
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_Network
default y
config ARDUINO_SELECTIVE_NetBIOS
bool "Enable NetBIOS"
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_Network
default y
config ARDUINO_SELECTIVE_WebServer
bool "Enable WebServer"
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_Network
default y
select ARDUINO_SELECTIVE_FS
select ARDUINO_SELECTIVE_Hash
config ARDUINO_SELECTIVE_WiFi
bool "Enable WiFi"
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_Network
default y
config ARDUINO_SELECTIVE_NetworkClientSecure
bool "Enable NetworkClientSecure"
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_Network
default y
config ARDUINO_SELECTIVE_WiFiProv
bool "Enable WiFiProv"
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_Network && ARDUINO_SELECTIVE_WiFi
default y
config ARDUINO_SELECTIVE_BLE
bool "Enable BLE"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_BluetoothSerial
bool "Enable BluetoothSerial"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_SimpleBLE
bool "Enable SimpleBLE"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_RainMaker
bool "Enable RainMaker"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_OpenThread
bool "Enable OpenThread"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_Insights
bool "Enable Insights"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
endmenu
+56399
View File
File diff suppressed because it is too large Load Diff
+281
View File
@@ -0,0 +1,281 @@
/*
Arduino.h - Main include file for the Arduino SDK
Copyright (c) 2005-2013 Arduino Team. All right 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
*/
#ifndef Arduino_h
#define Arduino_h
#include <stdbool.h>
#include <stdint.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "esp_arduino_version.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp8266-compat.h"
#include "soc/gpio_reg.h"
#include "stdlib_noniso.h"
#include "binary.h"
#include "extra_attr.h"
#include "pins_arduino.h"
#include "esp32-hal.h"
#define PI 3.1415926535897932384626433832795
#define HALF_PI 1.5707963267948966192313216916398
#define TWO_PI 6.283185307179586476925286766559
#define DEG_TO_RAD 0.017453292519943295769236907684886
#define RAD_TO_DEG 57.295779513082320876798154814105
#define EULER 2.718281828459045235360287471352
#define SERIAL 0x0
#define DISPLAY 0x1
#define LSBFIRST 0
#define MSBFIRST 1
//Interrupt Modes
#define RISING 0x01
#define FALLING 0x02
#define CHANGE 0x03
#define ONLOW 0x04
#define ONHIGH 0x05
#define ONLOW_WE 0x0C
#define ONHIGH_WE 0x0D
#define DEFAULT 1
#define EXTERNAL 0
#ifndef __STRINGIFY
#define __STRINGIFY(a) #a
#endif
// can't define max() / min() because of conflicts with C++
#define _min(a, b) ((a) < (b) ? (a) : (b))
#define _max(a, b) ((a) > (b) ? (a) : (b))
#define _abs(x) ((x) > 0 ? (x) : -(x)) // abs() comes from STL
#define constrain(amt, low, high) ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)))
#define _round(x) ((x) >= 0 ? (long)((x) + 0.5) : (long)((x) - 0.5)) // round() comes from STL
#define radians(deg) ((deg) * DEG_TO_RAD)
#define degrees(rad) ((rad) * RAD_TO_DEG)
#define sq(x) ((x) * (x))
// ESP32xx runs FreeRTOS... disabling interrupts can lead to issues, such as Watchdog Timeout
#define sei() portENABLE_INTERRUPTS()
#define cli() portDISABLE_INTERRUPTS()
#define interrupts() sei()
#define noInterrupts() cli()
#define clockCyclesPerMicrosecond() ((long int)getCpuFrequencyMhz())
#define clockCyclesToMicroseconds(a) ((a) / clockCyclesPerMicrosecond())
#define microsecondsToClockCycles(a) ((a) * clockCyclesPerMicrosecond())
#define lowByte(w) ((uint8_t)((w) & 0xff))
#define highByte(w) ((uint8_t)((w) >> 8))
#define bitRead(value, bit) (((value) >> (bit)) & 0x01)
#define bitSet(value, bit) ((value) |= (1UL << (bit)))
#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
#define bitToggle(value, bit) ((value) ^= (1UL << (bit)))
#define bitWrite(value, bit, bitvalue) ((bitvalue) ? bitSet(value, bit) : bitClear(value, bit))
// avr-libc defines _NOP() since 1.6.2
#ifndef _NOP
#define _NOP() \
do { \
__asm__ volatile("nop"); \
} while (0)
#endif
#define bit(b) (1UL << (b))
#define _BV(b) (1UL << (b))
#define digitalPinToTimer(pin) (0)
#define analogInPinToBit(P) (P)
#if SOC_GPIO_PIN_COUNT <= 32
#define digitalPinToPort(pin) (0)
#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) ((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))
#else
#error SOC_GPIO_PIN_COUNT > 64 not implemented
#endif
#define NOT_A_PIN -1
#define NOT_A_PORT -1
#define NOT_AN_INTERRUPT -1
#define NOT_ON_TIMER 0
// some defines generic for all SoC moved from variants/board_name/pins_arduino.h
#define NUM_DIGITAL_PINS SOC_GPIO_PIN_COUNT // All GPIOs
#if SOC_ADC_PERIPH_NUM == 1
#define NUM_ANALOG_INPUTS (SOC_ADC_CHANNEL_NUM(0)) // Depends on the SoC (ESP32C6, ESP32H2, ESP32C2, ESP32P4)
#elif SOC_ADC_PERIPH_NUM == 2
#define NUM_ANALOG_INPUTS (SOC_ADC_CHANNEL_NUM(0) + SOC_ADC_CHANNEL_NUM(1)) // Depends on the SoC (ESP32, ESP32S2, ESP32S3, ESP32C3)
#endif
#define EXTERNAL_NUM_INTERRUPTS NUM_DIGITAL_PINS // All GPIOs
#define analogInputToDigitalPin(p) (((p) < NUM_ANALOG_INPUTS) ? (analogChannelToDigitalPin(p)) : -1)
#define digitalPinToInterrupt(p) ((((uint8_t)digitalPinToGPIONumber(p)) < NUM_DIGITAL_PINS) ? (p) : NOT_AN_INTERRUPT)
#define digitalPinHasPWM(p) (((uint8_t)digitalPinToGPIONumber(p)) < NUM_DIGITAL_PINS)
typedef bool boolean;
typedef uint8_t byte;
typedef unsigned int word;
#ifdef __cplusplus
void setup(void);
void loop(void);
// The default is using Real Hardware random number generator
// But when randomSeed() is called, it turns to Psedo random
// generator, exactly as done in Arduino mainstream
long random(long);
long random(long, long);
// Calling randomSeed() will make random()
// using pseudo random like in Arduino
void randomSeed(unsigned long);
// Allow the Application to decide if the random generator
// will use Real Hardware random generation (true - default)
// or Pseudo random generation (false) as in Arduino MainStream
void useRealRandomGenerator(bool useRandomHW);
#endif
long map(long, long, long, long, long);
#ifdef __cplusplus
extern "C" {
#endif
void init(void);
void initVariant(void);
void initArduino(void);
unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout);
unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout);
uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder); // codespell:ignore shiftin
void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val);
#ifdef __cplusplus
}
#include <algorithm>
#include <cmath>
#include "WCharacter.h"
#include "WString.h"
#include "Stream.h"
#include "Printable.h"
#include "Print.h"
#include "IPAddress.h"
#include "Client.h"
#include "Server.h"
#include "Udp.h"
#include "HardwareSerial.h"
#include "Esp.h"
#include "freertos_stats.h"
// Use float-compatible stl abs() and round(), we don't use Arduino macros to avoid issues with the C++ libraries
using std::abs;
using std::isinf;
using std::isnan;
using std::max;
using std::min;
using std::round;
uint16_t makeWord(uint16_t w);
uint16_t makeWord(uint8_t h, uint8_t l);
#define word(...) makeWord(__VA_ARGS__)
size_t getArduinoLoopTaskStackSize(void);
#define SET_LOOP_TASK_STACK_SIZE(sz) \
size_t getArduinoLoopTaskStackSize() { \
return sz; \
}
#define ESP32_USB_MIDI_DEFAULT_NAME "TinyUSB MIDI"
/**
* @brief Set the current device name
* 1. Name set via constructor (if any)
* 2. Name set via SET_USB_MIDI_DEVICE_NAME() macro (if defined)
* 3. Default name "TinyUSB MIDI"
* If device name is set as "", it will be ignored
*/
#define SET_USB_MIDI_DEVICE_NAME(name) \
const char *getUSBMIDIDefaultDeviceName() { \
if (!name || strlen(name) == 0) { \
return ESP32_USB_MIDI_DEFAULT_NAME; \
} \
return name; \
}
bool shouldPrintChipDebugReport(void);
#define ENABLE_CHIP_DEBUG_REPORT \
bool shouldPrintChipDebugReport(void) { \
return true; \
}
// macro SET_TIME_BEFORE_STARTING_SKETCH_MS(time_ms) can set a time in milliseconds
// before the sketch would start its execution. It gives the user time to open the Serial Monitor
uint64_t getArduinoSetupWaitTime_ms(void);
#define SET_TIME_BEFORE_STARTING_SKETCH_MS(time_ms) \
uint64_t getArduinoSetupWaitTime_ms() { \
return (time_ms); \
}
// allows user to bypass esp_spiram_test()
bool esp_psram_extram_test(void);
#define BYPASS_SPIRAM_TEST(bypass) \
bool testSPIRAM(void) { \
if (bypass) \
return true; \
else \
return esp_psram_extram_test(); \
}
unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L);
unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L);
extern "C" bool getLocalTime(struct tm *info, uint32_t ms = 5000);
extern "C" void configTime(long gmtOffset_sec, int daylightOffset_sec, const char *server1, const char *server2 = nullptr, const char *server3 = nullptr);
extern "C" void configTzTime(const char *tz, const char *server1, const char *server2 = nullptr, const char *server3 = nullptr);
void setToneChannel(uint8_t channel = 0);
void tone(uint8_t _pin, unsigned int frequency, unsigned long duration = 0);
void noTone(uint8_t _pin);
#endif /* __cplusplus */
// must be applied last as it overrides some of the above
#include "io_pin_remap.h"
#endif /* _ESP32_CORE_ARDUINO_H_ */
+47
View File
@@ -0,0 +1,47 @@
/*
Client.h - Base class that provides Client
Copyright (c) 2011 Adrian McEwen. All right 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
*/
#ifndef client_h
#define client_h
#include "Print.h"
#include "Stream.h"
#include "IPAddress.h"
class Client : public Stream {
public:
virtual int connect(IPAddress ip, uint16_t port) = 0;
virtual int connect(const char *host, uint16_t port) = 0;
virtual size_t write(uint8_t) = 0;
virtual size_t write(const uint8_t *buf, size_t size) = 0;
virtual int available() = 0;
virtual int read() = 0;
virtual int read(uint8_t *buf, size_t size) = 0;
virtual int peek() = 0;
virtual void flush() = 0;
virtual void stop() = 0;
virtual uint8_t connected() = 0;
virtual operator bool() = 0;
protected:
uint8_t *rawIPAddress(IPAddress &addr) {
return addr.raw_address();
}
};
#endif
+281
View File
@@ -0,0 +1,281 @@
/*
*
* Copyright (c) 2021 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ColorFormat.h"
#include <math.h>
// define a clamp macro to substitute the std::clamp macro which is available from C++17 onwards
#define clamp(a, min, max) ((a) < (min) ? (min) : ((a) > (max) ? (max) : (a)))
const espHsvColor_t HSV_BLACK = {0, 0, 0};
const espHsvColor_t HSV_WHITE = {0, 0, 254};
const espHsvColor_t HSV_RED = {0, 254, 254};
const espHsvColor_t HSV_YELLOW = {42, 254, 254};
const espHsvColor_t HSV_GREEN = {84, 254, 254};
const espHsvColor_t HSV_CYAN = {127, 254, 254};
const espHsvColor_t HSV_BLUE = {169, 254, 254};
const espHsvColor_t HSV_MAGENTA = {211, 254, 254};
const espRgbColor_t RGB_BLACK = {0, 0, 0};
const espRgbColor_t RGB_WHITE = {255, 255, 255};
const espRgbColor_t RGB_RED = {255, 0, 0};
const espRgbColor_t RGB_YELLOW = {255, 255, 0};
const espRgbColor_t RGB_GREEN = {0, 255, 0};
const espRgbColor_t RGB_CYAN = {0, 255, 255};
const espRgbColor_t RGB_BLUE = {0, 0, 255};
const espRgbColor_t RGB_MAGENTA = {255, 0, 255};
// main color temperature values
const espCtColor_t COOL_WHITE_COLOR_TEMPERATURE = {142};
const espCtColor_t DAYLIGHT_WHITE_COLOR_TEMPERATURE = {181};
const espCtColor_t WHITE_COLOR_TEMPERATURE = {250};
const espCtColor_t SOFT_WHITE_COLOR_TEMPERATURE = {370};
const espCtColor_t WARM_WHITE_COLOR_TEMPERATURE = {454};
espRgbColor_t espHsvToRgbColor(uint16_t h, uint8_t s, uint8_t v) {
espHsvColor_t hsv = {h, s, v};
return espHsvColorToRgbColor(hsv);
}
espRgbColor_t espHsvColorToRgbColor(espHsvColor_t hsv) {
espRgbColor_t rgb;
uint8_t region, p, q, t;
uint32_t h, s, v, remainder;
if (hsv.s == 0) {
rgb.r = rgb.g = rgb.b = hsv.v;
} else {
h = hsv.h;
s = hsv.s;
v = hsv.v;
region = h / 43;
remainder = (h - (region * 43)) * 6;
p = (v * (255 - s)) >> 8;
q = (v * (255 - ((s * remainder) >> 8))) >> 8;
t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8;
switch (region) {
case 0: rgb.r = v, rgb.g = t, rgb.b = p; break;
case 1: rgb.r = q, rgb.g = v, rgb.b = p; break;
case 2: rgb.r = p, rgb.g = v, rgb.b = t; break;
case 3: rgb.r = p, rgb.g = q, rgb.b = v; break;
case 4: rgb.r = t, rgb.g = p, rgb.b = v; break;
case 5:
default: rgb.r = v, rgb.g = p, rgb.b = q; break;
}
}
return rgb;
}
espHsvColor_t espRgbToHsvColor(uint8_t r, uint8_t g, uint8_t b) {
espRgbColor_t rgb = {r, g, b};
return espRgbColorToHsvColor(rgb);
}
espHsvColor_t espRgbColorToHsvColor(espRgbColor_t rgb) {
espHsvColor_t hsv;
uint8_t rgbMin, rgbMax;
rgbMin = rgb.r < rgb.g ? (rgb.r < rgb.b ? rgb.r : rgb.b) : (rgb.g < rgb.b ? rgb.g : rgb.b);
rgbMax = rgb.r > rgb.g ? (rgb.r > rgb.b ? rgb.r : rgb.b) : (rgb.g > rgb.b ? rgb.g : rgb.b);
hsv.v = rgbMax;
if (hsv.v == 0) {
hsv.h = 0;
hsv.s = 0;
return hsv;
}
hsv.s = 255 * (rgbMax - rgbMin) / hsv.v;
if (hsv.s == 0) {
hsv.h = 0;
return hsv;
}
if (rgbMax == rgb.r) {
hsv.h = 0 + 43 * (rgb.g - rgb.b) / (rgbMax - rgbMin);
} else if (rgbMax == rgb.g) {
hsv.h = 85 + 43 * (rgb.b - rgb.r) / (rgbMax - rgbMin);
} else {
hsv.h = 171 + 43 * (rgb.r - rgb.g) / (rgbMax - rgbMin);
}
return hsv;
}
espRgbColor_t espXYColorToRgbColor(uint8_t Level, espXyColor_t xy) {
return espXYToRgbColor(Level, xy.x, xy.y, true);
}
espRgbColor_t espXYToRgbColor(uint8_t Level, uint16_t current_X, uint16_t current_Y, bool addXYZScaling) {
// convert xyY color space to RGB
// https://www.easyrgb.com/en/math.php
// https://en.wikipedia.org/wiki/SRGB
// refer https://en.wikipedia.org/wiki/CIE_1931_color_space#CIE_xy_chromaticity_diagram_and_the_CIE_xyY_color_space
// The current_X/current_Y attribute contains the current value of the normalized chromaticity value of x/y.
// The value of x/y shall be related to the current_X/current_Y attribute by the relationship
// x = current_X/65536
// y = current_Y/65536
// z = 1-x-y
espRgbColor_t rgb;
float x, y, z;
float X, Y, Z;
float r, g, b;
x = ((float)current_X) / 65535.0f;
y = ((float)current_Y) / 65535.0f;
z = 1.0f - x - y;
// Calculate XYZ values
// Y - given brightness in 0 - 1 range
Y = ((float)Level) / 254.0f;
X = (Y / y) * x;
Z = (Y / y) * z;
// X, Y and Z input refer to a D65/2° standard illuminant.
// sR, sG and sB (standard RGB) output range = 0 ÷ 255
// convert XYZ to RGB - CIE XYZ to sRGB
if (addXYZScaling) {
X = X / 100.0f;
Y = Y / 100.0f;
Z = Z / 100.0f;
}
r = (X * 3.2406f) - (Y * 1.5372f) - (Z * 0.4986f);
g = -(X * 0.9689f) + (Y * 1.8758f) + (Z * 0.0415f);
b = (X * 0.0557f) - (Y * 0.2040f) + (Z * 1.0570f);
// apply gamma 2.2 correction
r = (r <= 0.0031308f ? 12.92f * r : (1.055f) * pow(r, (1.0f / 2.4f)) - 0.055f);
g = (g <= 0.0031308f ? 12.92f * g : (1.055f) * pow(g, (1.0f / 2.4f)) - 0.055f);
b = (b <= 0.0031308f ? 12.92f * b : (1.055f) * pow(b, (1.0f / 2.4f)) - 0.055f);
// Round off
r = clamp(r, 0, 1);
g = clamp(g, 0, 1);
b = clamp(b, 0, 1);
// these rgb values are in the range of 0 to 1, convert to limit of HW specific LED
rgb.r = (uint8_t)(r * 255);
rgb.g = (uint8_t)(g * 255);
rgb.b = (uint8_t)(b * 255);
return rgb;
}
espXyColor_t espRgbToXYColor(uint8_t r, uint8_t g, uint8_t b) {
espRgbColor_t rgb = {r, g, b};
return espRgbColorToXYColor(rgb);
}
espXyColor_t espRgbColorToXYColor(espRgbColor_t rgb) {
// convert RGB to xy color space
// https://www.easyrgb.com/en/math.php
// https://en.wikipedia.org/wiki/SRGB
// refer https://en.wikipedia.org/wiki/CIE_1931_color_space#CIE_xy_chromaticity_diagram_and_the_CIE_xyY_color_space
espXyColor_t xy;
float r, g, b;
float X, Y, Z;
float x, y;
r = ((float)rgb.r) / 255.0f;
g = ((float)rgb.g) / 255.0f;
b = ((float)rgb.b) / 255.0f;
// convert RGB to XYZ - sRGB to CIE XYZ
r = (r <= 0.04045f ? r / 12.92f : pow((r + 0.055f) / 1.055f, 2.4f));
g = (g <= 0.04045f ? g / 12.92f : pow((g + 0.055f) / 1.055f, 2.4f));
b = (b <= 0.04045f ? b / 12.92f : pow((b + 0.055f) / 1.055f, 2.4f));
// https://gist.github.com/popcorn245/30afa0f98eea1c2fd34d
X = r * 0.649926f + g * 0.103455f + b * 0.197109f;
Y = r * 0.234327f + g * 0.743075f + b * 0.022598f;
Z = r * 0.0000000f + g * 0.053077f + b * 1.035763f;
// sR, sG and sB (standard RGB) input range = 0 ÷ 255
// X, Y and Z output refer to a D65/2° standard illuminant.
X = r * 0.4124564f + g * 0.3575761f + b * 0.1804375f;
Y = r * 0.2126729f + g * 0.7151522f + b * 0.0721750f;
Z = r * 0.0193339f + g * 0.1191920f + b * 0.9503041f;
// Calculate xy values
x = X / (X + Y + Z);
y = Y / (X + Y + Z);
// convert to 0-65535 range
xy.x = (uint16_t)(x * 65535);
xy.y = (uint16_t)(y * 65535);
return xy;
}
espRgbColor_t espCTToRgbColor(uint16_t ct) {
espCtColor_t ctColor = {ct};
return espCTColorToRgbColor(ctColor);
}
espRgbColor_t espCTColorToRgbColor(espCtColor_t ct) {
espRgbColor_t rgb = {0, 0, 0};
float r, g, b;
if (ct.ctMireds == 0) {
return rgb;
}
// Algorithm credits to Tanner Helland: https://tannerhelland.com/2012/09/18/convert-temperature-rgb-algorithm-code.html
// Convert Mireds to centiKelvins. k = 1,000,000/mired
float ctCentiKelvin = 10000 / ct.ctMireds;
// Red
if (ctCentiKelvin <= 66) {
r = 255;
} else {
r = 329.698727446f * pow(ctCentiKelvin - 60, -0.1332047592f);
}
// Green
if (ctCentiKelvin <= 66) {
g = 99.4708025861f * log(ctCentiKelvin) - 161.1195681661f;
} else {
g = 288.1221695283f * pow(ctCentiKelvin - 60, -0.0755148492f);
}
// Blue
if (ctCentiKelvin >= 66) {
b = 255;
} else {
if (ctCentiKelvin <= 19) {
b = 0;
} else {
b = 138.5177312231 * log(ctCentiKelvin - 10) - 305.0447927307;
}
}
rgb.r = (uint8_t)clamp(r, 0, 255);
rgb.g = (uint8_t)clamp(g, 0, 255);
rgb.b = (uint8_t)clamp(b, 0, 255);
return rgb;
}
+71
View File
@@ -0,0 +1,71 @@
/*
*
* Copyright (c) 2021 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
struct RgbColor_t {
uint8_t r;
uint8_t g;
uint8_t b;
};
struct HsvColor_t {
uint16_t h;
uint8_t s;
uint8_t v;
};
struct XyColor_t {
uint16_t x;
uint16_t y;
};
struct CtColor_t {
uint16_t ctMireds;
};
typedef struct RgbColor_t espRgbColor_t;
typedef struct HsvColor_t espHsvColor_t;
typedef struct XyColor_t espXyColor_t;
typedef struct CtColor_t espCtColor_t;
espRgbColor_t espXYToRgbColor(uint8_t Level, uint16_t current_X, uint16_t current_Y, bool addXYZScaling);
espRgbColor_t espXYColorToRgb(uint8_t Level, espXyColor_t xy);
espXyColor_t espRgbColorToXYColor(espRgbColor_t rgb);
espXyColor_t espRgbToXYColor(uint8_t r, uint8_t g, uint8_t b);
espRgbColor_t espHsvColorToRgbColor(espHsvColor_t hsv);
espRgbColor_t espHsvToRgbColor(uint16_t h, uint8_t s, uint8_t v);
espRgbColor_t espCTColorToRgbColor(espCtColor_t ct);
espRgbColor_t espCTToRgbColor(uint16_t ct);
espHsvColor_t espRgbColorToHsvColor(espRgbColor_t rgb);
espHsvColor_t espRgbToHsvColor(uint8_t r, uint8_t g, uint8_t b);
extern const espHsvColor_t HSV_BLACK, HSV_WHITE, HSV_RED, HSV_YELLOW, HSV_GREEN, HSV_CYAN, HSV_BLUE, HSV_MAGENTA;
extern const espCtColor_t COOL_WHITE_COLOR_TEMPERATURE, DAYLIGHT_WHITE_COLOR_TEMPERATURE, WHITE_COLOR_TEMPERATURE, SOFT_WHITE_COLOR_TEMPERATURE,
WARM_WHITE_COLOR_TEMPERATURE;
extern const espRgbColor_t RGB_BLACK, RGB_WHITE, RGB_RED, RGB_YELLOW, RGB_GREEN, RGB_CYAN, RGB_BLUE, RGB_MAGENTA;
#ifdef __cplusplus
}
#endif
+607
View File
@@ -0,0 +1,607 @@
/*
Esp.cpp - ESP31B-specific APIs
Copyright (c) 2015 Ivan Grokhotkov. 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 "Arduino.h"
#include "Esp.h"
#include "esp_sleep.h"
#include "spi_flash_mmap.h"
#include "esp_idf_version.h"
#include <memory>
#include <soc/soc.h>
#include <esp_partition.h>
extern "C" {
#include "esp_ota_ops.h"
#include "esp_image_format.h"
}
#include <MD5Builder.h>
#include "soc/spi_reg.h"
#include "esp_system.h"
#include "esp_chip_info.h"
#include "esp_mac.h"
#include "esp_flash.h"
// Include HAL layer for flash clock access
#include "hal/spi_flash_ll.h"
#if CONFIG_IDF_TARGET_ESP32
#include "soc/spi_struct.h"
#else
// All modern chips (S2, S3, C2, C3, C5, C6, H2, P4) use spimem
#include "hal/spimem_flash_ll.h"
// Try to include the newer c_struct header first, fall back to regular struct
#if __has_include("soc/spi_mem_c_struct.h")
#include "soc/spi_mem_c_struct.h"
#else
#include "soc/spi_mem_struct.h"
#endif
#endif
#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
#include "esp32/rom/spi_flash.h"
#include "soc/efuse_reg.h"
#define ESP_FLASH_IMAGE_BASE 0x1000 // Flash offset containing flash size and spi mode
#elif CONFIG_IDF_TARGET_ESP32S2
#include "esp32s2/rom/spi_flash.h"
#include "soc/efuse_reg.h"
#define ESP_FLASH_IMAGE_BASE 0x1000
#elif CONFIG_IDF_TARGET_ESP32S3
#include "esp32s3/rom/spi_flash.h"
#include "soc/efuse_reg.h"
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32s3 is located at 0x0000
#elif CONFIG_IDF_TARGET_ESP32C2
#include "esp32c2/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32c2 is located at 0x0000
#elif CONFIG_IDF_TARGET_ESP32C3
#include "esp32c3/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32c3 is located at 0x0000
#elif CONFIG_IDF_TARGET_ESP32C6
#include "esp32c6/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32c6 is located at 0x0000
#elif CONFIG_IDF_TARGET_ESP32H2
#include "esp32h2/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32h2 is located at 0x0000
#elif CONFIG_IDF_TARGET_ESP32P4
#include "esp32p4/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x2000 // Esp32p4 is located at 0x2000
#elif CONFIG_IDF_TARGET_ESP32C5
#include "esp32c5/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x2000 // Esp32c5 is located at 0x2000
#elif CONFIG_IDF_TARGET_ESP32C61
#include "esp32c61/rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32c61 is located at 0x0000
#else
#error Target CONFIG_IDF_TARGET is not supported
#endif
#else // ESP32 Before IDF 4.0
#include "rom/spi_flash.h"
#define ESP_FLASH_IMAGE_BASE 0x1000
#endif
// REG_SPI_BASE is not defined for S3/C3 ??
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3
#ifdef REG_SPI_BASE
#undef REG_SPI_BASE
#endif // REG_SPI_BASE
#define REG_SPI_BASE(i) (DR_REG_SPI1_BASE + (((i) > 1) ? (((i) * 0x1000) + 0x20000) : (((~(i)) & 1) * 0x1000)))
#endif // TARGET
/**
* User-defined Literals
* usage:
*
* uint32_t = test = 10_MHz; // --> 10000000
*/
unsigned long long operator""_kHz(unsigned long long x) {
return x * 1000;
}
unsigned long long operator""_MHz(unsigned long long x) {
return x * 1000 * 1000;
}
unsigned long long operator""_GHz(unsigned long long x) {
return x * 1000 * 1000 * 1000;
}
unsigned long long operator""_kBit(unsigned long long x) {
return x * 1024;
}
unsigned long long operator""_MBit(unsigned long long x) {
return x * 1024 * 1024;
}
unsigned long long operator""_GBit(unsigned long long x) {
return x * 1024 * 1024 * 1024;
}
unsigned long long operator""_kB(unsigned long long x) {
return x * 1024;
}
unsigned long long operator""_MB(unsigned long long x) {
return x * 1024 * 1024;
}
unsigned long long operator""_GB(unsigned long long x) {
return x * 1024 * 1024 * 1024;
}
EspClass ESP;
void EspClass::deepSleep(uint64_t time_us) {
esp_deep_sleep(time_us);
}
void EspClass::restart(void) {
esp_restart();
}
uint32_t EspClass::getHeapSize(void) {
return heap_caps_get_total_size(MALLOC_CAP_INTERNAL);
}
uint32_t EspClass::getFreeHeap(void) {
return heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
}
uint32_t EspClass::getMinFreeHeap(void) {
return heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL);
}
uint32_t EspClass::getMaxAllocHeap(void) {
return heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL);
}
uint32_t EspClass::getPsramSize(void) {
if (psramFound()) {
return heap_caps_get_total_size(MALLOC_CAP_SPIRAM);
}
return 0;
}
uint32_t EspClass::getFreePsram(void) {
if (psramFound()) {
return heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
}
return 0;
}
uint32_t EspClass::getMinFreePsram(void) {
if (psramFound()) {
return heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM);
}
return 0;
}
uint32_t EspClass::getMaxAllocPsram(void) {
if (psramFound()) {
return heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM);
}
return 0;
}
static uint32_t sketchSize(sketchSize_t response) {
esp_image_metadata_t data;
const esp_partition_t *running = esp_ota_get_running_partition();
if (!running) {
return 0;
}
const esp_partition_pos_t running_pos = {
.offset = running->address,
.size = running->size,
};
data.start_addr = running_pos.offset;
esp_image_verify(ESP_IMAGE_VERIFY, &running_pos, &data);
if (response) {
return running_pos.size - data.image_len;
} else {
return data.image_len;
}
}
uint32_t EspClass::getSketchSize() {
return sketchSize(SKETCH_SIZE_TOTAL);
}
String EspClass::getSketchMD5() {
static String result;
if (result.length()) {
return result;
}
uint32_t lengthLeft = getSketchSize();
const esp_partition_t *running = esp_ota_get_running_partition();
if (!running) {
log_e("Partition could not be found");
return String();
}
const size_t bufSize = SPI_FLASH_SEC_SIZE;
uint8_t *pb = (uint8_t *)malloc(bufSize);
if (!pb) {
log_e("Not enough memory to allocate buffer");
return String();
}
uint32_t offset = 0;
MD5Builder md5;
md5.begin();
while (lengthLeft > 0) {
size_t readBytes = (lengthLeft < bufSize) ? lengthLeft : bufSize;
if (!ESP.flashRead(running->address + offset, (uint32_t *)pb, (readBytes + 3) & ~3)) {
free(pb);
log_e("Could not read buffer from flash");
return String();
}
md5.add(pb, readBytes);
lengthLeft -= readBytes;
offset += readBytes;
#if CONFIG_FREERTOS_UNICORE
delay(1); // Fix solo WDT
#endif
}
free(pb);
md5.calculate();
result = md5.toString();
return result;
}
uint32_t EspClass::getFreeSketchSpace() {
const esp_partition_t *_partition = esp_ota_get_next_update_partition(NULL);
if (!_partition) {
return 0;
}
return _partition->size;
}
uint16_t EspClass::getChipRevision(void) {
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
return chip_info.revision;
}
const char *EspClass::getChipModel(void) {
#if CONFIG_IDF_TARGET_ESP32
uint32_t chip_ver = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_PACKAGE);
uint32_t pkg_ver = chip_ver & 0x7;
switch (pkg_ver) {
case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ6:
if ((getChipRevision() / 100) == 3) {
return "ESP32-D0WDQ6-V3";
} else {
return "ESP32-D0WDQ6";
}
case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ5:
if ((getChipRevision() / 100) == 3) {
return "ESP32-D0WD-V3";
} else {
return "ESP32-D0WD";
}
case EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5: return "ESP32-D2WD";
case EFUSE_RD_CHIP_VER_PKG_ESP32U4WDH: return "ESP32-U4WDH";
case EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4: return "ESP32-PICO-D4";
case EFUSE_RD_CHIP_VER_PKG_ESP32PICOV302: return "ESP32-PICO-V3-02";
case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDR2V3: return "ESP32-D0WDR2-V3";
default: return "Unknown";
}
#elif CONFIG_IDF_TARGET_ESP32S2
uint32_t pkg_ver = REG_GET_FIELD(EFUSE_RD_MAC_SPI_SYS_3_REG, EFUSE_PKG_VERSION);
switch (pkg_ver) {
case 0: return "ESP32-S2";
case 1: return "ESP32-S2FH16";
case 2: return "ESP32-S2FH32";
default: return "ESP32-S2 (Unknown)";
}
#else
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
switch (chip_info.model) {
case CHIP_ESP32S3: return "ESP32-S3";
case CHIP_ESP32C3: return "ESP32-C3";
case CHIP_ESP32C2: return "ESP32-C2";
case CHIP_ESP32C6: return "ESP32-C6";
case CHIP_ESP32H2: return "ESP32-H2";
case CHIP_ESP32P4: return "ESP32-P4";
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
case CHIP_ESP32C5: return "ESP32-C5";
case CHIP_ESP32C61: return "ESP32-C61";
case CHIP_ESP32H21: return "ESP32-H21";
#endif
default: return "UNKNOWN";
}
#endif
}
uint8_t EspClass::getChipCores(void) {
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
return chip_info.cores;
}
const char *EspClass::getSdkVersion(void) {
return esp_get_idf_version();
}
const char *EspClass::getCoreVersion(void) {
return ESP_ARDUINO_VERSION_STR;
}
uint32_t ESP_getFlashChipId(void) {
uint32_t id = g_rom_flashchip.device_id;
id = ((id & 0xff) << 16) | ((id >> 16) & 0xff) | (id & 0xff00);
return id;
}
uint32_t EspClass::getFlashChipSize(void) {
uint32_t id = (ESP_getFlashChipId() >> 16) & 0xFF;
return 2 << (id - 1);
}
uint32_t EspClass::getFlashChipSpeed(void) {
esp_image_header_t fhdr;
if (esp_flash_read(esp_flash_default_chip, (void *)&fhdr, ESP_FLASH_IMAGE_BASE, sizeof(esp_image_header_t)) && fhdr.magic != ESP_IMAGE_HEADER_MAGIC) {
return 0;
}
return magicFlashChipSpeed(fhdr.spi_speed);
}
FlashMode_t EspClass::getFlashChipMode(void) {
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61
uint32_t spi_ctrl = REG_READ(PERIPHS_SPI_FLASH_CTRL);
#elif CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C6
uint32_t spi_ctrl = REG_READ(DR_REG_SPI0_BASE + 0x8);
#else
uint32_t spi_ctrl = REG_READ(SPI_CTRL_REG(0));
#endif
/* Not all of the following constants are already defined in older versions of spi_reg.h, so do it manually for now*/
if (spi_ctrl & BIT(24)) { //SPI_FREAD_QIO
return (FM_QIO);
} else if (spi_ctrl & BIT(20)) { //SPI_FREAD_QUAD
return (FM_QOUT);
} else if (spi_ctrl & BIT(23)) { //SPI_FREAD_DIO
return (FM_DIO);
} else if (spi_ctrl & BIT(14)) { // SPI_FREAD_DUAL
return (FM_DOUT);
} else if (spi_ctrl & BIT(13)) { //SPI_FASTRD_MODE
return (FM_FAST_READ);
} else {
return (FM_SLOW_READ);
}
}
uint32_t EspClass::magicFlashChipSize(uint8_t flashByte) {
/*
FLASH_SIZES = {
"1MB": 0x00,
"2MB": 0x10,
"4MB": 0x20,
"8MB": 0x30,
"16MB": 0x40,
"32MB": 0x50,
"64MB": 0x60,
"128MB": 0x70,
}
*/
switch (flashByte & 0x0F) {
case 0x0: return (1_MB); // 8 MBit (1MB)
case 0x1: return (2_MB); // 16 MBit (2MB)
case 0x2: return (4_MB); // 32 MBit (4MB)
case 0x3: return (8_MB); // 64 MBit (8MB)
case 0x4: return (16_MB); // 128 MBit (16MB)
case 0x5: return (32_MB); // 256 MBit (32MB)
case 0x6: return (64_MB); // 512 MBit (64MB)
case 0x7: return (128_MB); // 1 GBit (128MB)
default: // fail?
return 0;
}
}
uint32_t EspClass::magicFlashChipSpeed(uint8_t flashByte) {
#if CONFIG_IDF_TARGET_ESP32C2
/*
FLASH_FREQUENCY = {
"60m": 0xF,
"30m": 0x0,
"20m": 0x1,
"15m": 0x2,
}
*/
switch (flashByte & 0x0F) {
case 0xF: return (60_MHz);
case 0x0: return (30_MHz);
case 0x1: return (20_MHz);
case 0x2: return (15_MHz);
default: // fail?
return 0;
}
#elif CONFIG_IDF_TARGET_ESP32C6
/*
FLASH_FREQUENCY = {
"80m": 0x0, # workaround for wrong mspi HS div value in ROM
"40m": 0x0,
"20m": 0x2,
}
*/
switch (flashByte & 0x0F) {
case 0x0: return (80_MHz);
case 0x2: return (20_MHz);
default: // fail?
return 0;
}
#elif CONFIG_IDF_TARGET_ESP32C61
/*
FLASH_FREQUENCY = {
"80m": 0xF,
"40m": 0x0,
"20m": 0x2,
}
*/
switch (flashByte & 0x0F) {
case 0xF: return (80_MHz);
case 0x0: return (40_MHz);
case 0x2: return (20_MHz);
default: // fail?
return 0;
}
#elif CONFIG_IDF_TARGET_ESP32H2
/*
FLASH_FREQUENCY = {
"48m": 0xF,
"24m": 0x0,
"16m": 0x1,
"12m": 0x2,
}
*/
switch (flashByte & 0x0F) {
case 0xF: return (48_MHz);
case 0x0: return (24_MHz);
case 0x1: return (16_MHz);
case 0x2: return (12_MHz);
default: // fail?
return 0;
}
#else
/*
FLASH_FREQUENCY = {
"80m": 0xF,
"40m": 0x0,
"26m": 0x1,
"20m": 0x2,
}
*/
switch (flashByte & 0x0F) {
case 0xF: return (80_MHz);
case 0x0: return (40_MHz);
case 0x1: return (26_MHz);
case 0x2: return (20_MHz);
default: // fail?
return 0;
}
#endif
}
FlashMode_t EspClass::magicFlashChipMode(uint8_t flashByte) {
FlashMode_t mode = (FlashMode_t)flashByte;
if (mode > FM_SLOW_READ) {
mode = FM_UNKNOWN;
}
return mode;
}
bool EspClass::flashEraseSector(uint32_t sector) {
return esp_flash_erase_region(esp_flash_default_chip, sector * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE) == ESP_OK;
}
// Warning: These functions do not work with encrypted flash
bool EspClass::flashWrite(uint32_t offset, uint32_t *data, size_t size) {
return esp_flash_write(esp_flash_default_chip, (const void *)data, offset, size) == ESP_OK;
}
bool EspClass::flashRead(uint32_t offset, uint32_t *data, size_t size) {
return esp_flash_read(esp_flash_default_chip, (void *)data, offset, size) == ESP_OK;
}
bool EspClass::partitionEraseRange(const esp_partition_t *partition, uint32_t offset, size_t size) {
return esp_partition_erase_range(partition, offset, size) == ESP_OK;
}
bool EspClass::partitionWrite(const esp_partition_t *partition, uint32_t offset, uint32_t *data, size_t size) {
return esp_partition_write(partition, offset, data, size) == ESP_OK;
}
bool EspClass::partitionRead(const esp_partition_t *partition, uint32_t offset, uint32_t *data, size_t size) {
return esp_partition_read(partition, offset, data, size) == ESP_OK;
}
uint64_t EspClass::getEfuseMac(void) {
uint64_t _chipmacid = 0LL;
esp_efuse_mac_get_default((uint8_t *)(&_chipmacid));
return _chipmacid;
}
// ============================================================================
// Flash Frequency Runtime Detection
// ============================================================================
/**
* @brief Read the source clock frequency using ESP-IDF HAL functions
* @return Source clock frequency in MHz (80, 120, 160, or 240)
*/
uint8_t EspClass::getFlashSourceFrequencyMHz(void) {
#if CONFIG_IDF_TARGET_ESP32
// ESP32: Use HAL function
return spi_flash_ll_get_source_clock_freq_mhz(0); // host_id = 0 for SPI0
#else
// All modern MCUs: Use spimem HAL function
return spimem_flash_ll_get_source_freq_mhz();
#endif
}
/**
* @brief Read the clock divider from hardware using HAL structures
* Based on ESP-IDF HAL implementation:
* - ESP32: Uses SPI1.clock (typedef in spi_flash_ll.h)
* - All newer MCUs: Use SPIMEM1.clock (typedef in spimem_flash_ll.h)
* @return Clock divider value (1 = no division, 2 = divide by 2, etc.)
*/
uint8_t EspClass::getFlashClockDivider(void) {
#if CONFIG_IDF_TARGET_ESP32
// ESP32: Flash uses SPI1
// See: line 52: esp-idf/components/hal/esp32/include/hal/spi_flash_ll.h
if (SPI1.clock.clk_equ_sysclk) {
return 1; // 1:1 clock
}
return SPI1.clock.clkcnt_n + 1;
#else
// All newer MCUs: Flash uses SPIMEM1
// See: esp-idf/components/hal/esp32*/include/hal/spimem_flash_ll.h
// Example S3: line 38: typedef typeof(SPIMEM1.clock.val) spimem_flash_ll_clock_reg_t;
// Example C5: lines 97-99: esp-idf/components/soc/esp32c5/mp/include/soc/spi_mem_struct.h
if (SPIMEM1.clock.clk_equ_sysclk) {
return 1; // 1:1 clock
}
return SPIMEM1.clock.clkcnt_n + 1;
#endif
}
/**
* @brief Get the actual flash frequency in MHz
* @return Flash frequency in MHz (80, 120, 160, or 240)
*/
uint32_t EspClass::getFlashFrequencyMHz(void) {
uint8_t source = getFlashSourceFrequencyMHz();
uint8_t divider = getFlashClockDivider();
if (divider == 0) {
divider = 1; // Safety check
}
return source / divider;
}
+125
View File
@@ -0,0 +1,125 @@
/*
Esp.h - ESP31B-specific APIs
Copyright (c) 2015 Ivan Grokhotkov. 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
*/
#ifndef ESP_H
#define ESP_H
#include <Arduino.h>
#include <esp_partition.h>
#include <hal/cpu_hal.h>
#include "esp_cpu.h"
/**
* AVR macros for WDT management
*/
typedef enum {
WDTO_0MS = 0, //!< WDTO_0MS
WDTO_15MS = 15, //!< WDTO_15MS
WDTO_30MS = 30, //!< WDTO_30MS
WDTO_60MS = 60, //!< WDTO_60MS
WDTO_120MS = 120, //!< WDTO_120MS
WDTO_250MS = 250, //!< WDTO_250MS
WDTO_500MS = 500, //!< WDTO_500MS
WDTO_1S = 1000, //!< WDTO_1S
WDTO_2S = 2000, //!< WDTO_2S
WDTO_4S = 4000, //!< WDTO_4S
WDTO_8S = 8000 //!< WDTO_8S
} WDTO_t;
typedef enum {
FM_QIO = 0x00,
FM_QOUT = 0x01,
FM_DIO = 0x02,
FM_DOUT = 0x03,
FM_FAST_READ = 0x04,
FM_SLOW_READ = 0x05,
FM_UNKNOWN = 0xff
} FlashMode_t;
typedef enum {
SKETCH_SIZE_TOTAL = 0,
SKETCH_SIZE_FREE = 1
} sketchSize_t;
class EspClass {
public:
EspClass() {}
~EspClass() {}
void restart();
//Internal RAM
uint32_t getHeapSize(); //total heap size
uint32_t getFreeHeap(); //available heap
uint32_t getMinFreeHeap(); //lowest level of free heap since boot
uint32_t getMaxAllocHeap(); //largest block of heap that can be allocated at once
//SPI RAM
uint32_t getPsramSize();
uint32_t getFreePsram();
uint32_t getMinFreePsram();
uint32_t getMaxAllocPsram();
uint16_t getChipRevision();
const char *getChipModel();
uint8_t getChipCores();
uint32_t getCpuFreqMHz() {
return getCpuFrequencyMhz();
}
inline uint32_t getCycleCount() __attribute__((always_inline));
const char *getSdkVersion(); //version of ESP-IDF
const char *getCoreVersion(); //version of this core
void deepSleep(uint64_t time_us);
uint32_t getFlashChipSize();
uint32_t getFlashChipSpeed();
FlashMode_t getFlashChipMode();
// Flash frequency runtime detection
uint32_t getFlashFrequencyMHz();
uint8_t getFlashSourceFrequencyMHz();
uint8_t getFlashClockDivider();
uint32_t magicFlashChipSize(uint8_t flashByte);
uint32_t magicFlashChipSpeed(uint8_t flashByte);
FlashMode_t magicFlashChipMode(uint8_t flashByte);
uint32_t getSketchSize();
String getSketchMD5();
uint32_t getFreeSketchSpace();
bool flashEraseSector(uint32_t sector);
bool flashWrite(uint32_t offset, uint32_t *data, size_t size);
bool flashRead(uint32_t offset, uint32_t *data, size_t size);
bool partitionEraseRange(const esp_partition_t *partition, uint32_t offset, size_t size);
bool partitionWrite(const esp_partition_t *partition, uint32_t offset, uint32_t *data, size_t size);
bool partitionRead(const esp_partition_t *partition, uint32_t offset, uint32_t *data, size_t size);
uint64_t getEfuseMac();
};
uint32_t ARDUINO_ISR_ATTR EspClass::getCycleCount() {
return (uint32_t)esp_cpu_get_cycle_count();
}
extern EspClass ESP;
#endif //ESP_H
+427
View File
@@ -0,0 +1,427 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "FirmwareMSC.h"
#if CONFIG_TINYUSB_MSC_ENABLED
#include <cstring>
#include "esp_partition.h"
#include "esp_ota_ops.h"
#include "esp_image_format.h"
#include "pins_arduino.h"
#include "esp32-hal.h"
#include "firmware_msc_fat.h"
#include "spi_flash_mmap.h"
#ifndef USB_FW_MSC_VENDOR_ID
#define USB_FW_MSC_VENDOR_ID "ESP32" //max 8 chars
#endif
#ifndef USB_FW_MSC_PRODUCT_ID
#define USB_FW_MSC_PRODUCT_ID "Firmware MSC" //max 16 chars
#endif
#ifndef USB_FW_MSC_PRODUCT_REVISION
#define USB_FW_MSC_PRODUCT_REVISION "1.0" //max 4 chars
#endif
#ifndef USB_FW_MSC_VOLUME_NAME
#define USB_FW_MSC_VOLUME_NAME "ESP32-FWMSC" //max 11 chars
#endif
#ifndef USB_FW_MSC_SERIAL_NUMBER
#define USB_FW_MSC_SERIAL_NUMBER 0x00000000
#endif
ESP_EVENT_DEFINE_BASE(ARDUINO_FIRMWARE_MSC_EVENTS);
esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait);
esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg);
//General Variables
static uint8_t *msc_ram_disk = NULL;
static fat_boot_sector_t *msc_boot = NULL;
static uint8_t *msc_table = NULL;
static uint16_t msc_table_sectors = 0;
static uint16_t msc_total_sectors = 0;
static bool mcs_is_fat16 = false;
//Firmware Read
static const esp_partition_t *msc_run_partition = NULL;
static uint16_t fw_start_sector = 0;
static uint16_t fw_end_sector = 0;
static size_t fw_size = 0;
static fat_dir_entry_t *fw_entry = NULL;
//Firmware Write
typedef enum {
MSC_UPDATE_IDLE,
MSC_UPDATE_STARTING,
MSC_UPDATE_RUNNING,
MSC_UPDATE_END
} msc_update_state_t;
static const esp_partition_t *msc_ota_partition = NULL;
static msc_update_state_t msc_update_state = MSC_UPDATE_IDLE;
static uint16_t msc_update_start_sector = 0;
static uint32_t msc_update_bytes_written = 0;
static fat_dir_entry_t *msc_update_entry = NULL;
static uint32_t get_firmware_size(const esp_partition_t *partition) {
esp_image_metadata_t data;
const esp_partition_pos_t running_pos = {
.offset = partition->address,
.size = partition->size,
};
data.start_addr = running_pos.offset;
esp_image_verify(ESP_IMAGE_VERIFY, &running_pos, &data);
return data.image_len;
}
//Get number of sectors required based on the size of the firmware and OTA partition
static size_t msc_update_get_required_disk_sectors() {
size_t data_sectors = 16;
size_t total_sectors = 0;
msc_run_partition = esp_ota_get_running_partition();
msc_ota_partition = esp_ota_get_next_update_partition(NULL);
if (msc_run_partition) {
fw_size = get_firmware_size(msc_run_partition);
data_sectors += FAT_SIZE_TO_SECTORS(fw_size);
log_d("APP size: %u (%u sectors)", fw_size, FAT_SIZE_TO_SECTORS(fw_size));
} else {
log_w("APP partition not found. Reading disabled");
}
if (msc_ota_partition) {
data_sectors += FAT_SIZE_TO_SECTORS(msc_ota_partition->size);
log_d("OTA size: %u (%u sectors)", msc_ota_partition->size, FAT_SIZE_TO_SECTORS(msc_ota_partition->size));
} else {
log_w("OTA partition not found. Writing disabled");
}
msc_table_sectors = fat_sectors_per_alloc_table(data_sectors, false);
total_sectors = data_sectors + msc_table_sectors + 2;
if (total_sectors > 0xFF4) {
log_d("USING FAT16");
mcs_is_fat16 = true;
total_sectors -= msc_table_sectors;
msc_table_sectors = fat_sectors_per_alloc_table(data_sectors, true);
total_sectors += msc_table_sectors;
} else {
log_d("USING FAT12");
mcs_is_fat16 = false;
}
log_d("FAT sector size: %u", DISK_SECTOR_SIZE);
log_d("FAT data sectors: %u", data_sectors);
log_d("FAT table sectors: %u", msc_table_sectors);
log_d("FAT total sectors: %u (%uKB)", total_sectors, (total_sectors * DISK_SECTOR_SIZE) / 1024);
return total_sectors;
}
//setup the ramdisk and add the firmware download file
static bool msc_update_setup_disk(const char *volume_label, uint32_t serial_number) {
msc_total_sectors = msc_update_get_required_disk_sectors();
uint8_t ram_sectors = msc_table_sectors + 2;
msc_ram_disk = (uint8_t *)calloc(ram_sectors, DISK_SECTOR_SIZE);
if (!msc_ram_disk) {
log_e("Failed to allocate RAM Disk: %u bytes", ram_sectors * DISK_SECTOR_SIZE);
return false;
}
fw_start_sector = ram_sectors;
fw_end_sector = fw_start_sector;
msc_boot = fat_add_boot_sector(msc_ram_disk, msc_total_sectors, msc_table_sectors, fat_file_system_type(mcs_is_fat16), volume_label, serial_number);
msc_table = fat_add_table(msc_ram_disk, msc_boot, mcs_is_fat16);
//fat_dir_entry_t * label = fat_add_label(msc_ram_disk, volume_label);
if (msc_run_partition) {
fw_entry = fat_add_root_file(msc_ram_disk, 0, "FIRMWARE", "BIN", fw_size, 2, mcs_is_fat16);
fw_end_sector = FAT_SIZE_TO_SECTORS(fw_size) + fw_start_sector;
}
return true;
}
static void msc_update_delete_disk() {
fw_entry = NULL;
fw_size = 0;
fw_end_sector = 0;
fw_start_sector = 0;
msc_table = NULL;
msc_boot = NULL;
msc_table_sectors = 0;
msc_total_sectors = 0;
msc_run_partition = NULL;
msc_ota_partition = NULL;
msc_update_state = MSC_UPDATE_IDLE;
msc_update_start_sector = 0;
msc_update_bytes_written = 0;
msc_update_entry = NULL;
free(msc_ram_disk);
msc_ram_disk = NULL;
}
//filter out entries to only include BINs in the root folder
static fat_dir_entry_t *msc_update_get_root_bin_entry(uint8_t index) {
fat_dir_entry_t *entry = (fat_dir_entry_t *)(msc_ram_disk + ((msc_boot->sectors_per_alloc_table + 1) * DISK_SECTOR_SIZE) + (index * sizeof(fat_dir_entry_t)));
fat_lfn_entry_t *lfn = (fat_lfn_entry_t *)entry;
//empty entry
if (entry->file_magic == 0) {
return NULL;
}
//long file name
if (lfn->attr == 0x0F && lfn->type == 0x00 && lfn->first_cluster == 0x0000) {
return NULL;
}
//only files marked as archives
if (entry->file_attr != FAT_FILE_ATTR_ARCHIVE) {
return NULL;
}
//deleted
if (entry->file_magic == 0xE5 || entry->file_magic == 0x05) {
return NULL;
}
//not bins
if (memcmp("BIN", entry->file_extension, 3)) {
return NULL;
}
return entry;
}
//get an empty bin (the host will add an entry for file about to be written with size of zero)
static fat_dir_entry_t *msc_update_find_new_bin() {
for (uint8_t i = 16; i;) {
i--;
fat_dir_entry_t *entry = msc_update_get_root_bin_entry(i);
if (entry && entry->file_size == 0) {
return entry;
}
}
return NULL;
}
//get a bin starting from particular sector
static fat_dir_entry_t *msc_update_find_bin(uint16_t sector) {
for (uint8_t i = 16; i;) {
i--;
fat_dir_entry_t *entry = msc_update_get_root_bin_entry(i);
if (entry && entry->data_start_sector == (sector - msc_boot->sectors_per_alloc_table)) {
return entry;
}
}
return NULL;
}
//write the new data and erase the flash blocks when necessary
static esp_err_t msc_update_write(const esp_partition_t *partition, uint32_t offset, void *data, size_t size) {
esp_err_t err = ESP_OK;
if ((offset & (SPI_FLASH_SEC_SIZE - 1)) == 0) {
err = esp_partition_erase_range(partition, offset, SPI_FLASH_SEC_SIZE);
log_v("ERASE[0x%08X]: %s", offset, (err != ESP_OK) ? "FAIL" : "OK");
if (err != ESP_OK) {
return err;
}
}
return esp_partition_write(partition, offset, data, size);
}
//called when error was encountered while updating
static void msc_update_error() {
log_e("UPDATE_ERROR: %u", msc_update_bytes_written);
arduino_firmware_msc_event_data_t p;
p.error.size = msc_update_bytes_written;
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_ERROR_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
msc_update_state = MSC_UPDATE_IDLE;
msc_update_entry = NULL;
msc_update_bytes_written = 0;
msc_update_start_sector = 0;
}
//called when all firmware bytes have been received
static void msc_update_end() {
log_d("UPDATE_END: %u", msc_update_entry->file_size);
msc_update_state = MSC_UPDATE_END;
size_t ota_size = get_firmware_size(msc_ota_partition);
if (ota_size != msc_update_entry->file_size) {
log_e("OTA SIZE MISMATCH %u != %u", ota_size, msc_update_entry->file_size);
msc_update_error();
return;
}
if (!ota_size || esp_ota_set_boot_partition(msc_ota_partition) != ESP_OK) {
log_e("ENABLING OTA PARTITION FAILED");
msc_update_error();
return;
}
arduino_firmware_msc_event_data_t p;
p.end.size = msc_update_entry->file_size;
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_END_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
}
static int32_t msc_write(uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) {
//log_d("lba: %u, offset: %u, bufsize: %u", lba, offset, bufsize);
if (lba < fw_start_sector) {
//write to sectors that are in RAM
memcpy(msc_ram_disk + (lba * DISK_SECTOR_SIZE) + offset, buffer, bufsize);
if (msc_ota_partition && lba == (fw_start_sector - 1)) {
//monitor the root folder table
if (msc_update_state <= MSC_UPDATE_RUNNING) {
fat_dir_entry_t *update_entry = msc_update_find_new_bin();
if (update_entry) {
if (msc_update_entry) {
log_v("REPLACING ENTRY");
} else {
log_v("ASSIGNING ENTRY");
}
if (msc_update_state <= MSC_UPDATE_STARTING) {
msc_update_state = MSC_UPDATE_STARTING;
msc_update_bytes_written = 0;
msc_update_start_sector = 0;
}
msc_update_entry = update_entry;
} else if (msc_update_state == MSC_UPDATE_RUNNING) {
if (!msc_update_entry && msc_update_start_sector) {
msc_update_entry = msc_update_find_bin(msc_update_start_sector);
}
if (msc_update_entry && msc_update_bytes_written >= msc_update_entry->file_size) {
msc_update_end();
}
}
}
}
} else if (msc_ota_partition && lba >= msc_update_start_sector) {
//handle writes to the region where the new firmware will be uploaded
arduino_firmware_msc_event_data_t p;
if (msc_update_state <= MSC_UPDATE_STARTING && buffer[0] == 0xE9) {
msc_update_state = MSC_UPDATE_RUNNING;
msc_update_start_sector = lba;
msc_update_bytes_written = 0;
log_d("UPDATE_START: %u (0x%02X)", lba, lba - msc_boot->sectors_per_alloc_table);
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_START_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
if (msc_update_write(msc_ota_partition, ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, buffer, bufsize) == ESP_OK) {
log_v("UPDATE_WRITE: %u %u", ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, bufsize);
msc_update_bytes_written = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset + bufsize;
p.write.offset = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset;
p.write.size = bufsize;
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_WRITE_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
} else {
msc_update_error();
return 0;
}
} else if (msc_update_state == MSC_UPDATE_RUNNING) {
if (msc_update_entry && msc_update_entry->file_size && msc_update_bytes_written < msc_update_entry->file_size
&& (msc_update_bytes_written + bufsize) >= msc_update_entry->file_size) {
bufsize = msc_update_entry->file_size - msc_update_bytes_written;
}
if (msc_update_write(msc_ota_partition, ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, buffer, bufsize) == ESP_OK) {
log_v("UPDATE_WRITE: %u %u", ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, bufsize);
msc_update_bytes_written = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset + bufsize;
p.write.offset = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset;
p.write.size = bufsize;
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_WRITE_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
if (msc_update_entry && msc_update_entry->file_size && msc_update_bytes_written >= msc_update_entry->file_size) {
msc_update_end();
}
} else {
msc_update_error();
return 0;
}
}
}
return bufsize;
}
static int32_t msc_read(uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize) {
//log_d("lba: %u, offset: %u, bufsize: %u", lba, offset, bufsize);
if (lba < fw_start_sector) {
memcpy(buffer, msc_ram_disk + (lba * DISK_SECTOR_SIZE) + offset, bufsize);
} else if (msc_run_partition && lba < fw_end_sector) {
//read the currently running firmware
if (esp_partition_read(msc_run_partition, ((lba - fw_start_sector) * DISK_SECTOR_SIZE) + offset, buffer, bufsize) != ESP_OK) {
return 0;
}
} else {
memset(buffer, 0, bufsize);
}
return bufsize;
}
static bool msc_start_stop(uint8_t power_condition, bool start, bool load_eject) {
//log_d("power: %u, start: %u, eject: %u", power_condition, start, load_eject);
arduino_firmware_msc_event_data_t p;
p.power.power_condition = power_condition;
p.power.start = start;
p.power.load_eject = load_eject;
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_POWER_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
return true;
}
static volatile TaskHandle_t msc_task_handle = NULL;
static void msc_task(void *pvParameters) {
for (;;) {
if (msc_update_state == MSC_UPDATE_END) {
delay(100);
esp_restart();
}
delay(100);
}
msc_task_handle = NULL;
vTaskDelete(NULL);
}
FirmwareMSC::FirmwareMSC() : msc() {}
FirmwareMSC::~FirmwareMSC() {
end();
}
bool FirmwareMSC::begin() {
if (msc_ram_disk) {
return true;
}
if (!msc_update_setup_disk(USB_FW_MSC_VOLUME_NAME, USB_FW_MSC_SERIAL_NUMBER)) {
return false;
}
if (!msc_task_handle) {
xTaskCreateUniversal(msc_task, "msc_disk", 1024, NULL, 2, (TaskHandle_t *)&msc_task_handle, 0);
if (!msc_task_handle) {
msc_update_delete_disk();
return false;
}
}
msc.vendorID(USB_FW_MSC_VENDOR_ID);
msc.productID(USB_FW_MSC_PRODUCT_ID);
msc.productRevision(USB_FW_MSC_PRODUCT_REVISION);
msc.onStartStop(msc_start_stop);
msc.onRead(msc_read);
msc.onWrite(msc_write);
msc.mediaPresent(true);
msc.begin(msc_boot->fat12_sector_num, DISK_SECTOR_SIZE);
return true;
}
void FirmwareMSC::end() {
msc.end();
if (msc_task_handle) {
vTaskDelete(msc_task_handle);
msc_task_handle = NULL;
}
msc_update_delete_disk();
}
void FirmwareMSC::onEvent(esp_event_handler_t callback) {
onEvent(ARDUINO_FIRMWARE_MSC_ANY_EVENT, callback);
}
void FirmwareMSC::onEvent(arduino_firmware_msc_event_t event, esp_event_handler_t callback) {
arduino_usb_event_handler_register_with(ARDUINO_FIRMWARE_MSC_EVENTS, event, callback, this);
}
#if ARDUINO_USB_MSC_ON_BOOT
FirmwareMSC MSC_Update;
#endif
#endif /* CONFIG_USB_MSC_ENABLED */
+70
View File
@@ -0,0 +1,70 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <stdbool.h>
#include "USBMSC.h"
#if CONFIG_TINYUSB_MSC_ENABLED
#include "esp_event.h"
ESP_EVENT_DECLARE_BASE(ARDUINO_FIRMWARE_MSC_EVENTS);
typedef enum {
ARDUINO_FIRMWARE_MSC_ANY_EVENT = ESP_EVENT_ANY_ID,
ARDUINO_FIRMWARE_MSC_START_EVENT = 0,
ARDUINO_FIRMWARE_MSC_WRITE_EVENT,
ARDUINO_FIRMWARE_MSC_END_EVENT,
ARDUINO_FIRMWARE_MSC_ERROR_EVENT,
ARDUINO_FIRMWARE_MSC_POWER_EVENT,
ARDUINO_FIRMWARE_MSC_MAX_EVENT,
} arduino_firmware_msc_event_t;
typedef union {
struct {
size_t offset;
size_t size;
} write;
struct {
uint8_t power_condition;
bool start;
bool load_eject;
} power;
struct {
size_t size;
} end;
struct {
size_t size;
} error;
} arduino_firmware_msc_event_data_t;
class FirmwareMSC {
private:
USBMSC msc;
public:
FirmwareMSC();
~FirmwareMSC();
bool begin();
void end();
void onEvent(esp_event_handler_t callback);
void onEvent(arduino_firmware_msc_event_t event, esp_event_handler_t callback);
};
#if ARDUINO_USB_MSC_ON_BOOT
extern FirmwareMSC MSC_Update;
#endif
#endif /* CONFIG_TINYUSB_MSC_ENABLED */
+34
View File
@@ -0,0 +1,34 @@
/*
* FunctionalInterrupt.cpp
*
* Created on: 8 jul. 2018
* Author: Herman
*/
#include "FunctionalInterrupt.h"
#include "Arduino.h"
typedef void (*voidFuncPtr)(void);
typedef void (*voidFuncPtrArg)(void *);
extern "C" {
extern void __attachInterruptFunctionalArg(uint8_t pin, voidFuncPtrArg userFunc, void *arg, int intr_type, bool functional);
}
void ARDUINO_ISR_ATTR interruptFunctional(void *arg) {
InterruptArgStructure *localArg = (InterruptArgStructure *)arg;
if (localArg->interruptFunction) {
localArg->interruptFunction();
}
}
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);
}
extern "C" {
void cleanupFunctional(void *arg) {
delete (InterruptArgStructure *)arg;
}
}
+22
View File
@@ -0,0 +1,22 @@
/*
* FunctionalInterrupt.h
*
* Created on: 8 jul. 2018
* Author: Herman
*/
#ifndef CORE_CORE_FUNCTIONALINTERRUPT_H_
#define CORE_CORE_FUNCTIONALINTERRUPT_H_
#include <functional>
#include <stdint.h>
struct InterruptArgStructure {
std::function<void(void)> interruptFunction;
};
// The extra set of parentheses here prevents macros defined
// in io_pin_remap.h from applying to this declaration.
void(attachInterrupt)(uint8_t pin, std::function<void(void)> intRoutine, int mode);
#endif /* CORE_CORE_FUNCTIONALINTERRUPT_H_ */
+89
View File
@@ -0,0 +1,89 @@
/*
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
This file is part of the esp32 core for Arduino environment.
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 "HEXBuilder.h"
#include <ctype.h>
static uint8_t hex_char_to_byte(uint8_t c) {
return (c >= 'a' && c <= 'f') ? (c - ((uint8_t)'a' - 0xa))
: (c >= 'A' && c <= 'F') ? (c - ((uint8_t)'A' - 0xA))
: (c >= '0' && c <= '9') ? (c - (uint8_t)'0')
: 0x10; // unknown char is 16
}
bool HEXBuilder::isHexString(const char *str, size_t len) {
for (size_t i = 0; i < len; i++) {
if (isxdigit(str[i]) == 0) {
return false;
}
}
return true;
}
bool HEXBuilder::isHexString(String str) {
return isHexString(str.c_str(), str.length());
}
size_t HEXBuilder::hex2bytes(unsigned char *out, size_t maxlen, String &in) {
return hex2bytes(out, maxlen, in.c_str());
}
size_t HEXBuilder::hex2bytes(unsigned char *out, size_t maxlen, const char *in) {
size_t len = 0;
for (; *in; in++) {
uint8_t c = hex_char_to_byte(*in);
// Silently skip anything unknown.
if (c > 15) {
continue;
}
if (len & 1) {
if (len / 2 < maxlen) {
out[len / 2] |= c;
}
} else {
if (len / 2 < maxlen) {
out[len / 2] = c << 4;
}
}
len++;
}
return (len + 1) / 2;
}
size_t HEXBuilder::bytes2hex(char *out, size_t maxlen, const unsigned char *in, size_t len) {
for (size_t i = 0; i < len; i++) {
if (i * 2 + 1 < maxlen) {
sprintf(out + (i * 2), "%02x", in[i]);
}
}
return len * 2 + 1;
}
String HEXBuilder::bytes2hex(const unsigned char *in, size_t len) {
size_t maxlen = len * 2 + 1;
char *out = (char *)malloc(maxlen);
if (!out) {
return String();
}
bytes2hex(out, maxlen, in, len);
String ret = String(out);
free(out);
return ret;
}
+39
View File
@@ -0,0 +1,39 @@
/*
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
This file is part of the esp32 core for Arduino environment.
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
*/
#ifndef HEXBuilder_h
#define HEXBuilder_h
#include <WString.h>
#include <Stream.h>
// Basic hex/byte conversion class to be used by hash builders
class HEXBuilder {
public:
static size_t hex2bytes(unsigned char *out, size_t maxlen, String &in);
static size_t hex2bytes(unsigned char *out, size_t maxlen, const char *in);
static String bytes2hex(const unsigned char *in, size_t len);
static size_t bytes2hex(char *out, size_t maxlen, const unsigned char *in, size_t len);
static bool isHexString(const char *str, size_t len);
static bool isHexString(String str);
};
#endif
+622
View File
@@ -0,0 +1,622 @@
// Copyright 2015-2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "USB.h"
#if SOC_USB_SERIAL_JTAG_SUPPORTED
#include "Arduino.h" // defines ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE and ARDUINO_SERIAL_EVENT_TASK_PRIORITY
#include "esp32-hal.h"
#include "esp32-hal-periman.h"
#include "HWCDC.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "freertos/ringbuf.h"
#include "esp_intr_alloc.h"
#include "soc/periph_defs.h"
#include "soc/io_mux_reg.h"
#include "soc/usb_serial_jtag_struct.h"
#pragma GCC diagnostic ignored "-Wvolatile"
#include "hal/usb_serial_jtag_ll.h"
#pragma GCC diagnostic warning "-Wvolatile"
#include "rom/ets_sys.h"
ESP_EVENT_DEFINE_BASE(ARDUINO_HW_CDC_EVENTS);
static RingbufHandle_t tx_ring_buf = NULL;
static QueueHandle_t rx_queue = NULL;
static uint8_t rx_data_buf[64] = {0};
static intr_handle_t intr_handle = NULL;
static SemaphoreHandle_t tx_lock = NULL;
static volatile bool connected = false;
// SOF in ISR causes problems for uploading firmware
//static volatile unsigned long lastSOF_ms;
//static volatile uint8_t SOF_TIMEOUT;
// timeout has no effect when USB CDC is unplugged
static uint32_t tx_timeout_ms = 100;
static esp_event_loop_handle_t arduino_hw_cdc_event_loop_handle = NULL;
static esp_err_t
arduino_hw_cdc_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, BaseType_t *task_unblocked) {
if (arduino_hw_cdc_event_loop_handle == NULL) {
return ESP_FAIL;
}
return esp_event_isr_post_to(arduino_hw_cdc_event_loop_handle, event_base, event_id, event_data, event_data_size, task_unblocked);
}
static esp_err_t
arduino_hw_cdc_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg) {
if (!arduino_hw_cdc_event_loop_handle) {
esp_event_loop_args_t event_task_args = {
.queue_size = 5,
.task_name = "arduino_hw_cdc_events",
.task_priority = ARDUINO_SERIAL_EVENT_TASK_PRIORITY,
.task_stack_size = ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE,
.task_core_id = tskNO_AFFINITY
};
if (esp_event_loop_create(&event_task_args, &arduino_hw_cdc_event_loop_handle) != ESP_OK) {
log_e("esp_event_loop_create failed");
}
}
if (arduino_hw_cdc_event_loop_handle == NULL) {
return ESP_FAIL;
}
return esp_event_handler_register_with(arduino_hw_cdc_event_loop_handle, event_base, event_id, event_handler, event_handler_arg);
}
static void hw_cdc_isr_handler(void *arg) {
portBASE_TYPE xTaskWoken = 0;
uint32_t usbjtag_intr_status = 0;
arduino_hw_cdc_event_data_t event = {0};
usbjtag_intr_status = usb_serial_jtag_ll_get_intsts_mask();
if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY) {
// Interrupt tells us the host picked up the data we sent.
if (!HWCDC::isPlugged()) {
connected = false;
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
// USB is unplugged, nothing to be done here
return;
} else {
connected = true;
}
if (tx_ring_buf != NULL && usb_serial_jtag_ll_txfifo_writable() == 1) {
// We disable the interrupt here so that the interrupt won't be triggered if there is no data to send.
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
size_t queued_size = 0;
uint8_t *queued_buff = (uint8_t *)xRingbufferReceiveUpToFromISR(tx_ring_buf, &queued_size, 64);
// If the hardware fifo is available, write in it. Otherwise, do nothing.
if (queued_buff != NULL) { //Although tx_queued_bytes may be larger than 0. We may have interrupt before xRingbufferSend() was called.
//Copy the queued buffer into the TX FIFO
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
usb_serial_jtag_ll_write_txfifo(queued_buff, queued_size);
usb_serial_jtag_ll_txfifo_flush();
vRingbufferReturnItemFromISR(tx_ring_buf, queued_buff, &xTaskWoken);
if (connected) {
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
}
//send event?
//ets_printf("TX:%u\n", queued_size);
event.tx.len = queued_size;
arduino_hw_cdc_event_post(ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_TX_EVENT, &event, sizeof(arduino_hw_cdc_event_data_t), &xTaskWoken);
}
} else {
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
}
}
if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT) {
// read rx buffer(max length is 64), and send available data to ringbuffer.
// Ensure the rx buffer size is larger than RX_MAX_SIZE.
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT);
uint32_t rx_fifo_len = usb_serial_jtag_ll_read_rxfifo(rx_data_buf, 64);
uint32_t i = 0;
for (i = 0; i < rx_fifo_len; i++) {
if (rx_queue == NULL || !xQueueSendFromISR(rx_queue, rx_data_buf + i, &xTaskWoken)) {
break;
}
}
event.rx.len = i;
arduino_hw_cdc_event_post(ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_RX_EVENT, &event, sizeof(arduino_hw_cdc_event_data_t), &xTaskWoken);
connected = true;
}
if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_BUS_RESET) {
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_BUS_RESET);
arduino_hw_cdc_event_post(ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_BUS_RESET_EVENT, &event, sizeof(arduino_hw_cdc_event_data_t), &xTaskWoken);
connected = false;
}
// SOF ISR is causing esptool to be unable to upload firmware to the board
// if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_SOF) {
// usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SOF);
// lastSOF_ms = millis();
// }
if (xTaskWoken == pdTRUE) {
portYIELD_FROM_ISR();
}
}
// Moved to header file as inline function. Kept just as future reference.
//inline bool HWCDC::isPlugged(void) {
// SOF ISR is causing esptool to be unable to upload firmware to the board
// Timer test for SOF seems to work when uploading firmware
// return usb_serial_jtag_is_connected();//(lastSOF_ms + SOF_TIMEOUT) >= millis();
//}
bool HWCDC::isCDC_Connected() {
static bool running = false;
// USB may be unplugged
if (!isPlugged()) {
connected = false;
running = false;
// SOF in ISR causes problems for uploading firmware
//SOF_TIMEOUT = 5; // SOF timeout when unplugged
return false;
}
//else {
// SOF_TIMEOUT = 50; // SOF timeout when plugged
//}
if (connected) {
running = false;
return true;
}
if (running == false && !connected) { // enables it only once!
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
}
// this will feed CDC TX FIFO to trigger IN_EMPTY
usb_serial_jtag_ll_txfifo_flush();
running = true;
return false;
}
static void flushTXBuffer(const uint8_t *buffer, size_t size) {
if (!tx_ring_buf) {
return;
}
UBaseType_t uxItemsWaiting = 0;
vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting);
size_t freeSpace = xRingbufferGetCurFreeSize(tx_ring_buf);
size_t ringbufferLength = freeSpace + uxItemsWaiting;
if (buffer == NULL) {
// just flush the whole ring buffer and exit - used by HWCDC::flush()
size_t queued_size = 0;
uint8_t *queued_buff = (uint8_t *)xRingbufferReceiveUpTo(tx_ring_buf, &queued_size, 0, ringbufferLength);
if (queued_size && queued_buff != NULL) {
vRingbufferReturnItem(tx_ring_buf, (void *)queued_buff);
}
return;
}
if (size == 0) {
return; // nothing to do
}
if (freeSpace >= size) {
// there is enough space, just add the data to the ring buffer
if (xRingbufferSend(tx_ring_buf, (void *)buffer, size, 0) != pdTRUE) {
return;
}
} else {
// how many byte should be flushed to make space for the new data
size_t to_flush = size - freeSpace;
if (to_flush > ringbufferLength) {
to_flush = ringbufferLength;
}
size_t queued_size = 0;
uint8_t *queued_buff = (uint8_t *)xRingbufferReceiveUpTo(tx_ring_buf, &queued_size, 0, to_flush);
if (queued_size && queued_buff != NULL) {
vRingbufferReturnItem(tx_ring_buf, (void *)queued_buff);
}
// now add the new data that fits into the ring buffer
uint8_t *bptr = (uint8_t *)buffer;
if (size >= ringbufferLength) {
size = ringbufferLength;
bptr = (uint8_t *)buffer + (size - ringbufferLength);
}
if (xRingbufferSend(tx_ring_buf, (void *)bptr, size, 0) != pdTRUE) {
return;
}
}
// flushes CDC FIFO
usb_serial_jtag_ll_txfifo_flush();
}
static void ARDUINO_ISR_ATTR cdc0_write_char(char c) {
if (tx_ring_buf == NULL) {
return;
}
if (!HWCDC::isConnected()) {
// just pop/push RingBuffer and apply FIFO policy
flushTXBuffer((const uint8_t *)&c, 1);
return;
}
if (xPortInIsrContext()) {
xRingbufferSendFromISR(tx_ring_buf, (void *)(&c), 1, NULL);
} else {
xRingbufferSend(tx_ring_buf, (void *)(&c), 1, tx_timeout_ms / portTICK_PERIOD_MS);
}
usb_serial_jtag_ll_txfifo_flush();
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
}
HWCDC::HWCDC() {
// SOF in ISR causes problems for uploading firmware
// lastSOF_ms = 0;
// SOF_TIMEOUT = 5;
}
HWCDC::~HWCDC() {
end();
}
// It should return <true> just when USB is plugged and CDC is connected.
HWCDC::operator bool() const {
return HWCDC::isCDC_Connected();
}
void HWCDC::onEvent(esp_event_handler_t callback) {
onEvent(ARDUINO_HW_CDC_ANY_EVENT, callback);
}
void HWCDC::onEvent(arduino_hw_cdc_event_t event, esp_event_handler_t callback) {
arduino_hw_cdc_event_handler_register_with(ARDUINO_HW_CDC_EVENTS, event, callback, this);
}
bool HWCDC::deinit(void *busptr) {
// avoid any recursion issue with Peripheral Manager perimanSetPinBus() call
static bool running = false;
if (running) {
return true;
}
running = true;
// Setting USB D+ D- pins
bool retCode = true;
retCode &= perimanClearPinBus(USB_INT_PHY0_DM_GPIO_NUM);
retCode &= perimanClearPinBus(USB_INT_PHY0_DP_GPIO_NUM);
if (retCode) {
// Force the host to re-enumerate (BUS_RESET)
pinMode(USB_INT_PHY0_DM_GPIO_NUM, OUTPUT_OPEN_DRAIN);
pinMode(USB_INT_PHY0_DP_GPIO_NUM, OUTPUT_OPEN_DRAIN);
digitalWrite(USB_INT_PHY0_DM_GPIO_NUM, LOW);
digitalWrite(USB_INT_PHY0_DP_GPIO_NUM, LOW);
}
// release the flag
running = false;
return retCode;
}
void HWCDC::begin(unsigned long baud) {
if (tx_lock == NULL) {
tx_lock = xSemaphoreCreateMutex();
}
//RX Buffer default has 256 bytes if not preset
if (rx_queue == NULL) {
if (!setRxBufferSize(256)) {
log_e("HW CDC RX Buffer error");
}
}
//TX Buffer default has 256 bytes if not preset
if (tx_ring_buf == NULL) {
if (!setTxBufferSize(256)) {
log_e("HW CDC TX Buffer error");
}
}
// the HW Serial pins needs to be first deinited in order to allow `if(Serial)` to work :-(
// But this is also causing terminal to hang, so they are disabled
// deinit(NULL);
// delay(10); // USB Host has to enumerate it again
// Peripheral Manager setting for USB D+ D- pins
uint8_t pin = USB_INT_PHY0_DM_GPIO_NUM;
if (perimanGetBusDeinit(ESP32_BUS_TYPE_USB_DM) == NULL) {
perimanSetBusDeinit(ESP32_BUS_TYPE_USB_DM, HWCDC::deinit);
}
if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_USB_DM, (void *)this, -1, -1)) {
goto err;
}
pin = USB_INT_PHY0_DP_GPIO_NUM;
if (perimanGetBusDeinit(ESP32_BUS_TYPE_USB_DP) == NULL) {
perimanSetBusDeinit(ESP32_BUS_TYPE_USB_DP, HWCDC::deinit);
}
if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_USB_DP, (void *)this, -1, -1)) {
goto err;
}
// Configure PHY
// USB_Serial_JTAG use internal PHY
USB_SERIAL_JTAG.conf0.phy_sel = 0;
// Disable software control USB D+ D- pullup pulldown (Device FS: dp_pullup = 1)
USB_SERIAL_JTAG.conf0.pad_pull_override = 0;
// Enable USB D+ pullup
USB_SERIAL_JTAG.conf0.dp_pullup = 1;
// Enable USB pad function
USB_SERIAL_JTAG.conf0.usb_pad_enable = 1;
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_LL_INTR_MASK);
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY | USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT | USB_SERIAL_JTAG_INTR_BUS_RESET);
// SOF ISR is causing esptool to be unable to upload firmware to the board
// usb_serial_jtag_ll_ena_intr_mask(
// USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY | USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT | USB_SERIAL_JTAG_INTR_BUS_RESET | USB_SERIAL_JTAG_INTR_SOF
// );
if (!intr_handle && esp_intr_alloc(ETS_USB_SERIAL_JTAG_INTR_SOURCE, 0, hw_cdc_isr_handler, NULL, &intr_handle) != ESP_OK) {
isr_log_e("HW USB CDC failed to init interrupts");
end();
return;
}
return;
err:
log_e("Serial JTAG Pin %u can't be set into Peripheral Manager.", pin);
end();
}
void HWCDC::end() {
//Disable/clear/free tx/rx interrupt.
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_LL_INTR_MASK);
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_LL_INTR_MASK);
esp_intr_free(intr_handle);
intr_handle = NULL;
if (tx_lock != NULL) {
vSemaphoreDelete(tx_lock);
tx_lock = NULL;
}
setRxBufferSize(0);
setTxBufferSize(0);
if (arduino_hw_cdc_event_loop_handle) {
esp_event_loop_delete(arduino_hw_cdc_event_loop_handle);
arduino_hw_cdc_event_loop_handle = NULL;
}
HWCDC::deinit(this);
setDebugOutput(false);
connected = false;
}
void HWCDC::setTxTimeoutMs(uint32_t timeout) {
tx_timeout_ms = timeout;
}
/*
* WRITING
*/
size_t HWCDC::setTxBufferSize(size_t tx_queue_len) {
if (tx_ring_buf) {
vRingbufferDelete(tx_ring_buf);
tx_ring_buf = NULL;
}
if (!tx_queue_len) {
return 0;
}
tx_ring_buf = xRingbufferCreate(tx_queue_len, RINGBUF_TYPE_BYTEBUF);
if (!tx_ring_buf) {
return 0;
}
return tx_queue_len;
}
int HWCDC::availableForWrite(void) {
if (tx_ring_buf == NULL || tx_lock == NULL) {
return 0;
}
if (xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS) {
return 0;
}
size_t a = xRingbufferGetCurFreeSize(tx_ring_buf);
xSemaphoreGive(tx_lock);
return a;
}
size_t HWCDC::write(const uint8_t *buffer, size_t size) {
if (buffer == NULL || size == 0 || tx_ring_buf == NULL || tx_lock == NULL) {
return 0;
}
if (xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS) {
return 0;
}
if (!isCDC_Connected()) {
// just pop/push RingBuffer and apply FIFO policy
flushTXBuffer(buffer, size);
} else {
size_t space = xRingbufferGetCurFreeSize(tx_ring_buf);
size_t to_send = size, so_far = 0;
if (space > size) {
space = size;
}
// Non-Blocking method, Sending data to ringbuffer, and handle the data in ISR.
if (space > 0 && xRingbufferSend(tx_ring_buf, (void *)(buffer), space, 0) != pdTRUE) {
size = 0;
} else {
to_send -= space;
so_far += space;
// Now trigger the ISR to read data from the ring buffer.
usb_serial_jtag_ll_txfifo_flush();
if (connected) {
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
}
// tracks CDC transmission progress to avoid hanging if CDC is unplugged while still sending data
size_t last_toSend = to_send;
uint32_t tries = tx_timeout_ms; // waits 1ms per sending data attempt, in case CDC is unplugged
while (connected && to_send) {
space = xRingbufferGetCurFreeSize(tx_ring_buf);
if (space > to_send) {
space = to_send;
}
// Blocking method, Sending data to ringbuffer, and handle the data in ISR.
if (xRingbufferSend(tx_ring_buf, (void *)(buffer + so_far), space, tx_timeout_ms / portTICK_PERIOD_MS) != pdTRUE) {
size = so_far;
log_w("write failed due to ring buffer full - timeout");
break;
}
so_far += space;
to_send -= space;
// Now trigger the ISR to read data from the ring buffer.
usb_serial_jtag_ll_txfifo_flush();
if (connected) {
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
}
if (last_toSend == to_send) {
// no progress in sending data... USB CDC is probably unplugged
tries--;
delay(1);
} else {
last_toSend = to_send;
tries = tx_timeout_ms; // reset the timeout
}
if (tries == 0) { // CDC isn't connected anymore...
size = so_far;
log_w("write failed due to waiting USB Host - timeout");
connected = false;
}
}
}
// CDC was disconnected while sending data ==> flush the TX buffer keeping the last data
if (to_send && !usb_serial_jtag_ll_txfifo_writable()) {
connected = false;
flushTXBuffer(buffer + so_far, to_send);
}
}
xSemaphoreGive(tx_lock);
return size;
}
size_t HWCDC::write(uint8_t c) {
return write(&c, 1);
}
void HWCDC::flush(void) {
if (tx_ring_buf == NULL || tx_lock == NULL) {
return;
}
if (xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS) {
return;
}
if (!isCDC_Connected()) {
flushTXBuffer(NULL, 0);
} else {
UBaseType_t uxItemsWaiting = 0;
vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting);
if (uxItemsWaiting) {
// Now trigger the ISR to read data from the ring buffer.
usb_serial_jtag_ll_txfifo_flush();
if (connected) {
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
}
}
uint32_t tries = tx_timeout_ms; // waits 1ms per ISR sending data attempt, in case CDC is unplugged
while (connected && tries && uxItemsWaiting) {
delay(1);
UBaseType_t lastUxItemsWaiting = uxItemsWaiting;
vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting);
if (lastUxItemsWaiting == uxItemsWaiting) {
tries--;
}
if (connected) {
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
}
}
if (tries == 0) { // CDC isn't connected anymore...
connected = false;
flushTXBuffer(NULL, 0); // flushes all TX Buffer
}
}
xSemaphoreGive(tx_lock);
}
/*
* READING
*/
size_t HWCDC::setRxBufferSize(size_t rx_queue_len) {
if (rx_queue) {
vQueueDelete(rx_queue);
rx_queue = NULL;
}
if (!rx_queue_len) {
return 0;
}
rx_queue = xQueueCreate(rx_queue_len, sizeof(uint8_t));
if (!rx_queue) {
return 0;
}
return rx_queue_len;
}
int HWCDC::available(void) {
if (rx_queue == NULL) {
return -1;
}
return uxQueueMessagesWaiting(rx_queue);
}
int HWCDC::peek(void) {
if (rx_queue == NULL) {
return -1;
}
uint8_t c;
if (xQueuePeek(rx_queue, &c, 0)) {
return c;
}
return -1;
}
int HWCDC::read(void) {
if (rx_queue == NULL) {
return -1;
}
uint8_t c = 0;
if (xQueueReceive(rx_queue, &c, 0)) {
return c;
}
return -1;
}
size_t HWCDC::read(uint8_t *buffer, size_t size) {
if (rx_queue == NULL) {
return -1;
}
uint8_t c = 0;
size_t count = 0;
while (count < size && xQueueReceive(rx_queue, &c, 0)) {
buffer[count++] = c;
}
return count;
}
/*
* DEBUG
*/
void HWCDC::setDebugOutput(bool en) {
if (en) {
uartSetDebug(NULL);
ets_install_putc2((void (*)(char)) & cdc0_write_char);
} else {
ets_install_putc2(NULL);
}
ets_install_putc1(NULL); // closes UART log output
}
#if ARDUINO_USB_MODE && ARDUINO_USB_CDC_ON_BOOT // Hardware JTAG CDC selected
// USBSerial is always available to be used
HWCDC HWCDCSerial;
#endif
#endif /* SOC_USB_SERIAL_JTAG_SUPPORTED */
+118
View File
@@ -0,0 +1,118 @@
// Copyright 2015-2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "sdkconfig.h"
#include "soc/soc_caps.h"
#if SOC_USB_SERIAL_JTAG_SUPPORTED
#include <inttypes.h>
#include "esp_event.h"
#include "Stream.h"
#include "driver/usb_serial_jtag.h"
ESP_EVENT_DECLARE_BASE(ARDUINO_HW_CDC_EVENTS);
typedef enum {
ARDUINO_HW_CDC_ANY_EVENT = ESP_EVENT_ANY_ID,
ARDUINO_HW_CDC_CONNECTED_EVENT = 0,
ARDUINO_HW_CDC_BUS_RESET_EVENT,
ARDUINO_HW_CDC_RX_EVENT,
ARDUINO_HW_CDC_TX_EVENT,
ARDUINO_HW_CDC_MAX_EVENT,
} arduino_hw_cdc_event_t;
typedef union {
struct {
size_t len;
} rx;
struct {
size_t len;
} tx;
} arduino_hw_cdc_event_data_t;
class HWCDC : public Stream {
private:
static bool deinit(void *busptr);
static bool isCDC_Connected();
public:
HWCDC();
~HWCDC();
void onEvent(esp_event_handler_t callback);
void onEvent(arduino_hw_cdc_event_t event, esp_event_handler_t callback);
size_t setRxBufferSize(size_t);
size_t setTxBufferSize(size_t);
void setTxTimeoutMs(uint32_t timeout);
void begin(unsigned long baud = 0);
void end();
int available(void);
int availableForWrite(void);
int peek(void);
int read(void);
size_t read(uint8_t *buffer, size_t size);
size_t write(uint8_t);
size_t write(const uint8_t *buffer, size_t size);
void flush(void);
inline static bool isPlugged(void) {
// SOF ISR is causing esptool to be unable to upload firmware to the board
// Using IDF 5.1 helper function because it is based on Timer check instead of ISR
return usb_serial_jtag_is_connected();
}
inline static bool isConnected(void) {
return isCDC_Connected();
}
inline size_t read(char *buffer, size_t size) {
return read((uint8_t *)buffer, size);
}
inline size_t write(const char *buffer, size_t size) {
return write((uint8_t *)buffer, size);
}
inline size_t write(const char *s) {
return write((uint8_t *)s, strlen(s));
}
inline size_t write(unsigned long n) {
return write((uint8_t)n);
}
inline size_t write(long n) {
return write((uint8_t)n);
}
inline size_t write(unsigned int n) {
return write((uint8_t)n);
}
inline size_t write(int n) {
return write((uint8_t)n);
}
operator bool() const;
void setDebugOutput(bool);
uint32_t baudRate() {
return 115200;
}
};
#if ARDUINO_USB_MODE && ARDUINO_USB_CDC_ON_BOOT // Hardware JTAG CDC selected
#ifndef HWCDC_SERIAL_IS_DEFINED
#define HWCDC_SERIAL_IS_DEFINED 1
#endif
// HWCDCSerial is always available to be used
extern HWCDC HWCDCSerial;
#endif
#endif /* SOC_USB_SERIAL_JTAG_SUPPORTED */
+43
View File
@@ -0,0 +1,43 @@
/*
Copyright (c) 2016 Arduino LLC. All right 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 <inttypes.h>
#include "Stream.h"
#include <functional>
class HardwareI2C : public Stream {
public:
virtual bool begin() = 0;
virtual bool begin(uint8_t address) = 0;
virtual bool end() = 0;
virtual bool setClock(uint32_t freq) = 0;
virtual void beginTransmission(uint8_t address) = 0;
virtual uint8_t endTransmission(bool stopBit) = 0;
virtual uint8_t endTransmission(void) = 0;
virtual size_t requestFrom(uint8_t address, size_t len, bool stopBit) = 0;
virtual size_t requestFrom(uint8_t address, size_t len) = 0;
// Update base class to use std::function
virtual void onReceive(const std::function<void(int)> &) = 0;
virtual void onRequest(const std::function<void()> &) = 0;
};
+696
View File
@@ -0,0 +1,696 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <ctime>
#include "pins_arduino.h"
#include "io_pin_remap.h"
#include "HardwareSerial.h"
#include "soc/soc_caps.h"
#include "driver/uart.h"
#include "freertos/queue.h"
#if (SOC_UART_LP_NUM >= 1)
#define UART_HW_FIFO_LEN(uart_num) ((uart_num < SOC_UART_HP_NUM) ? SOC_UART_FIFO_LEN : SOC_LP_UART_FIFO_LEN)
#else
#define UART_HW_FIFO_LEN(uart_num) SOC_UART_FIFO_LEN
#endif
void serialEvent(void) __attribute__((weak));
#if SOC_UART_NUM > 1
void serialEvent1(void) __attribute__((weak));
#endif /* SOC_UART_NUM > 1 */
#if SOC_UART_NUM > 2
void serialEvent2(void) __attribute__((weak));
#endif /* SOC_UART_NUM > 2 */
#if SOC_UART_NUM > 3
void serialEvent3(void) __attribute__((weak));
#endif /* SOC_UART_NUM > 3 */
#if SOC_UART_NUM > 4
void serialEvent4(void) __attribute__((weak));
#endif /* SOC_UART_NUM > 4 */
#if SOC_UART_NUM > 5
void serialEvent5(void) __attribute__((weak));
#endif /* SOC_UART_NUM > 5 */
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL)
// There is always Seria0 for UART0
HardwareSerial Serial0(0);
#if SOC_UART_NUM > 1
HardwareSerial Serial1(1);
#endif
#if SOC_UART_NUM > 2
HardwareSerial Serial2(2);
#endif
#if SOC_UART_NUM > 3
HardwareSerial Serial3(3);
#endif
#if SOC_UART_NUM > 4
HardwareSerial Serial4(4);
#endif
#if (SOC_UART_NUM > 5)
HardwareSerial Serial5(5);
#endif
#if HWCDC_SERIAL_IS_DEFINED == 1 // Hardware JTAG CDC Event
extern void HWCDCSerialEvent(void) __attribute__((weak));
#endif
// C-callable helper used by HAL when pins are detached and the high-level
// HardwareSerial instance must be finalized.
extern "C" void hal_uart_notify_pins_detached(int uart_num) {
log_d("hal_uart_notify_pins_detached: Notifying HardwareSerial for UART%d", uart_num);
switch (uart_num) {
case 0: Serial0.end(); break;
#if SOC_UART_NUM > 1
case 1: Serial1.end(); break;
#endif
#if SOC_UART_NUM > 2
case 2: Serial2.end(); break;
#endif
#if SOC_UART_NUM > 3
case 3: Serial3.end(); break;
#endif
#if SOC_UART_NUM > 4
case 4: Serial4.end(); break;
#endif
#if SOC_UART_NUM > 5
case 5: Serial5.end(); break;
#endif
default: log_e("hal_uart_notify_pins_detached: UART%d not handled!", uart_num); break;
}
}
#if USB_SERIAL_IS_DEFINED == 1 // Native USB CDC Event
// Used by Hardware Serial for USB CDC events
extern void USBSerialEvent(void) __attribute__((weak));
#endif
void serialEventRun(void) {
#if HWCDC_SERIAL_IS_DEFINED == 1 // Hardware JTAG CDC Event
if (HWCDCSerialEvent && HWCDCSerial.available()) {
HWCDCSerialEvent();
}
#endif
#if USB_SERIAL_IS_DEFINED == 1 // Native USB CDC Event
if (USBSerialEvent && USBSerial.available()) {
USBSerialEvent();
}
#endif
// UART0 is default serialEvent()
if (serialEvent && Serial0.available()) {
serialEvent();
}
#if SOC_UART_NUM > 1
if (serialEvent1 && Serial1.available()) {
serialEvent1();
}
#endif
#if SOC_UART_NUM > 2
if (serialEvent2 && Serial2.available()) {
serialEvent2();
}
#endif
#if SOC_UART_NUM > 3
if (serialEvent3 && Serial3.available()) {
serialEvent3();
}
#endif
#if SOC_UART_NUM > 4
if (serialEvent4 && Serial4.available()) {
serialEvent4();
}
#endif
#if SOC_UART_NUM > 5
if (serialEvent5 && Serial5.available()) {
serialEvent5();
}
#endif
}
#endif
#if !CONFIG_DISABLE_HAL_LOCKS
#define HSERIAL_MUTEX_LOCK() \
do { \
} while (xSemaphoreTake(_lock, portMAX_DELAY) != pdPASS)
#define HSERIAL_MUTEX_UNLOCK() xSemaphoreGive(_lock)
#else
#define HSERIAL_MUTEX_LOCK()
#define HSERIAL_MUTEX_UNLOCK()
#endif
HardwareSerial::HardwareSerial(uint8_t uart_nr)
: _uart_nr(uart_nr), _uart(NULL), _rxBufferSize(256), _txBufferSize(0), _onReceiveCB(NULL), _onReceiveErrorCB(NULL), _onReceiveTimeout(false), _rxTimeout(1),
_rxFIFOFull(0), _eventTask(NULL)
#if !CONFIG_DISABLE_HAL_LOCKS
,
_lock(NULL)
#endif
{
#if !CONFIG_DISABLE_HAL_LOCKS
if (_lock == NULL) {
_lock = xSemaphoreCreateMutex();
if (_lock == NULL) {
log_e("xSemaphoreCreateMutex failed");
return;
}
}
#endif
}
HardwareSerial::~HardwareSerial() {
end(); // explicit Full UART termination
#if !CONFIG_DISABLE_HAL_LOCKS
if (_lock != NULL) {
vSemaphoreDelete(_lock);
}
#endif
}
void HardwareSerial::_createEventTask(void *args) {
// Creating UART event Task
xTaskCreateUniversal(
_uartEventTask, "uart_event_task", ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE, this, ARDUINO_SERIAL_EVENT_TASK_PRIORITY, &_eventTask,
ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE
);
if (_eventTask == NULL) {
log_e(" -- UART%d Event Task not Created!", _uart_nr);
}
}
void HardwareSerial::_destroyEventTask(void) {
if (_eventTask != NULL) {
vTaskDelete(_eventTask);
_eventTask = NULL;
}
}
void HardwareSerial::onReceiveError(OnReceiveErrorCb function) {
HSERIAL_MUTEX_LOCK();
// function may be NULL to cancel onReceive() from its respective task
_onReceiveErrorCB = function;
// this can be called after Serial.begin(), therefore it shall create the event task
if (function != NULL && _uart != NULL && _eventTask == NULL) {
_createEventTask(this);
}
HSERIAL_MUTEX_UNLOCK();
}
void HardwareSerial::onReceive(OnReceiveCb function, bool onlyOnTimeout) {
HSERIAL_MUTEX_LOCK();
// function may be NULL to cancel onReceive() from its respective task
_onReceiveCB = function;
// setting the callback to NULL will just disable it
if (_onReceiveCB != NULL) {
// When Rx timeout is Zero (disabled), there is only one possible option that is callback when FIFO reaches 120 bytes
_onReceiveTimeout = _rxTimeout > 0 ? onlyOnTimeout : false;
// in case that onReceive() shall work only with RX Timeout, FIFO shall be high
// this is a work around for an IDF issue with events and low FIFO Full value (< 3)
// Not valid for the LP UART
if (_onReceiveTimeout && _uart_nr < SOC_UART_HP_NUM) {
uartSetRxFIFOFull(_uart, 120);
log_w("OnReceive is set to Timeout only, thus FIFO Full is now 120 bytes.");
}
// this method can be called after Serial.begin(), therefore it shall create the event task
if (_uart != NULL && _eventTask == NULL) {
_createEventTask(this); // Create event task
}
}
HSERIAL_MUTEX_UNLOCK();
}
// This function allow the user to define how many bytes will trigger an Interrupt that will copy RX FIFO to the internal RX Ringbuffer
// ISR will also move data from FIFO to RX Ringbuffer after a RX Timeout defined in HardwareSerial::setRxTimeout(uint8_t symbols_timeout)
// A low value of FIFO Full bytes will consume more CPU time within the ISR
// A high value of FIFO Full bytes will make the application wait longer to have byte available for the Stkech in a streaming scenario
// Both RX FIFO Full and RX Timeout may affect when onReceive() will be called
bool HardwareSerial::setRxFIFOFull(uint8_t fifoBytes) {
HSERIAL_MUTEX_LOCK();
// in case that onReceive() shall work only with RX Timeout, FIFO shall be high
// this is a work around for an IDF issue with events and low FIFO Full value (< 3)
// Not valid for the LP UART
if (_onReceiveCB != NULL && _onReceiveTimeout && _uart_nr < SOC_UART_HP_NUM) {
fifoBytes = 120;
log_w("OnReceive is set to Timeout only, thus FIFO Full is now 120 bytes.");
}
bool retCode = uartSetRxFIFOFull(_uart, fifoBytes); // Set new timeout
if (fifoBytes > 0 && fifoBytes < UART_HW_FIFO_LEN(_uart_nr) - 1) {
_rxFIFOFull = fifoBytes;
}
HSERIAL_MUTEX_UNLOCK();
return retCode;
}
// timeout is calculates in time to receive UART symbols at the UART baudrate.
// the estimation is about 11 bits per symbol (SERIAL_8N1)
bool HardwareSerial::setRxTimeout(uint8_t symbols_timeout) {
HSERIAL_MUTEX_LOCK();
// Zero disables timeout, thus, onReceive callback will only be called when RX FIFO reaches 120 bytes
// Any non-zero value will activate onReceive callback based on UART baudrate with about 11 bits per symbol
_rxTimeout = symbols_timeout;
if (!symbols_timeout) {
_onReceiveTimeout = false; // only when RX timeout is disabled, we also must disable this flag
}
bool retCode = uartSetRxTimeout(_uart, _rxTimeout); // Set new timeout
HSERIAL_MUTEX_UNLOCK();
return retCode;
}
void HardwareSerial::eventQueueReset() {
QueueHandle_t uartEventQueue = NULL;
if (_uart == NULL) {
return;
}
uartGetEventQueue(_uart, &uartEventQueue);
if (uartEventQueue != NULL) {
xQueueReset(uartEventQueue);
}
}
void HardwareSerial::_uartEventTask(void *args) {
HardwareSerial *uart = (HardwareSerial *)args;
uart_event_t event;
QueueHandle_t uartEventQueue = NULL;
uartGetEventQueue(uart->_uart, &uartEventQueue);
if (uartEventQueue != NULL) {
for (;;) {
//Waiting for UART event.
if (xQueueReceive(uartEventQueue, (void *)&event, (TickType_t)portMAX_DELAY)) {
hardwareSerial_error_t currentErr = UART_NO_ERROR;
switch (event.type) {
case UART_DATA:
if (uart->_onReceiveCB && uart->available() > 0 && ((uart->_onReceiveTimeout && event.timeout_flag) || !uart->_onReceiveTimeout)) {
uart->_onReceiveCB();
}
break;
case UART_FIFO_OVF:
log_w("UART%d FIFO Overflow. Consider adding Hardware Flow Control to your Application.", uart->_uart_nr);
currentErr = UART_FIFO_OVF_ERROR;
break;
case UART_BUFFER_FULL:
log_w("UART%d Buffer Full. Consider increasing your buffer size of your Application.", uart->_uart_nr);
currentErr = UART_BUFFER_FULL_ERROR;
break;
case UART_BREAK:
log_v("UART%d RX break.", uart->_uart_nr);
currentErr = UART_BREAK_ERROR;
break;
case UART_PARITY_ERR:
log_v("UART%d parity error.", uart->_uart_nr);
currentErr = UART_PARITY_ERROR;
break;
case UART_FRAME_ERR:
log_v("UART%d frame error.", uart->_uart_nr);
currentErr = UART_FRAME_ERROR;
break;
default: log_v("UART%d unknown event type %d.", uart->_uart_nr, event.type); break;
}
if (currentErr != UART_NO_ERROR) {
if (uart->_onReceiveErrorCB) {
uart->_onReceiveErrorCB(currentErr);
}
}
}
}
}
vTaskDelete(NULL);
}
void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert, unsigned long timeout_ms, uint8_t rxfifo_full_thrhd) {
if (_uart_nr >= SOC_UART_NUM) {
log_e("Serial number is invalid, please use a number from 0 to %u", SOC_UART_NUM - 1);
return;
}
#if !CONFIG_DISABLE_HAL_LOCKS
if (_lock == NULL) {
log_e("MUTEX Lock failed. Can't begin.");
return;
}
#endif
// map logical pins to GPIO numbers
rxPin = digitalPinToGPIONumber(rxPin);
txPin = digitalPinToGPIONumber(txPin);
int8_t _rxPin = uart_get_RxPin(_uart_nr);
int8_t _txPin = uart_get_TxPin(_uart_nr);
rxPin = rxPin < 0 ? _rxPin : rxPin;
txPin = txPin < 0 ? _txPin : txPin;
HSERIAL_MUTEX_LOCK();
// First Time or after end() --> set default Pins
if (!uartIsDriverInstalled(_uart)) {
// get previously used RX/TX pins, if any.
int8_t _rxPin = uart_get_RxPin(_uart_nr);
int8_t _txPin = uart_get_TxPin(_uart_nr);
switch (_uart_nr) {
case UART_NUM_0:
if (rxPin < 0 && txPin < 0) {
// do not change RX0/TX0 if it has already been set before
rxPin = _rxPin < 0 ? (int8_t)SOC_RX0 : _rxPin;
txPin = _txPin < 0 ? (int8_t)SOC_TX0 : _txPin;
}
break;
#if SOC_UART_HP_NUM > 1
case UART_NUM_1:
if (rxPin < 0 && txPin < 0) {
// do not change RX1/TX1 if it has already been set before
rxPin = _rxPin < 0 ? (int8_t)RX1 : _rxPin;
txPin = _txPin < 0 ? (int8_t)TX1 : _txPin;
}
break;
#endif // UART_NUM_1
#if SOC_UART_HP_NUM > 2
case UART_NUM_2:
if (rxPin < 0 && txPin < 0) {
// do not change RX2/TX2 if it has already been set before
#ifdef RX2
rxPin = _rxPin < 0 ? (int8_t)RX2 : _rxPin;
#endif
#ifdef TX2
txPin = _txPin < 0 ? (int8_t)TX2 : _txPin;
#endif
}
break;
#endif // UART_NUM_2
#if SOC_UART_HP_NUM > 3
case UART_NUM_3:
if (rxPin < 0 && txPin < 0) {
// do not change RX3/TX3 if it has already been set before
#ifdef RX3
rxPin = _rxPin < 0 ? (int8_t)RX3 : _rxPin;
#endif
#ifdef TX3
txPin = _txPin < 0 ? (int8_t)TX3 : _txPin;
#endif
}
break;
#endif // UART_NUM_3
#if SOC_UART_HP_NUM > 4
case UART_NUM_4:
if (rxPin < 0 && txPin < 0) {
// do not change RX4/TX4 if it has already been set before
#ifdef RX4
rxPin = _rxPin < 0 ? (int8_t)RX4 : _rxPin;
#endif
#ifdef TX4
txPin = _txPin < 0 ? (int8_t)TX4 : _txPin;
#endif
}
break;
#endif // UART_NUM_4
#if (SOC_UART_LP_NUM >= 1)
case LP_UART_NUM_0:
if (rxPin < 0 && txPin < 0) {
// do not change RX0_LP/TX0_LP if it has already been set before
#ifdef LP_RX0
rxPin = _rxPin < 0 ? (int8_t)LP_RX0 : _rxPin;
#endif
#ifdef LP_TX0
txPin = _txPin < 0 ? (int8_t)LP_TX0 : _txPin;
#endif
}
break;
#endif // LP_UART_NUM_0
}
}
// if no RX/TX pins are defined, it will not start the UART driver
if (rxPin < 0 && txPin < 0) {
log_e("No RX/TX pins defined. Please set RX/TX pins.");
HSERIAL_MUTEX_UNLOCK();
return;
}
// IDF UART driver keeps Pin setting on restarting. Negative Pin number will keep it unmodified.
// it will detach previous UART attached pins
// indicates that uartbegin() has to initialize a new IDF driver
if (_testUartBegin(_uart_nr, baud ? baud : 9600, config, rxPin, txPin, _rxBufferSize, _txBufferSize, invert, rxfifo_full_thrhd)) {
_destroyEventTask(); // when IDF uart driver must be restarted, _eventTask must finish too
}
// IDF UART driver keeps Pin setting on restarting. Negative Pin number will keep it unmodified.
// it will detach previous UART attached pins
_uart = uartBegin(_uart_nr, baud ? baud : 9600, config, rxPin, txPin, _rxBufferSize, _txBufferSize, invert, rxfifo_full_thrhd);
if (_uart == NULL) {
log_e("UART driver failed to start. Please check the logs.");
HSERIAL_MUTEX_UNLOCK();
return;
}
if (!baud) {
// using baud rate as zero, forces it to try to detect the current baud rate in place
uartStartDetectBaudrate(_uart);
time_t startMillis = millis();
unsigned long detectedBaudRate = 0;
while (millis() - startMillis < timeout_ms && !(detectedBaudRate = uartDetectBaudrate(_uart))) {
yield();
}
if (detectedBaudRate) {
delay(100); // Give some time...
_uart = uartBegin(_uart_nr, detectedBaudRate, config, rxPin, txPin, _rxBufferSize, _txBufferSize, invert, rxfifo_full_thrhd);
if (_uart == NULL) {
log_e("UART driver failed to start. Please check the logs.");
HSERIAL_MUTEX_UNLOCK();
return;
}
} else {
log_e("Could not detect baudrate. Serial data at the port must be present within the timeout for detection to be possible");
_uart = NULL;
}
}
// create a task to deal with Serial Events when, for example, calling begin() twice to change the baudrate,
// or when setting the callback before calling begin()
if (_uart != NULL && (_onReceiveCB != NULL || _onReceiveErrorCB != NULL) && _eventTask == NULL) {
_createEventTask(this);
}
// Set UART RX timeout
uartSetRxTimeout(_uart, _rxTimeout);
// Set UART FIFO Full depending on the baud rate.
// Lower baud rates will force to emulate byte-by-byte reading
// Higher baud rates will keep IDF default of 120 bytes for FIFO FULL Interrupt
// It can also be changed by the application at any time
if (!_rxFIFOFull) { // it has not being changed before calling begin()
// set a default FIFO Full value for the IDF driver
uint8_t fifoFull = 1;
// if baud rate is higher than 57600 or onReceive() is set, it will set FIFO Full to 120 bytes, except for LP UART
if (_uart_nr < SOC_UART_HP_NUM && (baud > 57600 || (_onReceiveCB != NULL && _onReceiveTimeout))) {
fifoFull = 120;
}
uartSetRxFIFOFull(_uart, fifoFull);
_rxFIFOFull = fifoFull;
}
HSERIAL_MUTEX_UNLOCK();
}
void HardwareSerial::updateBaudRate(unsigned long baud) {
uartSetBaudRate(_uart, baud);
}
void HardwareSerial::end() {
// default Serial.end() will completely disable HardwareSerial,
// including any tasks or debug message channel (log_x()) - but not for IDF log messages!
_onReceiveCB = NULL;
_onReceiveErrorCB = NULL;
_rxFIFOFull = 0;
uartEnd(_uart_nr); // fully detach all pins and delete the UART driver
_destroyEventTask(); // when IDF uart driver is deleted, _eventTask must finish too
_uart = NULL;
}
void HardwareSerial::setDebugOutput(bool en) {
if (_uart == 0) {
return;
}
#if (SOC_UART_LP_NUM >= 1)
if (_uart_nr >= SOC_UART_HP_NUM) {
log_e("LP UART does not support Debug Output.");
return;
}
#endif
if (en) {
uartSetDebug(_uart);
} else {
if (uartGetDebug() == _uart_nr) {
uartSetDebug(NULL);
}
}
}
int HardwareSerial::available(void) {
return uartAvailable(_uart);
}
int HardwareSerial::availableForWrite(void) {
return uartAvailableForWrite(_uart);
}
int HardwareSerial::peek(void) {
if (available()) {
return uartPeek(_uart);
}
return -1;
}
int HardwareSerial::read(void) {
uint8_t c = 0;
if (uartReadBytes(_uart, &c, 1, 0) == 1) {
return c;
} else {
return -1;
}
}
// read characters into buffer
// terminates if size characters have been read, or no further are pending
// returns the number of characters placed in the buffer
// the buffer is NOT null terminated.
size_t HardwareSerial::read(uint8_t *buffer, size_t size) {
return uartReadBytes(_uart, buffer, size, 0);
}
// Overrides Stream::readBytes() to be faster using IDF
size_t HardwareSerial::readBytes(uint8_t *buffer, size_t length) {
return uartReadBytes(_uart, buffer, length, (uint32_t)getTimeout());
}
void HardwareSerial::flush(void) {
uartFlush(_uart);
}
void HardwareSerial::flush(bool txOnly) {
uartFlushTxOnly(_uart, txOnly);
}
size_t HardwareSerial::write(uint8_t c) {
uartWrite(_uart, c);
return 1;
}
size_t HardwareSerial::write(const uint8_t *buffer, size_t size) {
uartWriteBuf(_uart, buffer, size);
return size;
}
uint32_t HardwareSerial::baudRate() {
return uartGetBaudRate(_uart);
}
HardwareSerial::operator bool() const {
return uartIsDriverInstalled(_uart);
}
bool HardwareSerial::setRxInvert(bool invert) {
return uartSetRxInvert(_uart, invert);
}
bool HardwareSerial::setTxInvert(bool invert) {
return uartSetTxInvert(_uart, invert);
}
bool HardwareSerial::setCtsInvert(bool invert) {
return uartSetCtsInvert(_uart, invert);
}
bool HardwareSerial::setRtsInvert(bool invert) {
return uartSetRtsInvert(_uart, invert);
}
// negative Pin value will keep it unmodified
// can be called after or before begin()
bool HardwareSerial::setPins(int8_t rxPin, int8_t txPin, int8_t ctsPin, int8_t rtsPin) {
// map logical pins to GPIO numbers
rxPin = digitalPinToGPIONumber(rxPin);
txPin = digitalPinToGPIONumber(txPin);
ctsPin = digitalPinToGPIONumber(ctsPin);
rtsPin = digitalPinToGPIONumber(rtsPin);
// uartSetPins() checks if pins are valid and, if necessary, detaches the previous ones
return uartSetPins(_uart_nr, rxPin, txPin, ctsPin, rtsPin);
}
// Enables or disables Hardware Flow Control using RTS and/or CTS pins
// must use setAllPins() in order to set RTS/CTS pins
// SerialHwFlowCtrl = UART_HW_FLOWCTRL_DISABLE, UART_HW_FLOWCTRL_RTS,
// UART_HW_FLOWCTRL_CTS, UART_HW_FLOWCTRL_CTS_RTS
bool HardwareSerial::setHwFlowCtrlMode(SerialHwFlowCtrl mode, uint8_t threshold) {
return uartSetHwFlowCtrlMode(_uart, mode, threshold);
}
// Sets the uart mode in the esp32 uart for use with RS485 modes
// HwFlowCtrl must be disabled and RTS pin set
// SerialMode = UART_MODE_UART, UART_MODE_RS485_HALF_DUPLEX, UART_MODE_IRDA,
// or testing mode: UART_MODE_RS485_COLLISION_DETECT, UART_MODE_RS485_APP_CTRL
bool HardwareSerial::setMode(SerialMode mode) {
return uartSetMode(_uart, mode);
}
// Sets the UART Clock Source based on the compatible SoC options
// This method must be called before starting UART using begin(), otherwise it won't have any effect.
// Clock Source Options are:
// UART_CLK_SRC_DEFAULT :: any SoC - it will set whatever IDF defines as the default UART Clock Source
// UART_CLK_SRC_APB :: ESP32, ESP32-S2, ESP32-C3 and ESP32-S3
// UART_CLK_SRC_PLL :: ESP32-C2, ESP32-C5, ESP32-C6, ESP32-C61, ESP32-H2 and ESP32-P4
// UART_CLK_SRC_XTAL :: ESP32-C2, ESP32-C3, ESP32-C5, ESP32-C6, ESP32-C61, ESP32-H2, ESP32-S3 and ESP32-P4
// UART_CLK_SRC_RTC :: ESP32-C2, ESP32-C3, ESP32-C5, ESP32-C6, ESP32-C61, ESP32-H2, ESP32-S3 and ESP32-P4
// UART_CLK_SRC_REF_TICK :: ESP32 and ESP32-S2
// Note: CLK_SRC_PLL Freq depends on the SoC - ESP32-C2 has 40MHz, ESP32-H2 has 48MHz and ESP32-C5, C6, C61 and P4 has 80MHz
// Note: ESP32-C6, C61, ESP32-P4 and ESP32-C5 have LP UART that will use only RTC_FAST or XTAL/2 as Clock Source
bool HardwareSerial::setClockSource(SerialClkSrc clkSrc) {
if (_uart) {
log_e("No Clock Source change was done. This function must be called before beginning UART%d.", _uart_nr);
return false;
}
return uartSetClockSource(_uart_nr, (uart_sclk_t)clkSrc);
}
// minimum total RX Buffer size is the UART FIFO space (128 bytes for most SoC) + 1. IDF imposition.
// LP UART has FIFO of 16 bytes
size_t HardwareSerial::setRxBufferSize(size_t new_size) {
if (_uart) {
log_e("RX Buffer can't be resized when Serial is already running. Set it before calling begin().");
return 0;
}
uint8_t FIFOLen = UART_HW_FIFO_LEN(_uart_nr);
// Valid value is higher than the FIFO length
if (new_size <= FIFOLen) {
new_size = FIFOLen + 1;
log_w("RX Buffer set to minimum value: %d.", new_size);
}
_rxBufferSize = new_size;
return _rxBufferSize;
}
// minimum total TX Buffer size is the UART FIFO space (128 bytes for most SoC) + 1.
// LP UART has FIFO of 16 bytes
size_t HardwareSerial::setTxBufferSize(size_t new_size) {
if (_uart) {
log_e("TX Buffer can't be resized when Serial is already running. Set it before calling begin().");
return 0;
}
uint8_t FIFOLen = UART_HW_FIFO_LEN(_uart_nr);
// Valid values are zero or higher than the FIFO length
if (new_size > 0 && new_size <= FIFOLen) {
new_size = FIFOLen + 1;
log_w("TX Buffer set to minimum value: %d.", new_size);
}
// if new_size is higher than SOC_UART_FIFO_LEN, TX Ringbuffer will be active and it will be used to report back "availableToWrite()"
_txBufferSize = new_size;
return new_size;
}
+460
View File
@@ -0,0 +1,460 @@
/*
HardwareSerial.h - Hardware serial library for Wiring
Copyright (c) 2006 Nicholas Zambetti. All right 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
Modified 28 September 2010 by Mark Sproul
Modified 14 August 2012 by Alarus
Modified 3 December 2013 by Matthijs Kooijman
Modified 18 December 2014 by Ivan Grokhotkov (esp8266 platform support)
Modified 31 March 2015 by Markus Sattler (rewrite the code for UART0 + UART1 support in ESP8266)
Modified 25 April 2015 by Thomas Flayols (add configuration different from 8N1 in ESP8266)
Modified 13 October 2018 by Jeroen Döll (add baudrate detection)
Baudrate detection example usage (detection on Serial1):
void setup() {
Serial.begin(115200);
delay(100);
Serial.println();
Serial1.begin(0, SERIAL_8N1, -1, -1, true, 11000UL); // Passing 0 for baudrate to detect it, the last parameter is a timeout in ms
unsigned long detectedBaudRate = Serial1.baudRate();
if(detectedBaudRate) {
Serial.printf("Detected baudrate is %lu\n", detectedBaudRate);
} else {
Serial.println("No baudrate detected, Serial1 will not work!");
}
}
Pay attention: the baudrate returned by baudRate() may be rounded, eg 115200 returns 115201
*/
#ifndef HardwareSerial_h
#define HardwareSerial_h
#include <inttypes.h>
#include <functional>
#include "Stream.h"
#include "esp32-hal.h"
#include "soc/soc_caps.h"
#include "HWCDC.h"
#include "USBCDC.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
enum SerialConfig {
SERIAL_5N1 = 0x8000010,
SERIAL_6N1 = 0x8000014,
SERIAL_7N1 = 0x8000018,
SERIAL_8N1 = 0x800001c,
SERIAL_5N2 = 0x8000030,
SERIAL_6N2 = 0x8000034,
SERIAL_7N2 = 0x8000038,
SERIAL_8N2 = 0x800003c,
SERIAL_5E1 = 0x8000012,
SERIAL_6E1 = 0x8000016,
SERIAL_7E1 = 0x800001a,
SERIAL_8E1 = 0x800001e,
SERIAL_5E2 = 0x8000032,
SERIAL_6E2 = 0x8000036,
SERIAL_7E2 = 0x800003a,
SERIAL_8E2 = 0x800003e,
SERIAL_5O1 = 0x8000013,
SERIAL_6O1 = 0x8000017,
SERIAL_7O1 = 0x800001b,
SERIAL_8O1 = 0x800001f,
SERIAL_5O2 = 0x8000033,
SERIAL_6O2 = 0x8000037,
SERIAL_7O2 = 0x800003b,
SERIAL_8O2 = 0x800003f
};
typedef uart_mode_t SerialMode;
typedef uart_hw_flowcontrol_t SerialHwFlowCtrl;
typedef enum {
UART_NO_ERROR,
UART_BREAK_ERROR,
UART_BUFFER_FULL_ERROR,
UART_FIFO_OVF_ERROR,
UART_FRAME_ERROR,
UART_PARITY_ERROR
} hardwareSerial_error_t;
typedef enum {
UART_CLK_SRC_DEFAULT = UART_SCLK_DEFAULT,
#if SOC_UART_SUPPORT_APB_CLK
UART_CLK_SRC_APB = UART_SCLK_APB,
#endif
#if SOC_UART_SUPPORT_PLL_F40M_CLK
UART_CLK_SRC_PLL = UART_SCLK_PLL_F40M,
#elif SOC_UART_SUPPORT_PLL_F80M_CLK
UART_CLK_SRC_PLL = UART_SCLK_PLL_F80M,
#elif CONFIG_IDF_TARGET_ESP32H2
UART_CLK_SRC_PLL = UART_SCLK_PLL_F48M,
#endif
#if SOC_UART_SUPPORT_XTAL_CLK
UART_CLK_SRC_XTAL = UART_SCLK_XTAL,
#endif
#if SOC_UART_SUPPORT_RTC_CLK
UART_CLK_SRC_RTC = UART_SCLK_RTC,
#endif
#if SOC_UART_SUPPORT_REF_TICK
UART_CLK_SRC_REF_TICK = UART_SCLK_REF_TICK,
#endif
} SerialClkSrc;
#ifndef ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE
#ifndef CONFIG_ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE
#define ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE 2048
#else
#define ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE CONFIG_ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE
#endif
#endif
#ifndef ARDUINO_SERIAL_EVENT_TASK_PRIORITY
#ifndef CONFIG_ARDUINO_SERIAL_EVENT_TASK_PRIORITY
#define ARDUINO_SERIAL_EVENT_TASK_PRIORITY (configMAX_PRIORITIES - 1)
#else
#define ARDUINO_SERIAL_EVENT_TASK_PRIORITY CONFIG_ARDUINO_SERIAL_EVENT_TASK_PRIORITY
#endif
#endif
#ifndef ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE
#ifndef CONFIG_ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE
#define ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE -1
#else
#define ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE CONFIG_ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE
#endif
#endif
// UART0 pins are defined by default by the bootloader.
// The definitions for SOC_* should not be changed unless the bootloader pins
// have changed and you know what you are doing.
#ifndef SOC_RX0
#if CONFIG_IDF_TARGET_ESP32
#define SOC_RX0 (gpio_num_t)3
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
#define SOC_RX0 (gpio_num_t)44
#elif CONFIG_IDF_TARGET_ESP32C2
#define SOC_RX0 (gpio_num_t)19
#elif CONFIG_IDF_TARGET_ESP32C3
#define SOC_RX0 (gpio_num_t)20
#elif CONFIG_IDF_TARGET_ESP32C6
#define SOC_RX0 (gpio_num_t)17
#elif CONFIG_IDF_TARGET_ESP32H2
#define SOC_RX0 (gpio_num_t)23
#elif CONFIG_IDF_TARGET_ESP32P4
#define SOC_RX0 (gpio_num_t)38
#elif CONFIG_IDF_TARGET_ESP32C5
#define SOC_RX0 (gpio_num_t)12
#elif CONFIG_IDF_TARGET_ESP32C61
#define SOC_RX0 (gpio_num_t)10
#endif
#endif
#ifndef SOC_TX0
#if CONFIG_IDF_TARGET_ESP32
#define SOC_TX0 (gpio_num_t)1
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
#define SOC_TX0 (gpio_num_t)43
#elif CONFIG_IDF_TARGET_ESP32C2
#define SOC_TX0 (gpio_num_t)20
#elif CONFIG_IDF_TARGET_ESP32C3
#define SOC_TX0 (gpio_num_t)21
#elif CONFIG_IDF_TARGET_ESP32C6
#define SOC_TX0 (gpio_num_t)16
#elif CONFIG_IDF_TARGET_ESP32H2
#define SOC_TX0 (gpio_num_t)24
#elif CONFIG_IDF_TARGET_ESP32P4
#define SOC_TX0 (gpio_num_t)37
#elif CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61
#define SOC_TX0 (gpio_num_t)11
#endif
#endif
// Default pins for UART1 are arbitrary, and defined here for convenience.
#if SOC_UART_HP_NUM > 1
#ifndef RX1
#if CONFIG_IDF_TARGET_ESP32
#define RX1 (gpio_num_t)26
#elif CONFIG_IDF_TARGET_ESP32S2
#define RX1 (gpio_num_t)4
#elif CONFIG_IDF_TARGET_ESP32C2
#define RX1 (gpio_num_t)10
#elif CONFIG_IDF_TARGET_ESP32C3
#define RX1 (gpio_num_t)18
#elif CONFIG_IDF_TARGET_ESP32S3
#define RX1 (gpio_num_t)15
#elif CONFIG_IDF_TARGET_ESP32C6
#define RX1 (gpio_num_t)4
#elif CONFIG_IDF_TARGET_ESP32H2
#define RX1 (gpio_num_t)0
#elif CONFIG_IDF_TARGET_ESP32P4
#define RX1 (gpio_num_t)11
#elif CONFIG_IDF_TARGET_ESP32C5
#define RX1 (gpio_num_t)4
#elif CONFIG_IDF_TARGET_ESP32C61
#define RX1 (gpio_num_t)8
#endif
#endif
#ifndef TX1
#if CONFIG_IDF_TARGET_ESP32
#define TX1 (gpio_num_t)27
#elif CONFIG_IDF_TARGET_ESP32S2
#define TX1 (gpio_num_t)5
#elif CONFIG_IDF_TARGET_ESP32C2
#define TX1 (gpio_num_t)18
#elif CONFIG_IDF_TARGET_ESP32C3
#define TX1 (gpio_num_t)19
#elif CONFIG_IDF_TARGET_ESP32S3
#define TX1 (gpio_num_t)16
#elif CONFIG_IDF_TARGET_ESP32C6
#define TX1 (gpio_num_t)5
#elif CONFIG_IDF_TARGET_ESP32H2
#define TX1 (gpio_num_t)1
#elif CONFIG_IDF_TARGET_ESP32P4
#define TX1 (gpio_num_t)10
#elif CONFIG_IDF_TARGET_ESP32C5
#define TX1 (gpio_num_t)5
#elif CONFIG_IDF_TARGET_ESP32C61
#define TX1 (gpio_num_t)29
#endif
#endif
#endif /* SOC_UART_HP_NUM > 1 */
// Default pins for UART2 are arbitrary, and defined here for convenience.
#if SOC_UART_HP_NUM > 2
#ifndef RX2
#if CONFIG_IDF_TARGET_ESP32
#define RX2 (gpio_num_t)4
#elif CONFIG_IDF_TARGET_ESP32S3
#define RX2 (gpio_num_t)19
#endif
#endif
#ifndef TX2
#if CONFIG_IDF_TARGET_ESP32
#define TX2 (gpio_num_t)25
#elif CONFIG_IDF_TARGET_ESP32S3
#define TX2 (gpio_num_t)20
#endif
#endif
#endif /* SOC_UART_HP_NUM > 2 */
#if SOC_UART_LP_NUM >= 1
#ifndef LP_RX0
#define LP_RX0 (gpio_num_t) LP_U0RXD_GPIO_NUM
#endif
#ifndef LP_TX0
#define LP_TX0 (gpio_num_t) LP_U0TXD_GPIO_NUM
#endif
#endif /* SOC_UART_LP_NUM >= 1 */
typedef std::function<void(void)> OnReceiveCb;
typedef std::function<void(hardwareSerial_error_t)> OnReceiveErrorCb;
class HardwareSerial : public Stream {
public:
HardwareSerial(uint8_t uart_nr);
~HardwareSerial();
// setRxTimeout sets the timeout after which onReceive callback will be called (after receiving data, it waits for this time of UART rx inactivity to call the callback fnc)
// param symbols_timeout defines a timeout threshold in uart symbol periods. Setting 0 symbol timeout disables the callback call by timeout.
// Maximum timeout setting is calculacted automatically by IDF. If set above the maximum, it is ignored and an error is printed on Serial0 (check console).
// Examples: Maximum for 11 bits symbol is 92 (SERIAL_8N2, SERIAL_8E1, SERIAL_8O1, etc), Maximum for 10 bits symbol is 101 (SERIAL_8N1).
// For example symbols_timeout=1 defines a timeout equal to transmission time of one symbol (~11 bit) on current baudrate.
// For a baudrate of 9600, SERIAL_8N1 (10 bit symbol) and symbols_timeout = 3, the timeout would be 3 / (9600 / 10) = 3.125 ms
bool setRxTimeout(uint8_t symbols_timeout);
// setRxFIFOFull(uint8_t fifoBytes) will set the number of bytes that will trigger UART_INTR_RXFIFO_FULL interrupt and fill up RxRingBuffer
// This affects some functions such as Serial::available() and Serial.read() because, in a UART flow of receiving data, Serial internal
// RxRingBuffer will be filled only after these number of bytes arrive or a RX Timeout happens.
// This parameter can be set to 1 in order to receive byte by byte, but it will also consume more CPU time as the ISR will be activates often.
bool setRxFIFOFull(uint8_t fifoBytes);
// onReceive will setup a callback that will be called whenever an UART interruption occurs (UART_INTR_RXFIFO_FULL or UART_INTR_RXFIFO_TOUT)
// UART_INTR_RXFIFO_FULL interrupt triggers at UART_FULL_THRESH_DEFAULT bytes received (defined as 120 bytes by default in IDF)
// UART_INTR_RXFIFO_TOUT interrupt triggers at UART_TOUT_THRESH_DEFAULT symbols passed without any reception (defined as 10 symbols by default in IDF)
// onlyOnTimeout parameter will define how onReceive will behave:
// Default: true -- The callback will only be called when RX Timeout happens.
// Whole stream of bytes will be ready for being read on the callback function at once.
// This option may lead to Rx Overflow depending on the Rx Buffer Size and number of bytes received in the streaming
// false -- The callback will be called when FIFO reaches 120 bytes and also on RX Timeout.
// The stream of incommig bytes will be "split" into blocks of 120 bytes on each callback.
// This option avoid any sort of Rx Overflow, but leaves the UART packet reassembling work to the Application.
void onReceive(OnReceiveCb function, bool onlyOnTimeout = false);
// onReceive will be called on error events (see hardwareSerial_error_t)
void onReceiveError(OnReceiveErrorCb function);
// eventQueueReset clears all events in the queue (the events that trigger onReceive and onReceiveError) - maybe useful in some use cases
void eventQueueReset();
// When pins are changed, it will detach the previous ones
// if pin is negative, it won't be set/changed and will be kept as is
// timeout_ms is used in baudrate detection (ESP32, ESP32S2 only)
// invert will invert RX/TX polarity
// rxfifo_full_thrhd if the UART Flow Control Threshold in the UART FIFO (max 127)
void begin(
unsigned long baud, uint32_t config = SERIAL_8N1, int8_t rxPin = -1, int8_t txPin = -1, bool invert = false, unsigned long timeout_ms = 20000UL,
uint8_t rxfifo_full_thrhd = 120
);
void end(void);
void updateBaudRate(unsigned long baud);
int available(void);
int availableForWrite(void);
int peek(void);
int read(void);
size_t read(uint8_t *buffer, size_t size);
inline size_t read(char *buffer, size_t size) {
return read((uint8_t *)buffer, size);
}
// Overrides Stream::readBytes() to be faster using IDF
size_t readBytes(uint8_t *buffer, size_t length);
size_t readBytes(char *buffer, size_t length) {
return readBytes((uint8_t *)buffer, length);
}
void flush(void);
void flush(bool txOnly);
size_t write(uint8_t);
size_t write(const uint8_t *buffer, size_t size);
inline size_t write(const char *buffer, size_t size) {
return write((uint8_t *)buffer, size);
}
inline size_t write(const char *s) {
return write((uint8_t *)s, strlen(s));
}
inline size_t write(unsigned long n) {
return write((uint8_t)n);
}
inline size_t write(long n) {
return write((uint8_t)n);
}
inline size_t write(unsigned int n) {
return write((uint8_t)n);
}
inline size_t write(int n) {
return write((uint8_t)n);
}
uint32_t baudRate();
operator bool() const;
void setDebugOutput(bool);
// functions used to enable or disable UART pins signal inversion
// returns the requested operation success status
bool setRxInvert(bool);
bool setTxInvert(bool);
bool setCtsInvert(bool);
bool setRtsInvert(bool);
// Negative Pin Number will keep it unmodified, thus this function can set individual pins
// setPins() can be called after or before begin()
// When pins are changed, it will detach the previous ones
bool setPins(int8_t rxPin, int8_t txPin, int8_t ctsPin = -1, int8_t rtsPin = -1);
// Enables or disables Hardware Flow Control using RTS and/or CTS pins (must use setAllPins() before)
// UART_HW_FLOWCTRL_DISABLE = 0x0 disable hardware flow control
// UART_HW_FLOWCTRL_RTS = 0x1 enable RX hardware flow control (rts)
// UART_HW_FLOWCTRL_CTS = 0x2 enable TX hardware flow control (cts)
// UART_HW_FLOWCTRL_CTS_RTS = 0x3 enable hardware flow control
bool setHwFlowCtrlMode(SerialHwFlowCtrl mode = UART_HW_FLOWCTRL_CTS_RTS, uint8_t threshold = 64); // 64 is half FIFO Length
// Used to set RS485 modes such as UART_MODE_RS485_HALF_DUPLEX for Auto RTS function on ESP32
// UART_MODE_UART = 0x00 mode: regular UART mode
// UART_MODE_RS485_HALF_DUPLEX = 0x01 mode: half duplex RS485 UART mode control by RTS pin
// UART_MODE_IRDA = 0x02 mode: IRDA UART mode
// UART_MODE_RS485_COLLISION_DETECT = 0x03 mode: RS485 collision detection UART mode (used for test purposes)
// UART_MODE_RS485_APP_CTRL = 0x04 mode: application control RS485 UART mode (used for test purposes)
bool setMode(SerialMode mode);
// Used to set the UART clock source mode. It must be set before calling begin(), otherwise it won't have any effect.
// Not all clock source are available to every SoC. The compatible option are listed here:
// UART_CLK_SRC_DEFAULT :: any SoC - it will set whatever IDF defines as the default UART Clock Source
// UART_CLK_SRC_APB :: ESP32, ESP32-S2, ESP32-C3 and ESP32-S3
// UART_CLK_SRC_PLL :: ESP32-C2, ESP32-C5, ESP32-C6, ESP32-C61, ESP32-H2 and ESP32-P4
// UART_CLK_SRC_XTAL :: ESP32-C2, ESP32-C3, ESP32-C5, ESP32-C6, ESP32-C61, ESP32-H2, ESP32-S3 and ESP32-P4
// UART_CLK_SRC_RTC :: ESP32-C2, ESP32-C3, ESP32-C5, ESP32-C6, ESP32-C61, ESP32-H2, ESP32-S3 and ESP32-P4
// UART_CLK_SRC_REF_TICK :: ESP32 and ESP32-S2
// Note: CLK_SRC_PLL Freq depends on the SoC - ESP32-C2 has 40MHz, ESP32-H2 has 48MHz and ESP32-C5, C6, C61 and P4 has 80MHz
// Note: ESP32-C6, C61, ESP32-P4 and ESP32-C5 have LP UART that will use only RTC_FAST or XTAL/2 as Clock Source
bool setClockSource(SerialClkSrc clkSrc);
size_t setRxBufferSize(size_t new_size);
size_t setTxBufferSize(size_t new_size);
protected:
uint8_t _uart_nr;
uart_t *_uart;
size_t _rxBufferSize;
size_t _txBufferSize;
OnReceiveCb _onReceiveCB;
OnReceiveErrorCb _onReceiveErrorCB;
// _onReceive and _rxTimeout have be consistent when timeout is disabled
bool _onReceiveTimeout;
uint8_t _rxTimeout, _rxFIFOFull;
TaskHandle_t _eventTask;
#if !CONFIG_DISABLE_HAL_LOCKS
SemaphoreHandle_t _lock;
#endif
void _createEventTask(void *args);
void _destroyEventTask(void);
static void _uartEventTask(void *args);
};
extern void serialEventRun(void) __attribute__((weak));
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL)
#ifndef ARDUINO_USB_CDC_ON_BOOT
#define ARDUINO_USB_CDC_ON_BOOT 0
#endif
#if ARDUINO_USB_CDC_ON_BOOT //Serial used from Native_USB_CDC | HW_CDC_JTAG
#if ARDUINO_USB_MODE // Hardware CDC mode
// Arduino Serial is the HW JTAG CDC device
#define Serial HWCDCSerial
#else // !ARDUINO_USB_MODE -- Native USB Mode
// Arduino Serial is the Native USB CDC device
#define Serial USBSerial
#endif // ARDUINO_USB_MODE
#else // !ARDUINO_USB_CDC_ON_BOOT -- Serial is used from UART0
// if not using CDC on Boot, Arduino Serial is the UART0 device
#define Serial Serial0
#endif // ARDUINO_USB_CDC_ON_BOOT
// There is always Seria0 for UART0
extern HardwareSerial Serial0;
#if SOC_UART_NUM > 1
extern HardwareSerial Serial1;
#endif
#if SOC_UART_NUM > 2
extern HardwareSerial Serial2;
#endif
#if SOC_UART_NUM > 3
extern HardwareSerial Serial3;
#endif
#if SOC_UART_NUM > 4
extern HardwareSerial Serial4;
#endif
#if SOC_UART_NUM > 5
extern HardwareSerial Serial5;
#endif
#endif //!defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL)
#endif // HardwareSerial_h
+38
View File
@@ -0,0 +1,38 @@
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "HashBuilder.h"
void HashBuilder::add(const char *data) {
add((const uint8_t *)data, strlen(data));
}
void HashBuilder::add(String data) {
add(data.c_str());
}
void HashBuilder::addHexString(const char *data) {
size_t len = strlen(data);
uint8_t *tmp = (uint8_t *)malloc(len / 2);
if (tmp == NULL) {
return;
}
hex2bytes(tmp, len / 2, data);
add(tmp, len / 2);
free(tmp);
}
void HashBuilder::addHexString(String data) {
addHexString(data.c_str());
}
+63
View File
@@ -0,0 +1,63 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef HashBuilder_h
#define HashBuilder_h
#include <WString.h>
#include <Stream.h>
#include "HEXBuilder.h"
/* Try to prevent most compilers from optimizing out clearing of memory that
* becomes unaccessible after this function is called. This is mostly the case
* for clearing local stack variables at the end of a function. This is not
* exactly perfect, i.e., someone could come up with a compiler that figures out
* the pointer is pointing to memset and then end up optimizing the call out, so
* try go a bit further by storing the first octet (now zero) to make this even
* a bit more difficult to optimize out. Once memset_s() is available, that
* could be used here instead. */
static void *(*const volatile memset_func)(void *, int, size_t) = memset;
static uint8_t forced_memzero_val;
static inline void forced_memzero(void *ptr, size_t len) {
memset_func(ptr, 0, len);
if (len) {
forced_memzero_val = ((uint8_t *)ptr)[0];
}
}
// Base class for hash builders
class HashBuilder : public HEXBuilder {
public:
virtual ~HashBuilder() {}
virtual void begin() = 0;
virtual void add(const uint8_t *data, size_t len) = 0;
void add(const char *data);
void add(String data);
void addHexString(const char *data);
void addHexString(String data);
virtual bool addStream(Stream &stream, const size_t maxLen) = 0;
virtual void calculate() = 0;
virtual void getBytes(uint8_t *output) = 0;
virtual void getChars(char *output) = 0;
virtual String toString() = 0;
virtual size_t getHashSize() const = 0;
};
#endif
+453
View File
@@ -0,0 +1,453 @@
/*
IPAddress.cpp - Base class that provides IPAddress
Copyright (c) 2011 Adrian McEwen. All right 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 "IPAddress.h"
#include "Print.h"
#include "lwip/netif.h"
#include "StreamString.h"
#ifndef CONFIG_LWIP_IPV6
#define IP6_NO_ZONE 0
#endif
IPAddress::IPAddress() : IPAddress(IPv4) {}
IPAddress::IPAddress(IPType ip_type) {
_type = ip_type;
_zone = IP6_NO_ZONE;
memset(_address.bytes, 0, sizeof(_address.bytes));
}
IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) {
_type = IPv4;
_zone = IP6_NO_ZONE;
memset(_address.bytes, 0, sizeof(_address.bytes));
_address.bytes[IPADDRESS_V4_BYTES_INDEX] = first_octet;
_address.bytes[IPADDRESS_V4_BYTES_INDEX + 1] = second_octet;
_address.bytes[IPADDRESS_V4_BYTES_INDEX + 2] = third_octet;
_address.bytes[IPADDRESS_V4_BYTES_INDEX + 3] = fourth_octet;
}
IPAddress::IPAddress(
uint8_t o1, uint8_t o2, uint8_t o3, uint8_t o4, uint8_t o5, uint8_t o6, uint8_t o7, uint8_t o8, uint8_t o9, uint8_t o10, uint8_t o11, uint8_t o12,
uint8_t o13, uint8_t o14, uint8_t o15, uint8_t o16, uint8_t z
) {
_type = IPv6;
_address.bytes[0] = o1;
_address.bytes[1] = o2;
_address.bytes[2] = o3;
_address.bytes[3] = o4;
_address.bytes[4] = o5;
_address.bytes[5] = o6;
_address.bytes[6] = o7;
_address.bytes[7] = o8;
_address.bytes[8] = o9;
_address.bytes[9] = o10;
_address.bytes[10] = o11;
_address.bytes[11] = o12;
_address.bytes[12] = o13;
_address.bytes[13] = o14;
_address.bytes[14] = o15;
_address.bytes[15] = o16;
_zone = z;
}
IPAddress::IPAddress(uint32_t address) {
// IPv4 only
_type = IPv4;
_zone = IP6_NO_ZONE;
memset(_address.bytes, 0, sizeof(_address.bytes));
_address.dword[IPADDRESS_V4_DWORD_INDEX] = address;
// NOTE on conversion/comparison and uint32_t:
// These conversions are host platform dependent.
// There is a defined integer representation of IPv4 addresses,
// based on network byte order (will be the value on big endian systems),
// e.g. http://2398766798 is the same as http://142.250.70.206,
// However on little endian systems the octets 0x83, 0xFA, 0x46, 0xCE,
// in that order, will form the integer (uint32_t) 3460758158 .
}
IPAddress::IPAddress(const uint8_t *address) : IPAddress(IPv4, address) {}
IPAddress::IPAddress(IPType ip_type, const uint8_t *address, uint8_t z) {
_type = ip_type;
if (ip_type == IPv4) {
memset(_address.bytes, 0, sizeof(_address.bytes));
memcpy(&_address.bytes[IPADDRESS_V4_BYTES_INDEX], address, sizeof(uint32_t));
_zone = 0;
} else {
memcpy(_address.bytes, address, sizeof(_address.bytes));
_zone = z;
}
}
IPAddress::IPAddress(const char *address) {
fromString(address);
}
IPAddress::IPAddress(const IPAddress &address) {
*this = address;
}
String IPAddress::toString(bool includeZone) const {
StreamString s;
printTo(s, includeZone);
return String(s);
}
bool IPAddress::fromString(const char *address) {
if (!fromString4(address)) {
return fromString6(address);
}
return true;
}
bool IPAddress::fromString4(const char *address) {
// TODO: add support for "a", "a.b", "a.b.c" formats
int16_t acc = -1; // Accumulator
uint8_t dots = 0;
memset(_address.bytes, 0, sizeof(_address.bytes));
while (*address) {
char c = *address++;
if (c >= '0' && c <= '9') {
acc = (acc < 0) ? (c - '0') : acc * 10 + (c - '0');
if (acc > 255) {
// Value out of [0..255] range
return false;
}
} else if (c == '.') {
if (dots == 3) {
// Too many dots (there must be 3 dots)
return false;
}
if (acc < 0) {
/* No value between dots, e.g. '1..' */
return false;
}
_address.bytes[IPADDRESS_V4_BYTES_INDEX + dots++] = acc;
acc = -1;
} else {
// Invalid char
return false;
}
}
if (dots != 3) {
// Too few dots (there must be 3 dots)
return false;
}
if (acc < 0) {
/* No value between dots, e.g. '1..' */
return false;
}
_address.bytes[IPADDRESS_V4_BYTES_INDEX + 3] = acc;
_type = IPv4;
return true;
}
bool IPAddress::fromString6(const char *address) {
uint32_t acc = 0; // Accumulator
int colons = 0, double_colons = -1;
while (*address) {
char c = tolower(*address++);
if (isalnum(c) && c <= 'f') {
if (c >= 'a') {
c -= 'a' - '0' - 10;
}
acc = acc * 16 + (c - '0');
if (acc > 0xffff) {
// Value out of range
return false;
}
} else if (c == ':') {
if (*address == ':') {
if (double_colons >= 0) {
// :: allowed once
return false;
}
if (*address != '\0' && *(address + 1) == ':') {
// ::: not allowed
return false;
}
// remember location
double_colons = colons + !!acc;
address++;
} else if (*address == '\0') {
// can't end with a single colon
return false;
}
if (colons == 7) {
// too many separators
return false;
}
_address.bytes[colons * 2] = acc >> 8;
_address.bytes[colons * 2 + 1] = acc & 0xff;
colons++;
acc = 0;
} else if (c == '%') {
// netif_index_to_name crashes on latest esp-idf
// _zone = netif_name_to_index(address);
// in the interim, we parse the suffix as a zone number
while ((*address != '\0') && (!isdigit(*address))) { // skip all non-digit after '%'
address++;
}
_zone = atol(address) + 1; // increase by one by convention, so we can have zone '0'
while (*address != '\0') {
address++;
}
} else {
// Invalid char
return false;
}
}
if (double_colons == -1 && colons != 7) {
// Too few separators
return false;
}
if (double_colons > -1 && colons > 6) {
// Too many segments (double colon must be at least one zero field)
return false;
}
_address.bytes[colons * 2] = acc >> 8;
_address.bytes[colons * 2 + 1] = acc & 0xff;
colons++;
if (double_colons != -1) {
for (int i = colons * 2 - double_colons * 2 - 1; i >= 0; i--) {
_address.bytes[16 - colons * 2 + double_colons * 2 + i] = _address.bytes[double_colons * 2 + i];
}
for (int i = double_colons * 2; i < 16 - colons * 2 + double_colons * 2; i++) {
_address.bytes[i] = 0;
}
}
_type = IPv6;
return true;
}
IPAddress &IPAddress::operator=(const uint8_t *address) {
// IPv4 only conversion from byte pointer
_type = IPv4;
memset(_address.bytes, 0, sizeof(_address.bytes));
memcpy(&_address.bytes[IPADDRESS_V4_BYTES_INDEX], address, sizeof(uint32_t));
return *this;
}
IPAddress &IPAddress::operator=(const char *address) {
fromString(address);
return *this;
}
IPAddress &IPAddress::operator=(uint32_t address) {
// IPv4 conversion
// See note on conversion/comparison and uint32_t
_type = IPv4;
memset(_address.bytes, 0, sizeof(_address.bytes));
_address.dword[IPADDRESS_V4_DWORD_INDEX] = address;
return *this;
}
IPAddress &IPAddress::operator=(const IPAddress &address) {
_type = address.type();
_zone = address.zone();
memcpy(_address.bytes, address._address.bytes, sizeof(_address.bytes));
return *this;
}
bool IPAddress::operator==(const IPAddress &addr) const {
return (addr._type == _type) && (_type == IPType::IPv4 ? addr._address.dword[IPADDRESS_V4_DWORD_INDEX] == _address.dword[IPADDRESS_V4_DWORD_INDEX] : memcmp(addr._address.bytes, _address.bytes, sizeof(_address.bytes)) == 0);
}
bool IPAddress::operator==(const uint8_t *addr) const {
// IPv4 only comparison to byte pointer
// Can't support IPv6 as we know our type, but not the length of the pointer
return _type == IPv4 && memcmp(addr, &_address.bytes[IPADDRESS_V4_BYTES_INDEX], sizeof(uint32_t)) == 0;
}
uint8_t IPAddress::operator[](int index) const {
if (_type == IPv4) {
return _address.bytes[IPADDRESS_V4_BYTES_INDEX + index];
}
return _address.bytes[index];
}
uint8_t &IPAddress::operator[](int index) {
if (_type == IPv4) {
return _address.bytes[IPADDRESS_V4_BYTES_INDEX + index];
}
return _address.bytes[index];
}
size_t IPAddress::printTo(Print &p) const {
return printTo(p, false);
}
size_t IPAddress::printTo(Print &p, bool includeZone) const {
size_t n = 0;
if (_type == IPv6) {
// IPv6 IETF canonical format: compress left-most longest run of two or more zero fields, lower case
int8_t longest_start = -1;
int8_t longest_length = 1;
int8_t current_start = -1;
int8_t current_length = 0;
for (int8_t f = 0; f < 8; f++) {
if (_address.bytes[f * 2] == 0 && _address.bytes[f * 2 + 1] == 0) {
if (current_start == -1) {
current_start = f;
current_length = 1;
} else {
current_length++;
}
if (current_length > longest_length) {
longest_start = current_start;
longest_length = current_length;
}
} else {
current_start = -1;
}
}
for (int f = 0; f < 8; f++) {
if (f < longest_start || f >= longest_start + longest_length) {
uint8_t c1 = _address.bytes[f * 2] >> 4;
uint8_t c2 = _address.bytes[f * 2] & 0xf;
uint8_t c3 = _address.bytes[f * 2 + 1] >> 4;
uint8_t c4 = _address.bytes[f * 2 + 1] & 0xf;
if (c1 > 0) {
n += p.print((char)(c1 < 10 ? '0' + c1 : 'a' + c1 - 10));
}
if (c1 > 0 || c2 > 0) {
n += p.print((char)(c2 < 10 ? '0' + c2 : 'a' + c2 - 10));
}
if (c1 > 0 || c2 > 0 || c3 > 0) {
n += p.print((char)(c3 < 10 ? '0' + c3 : 'a' + c3 - 10));
}
n += p.print((char)(c4 < 10 ? '0' + c4 : 'a' + c4 - 10));
if (f < 7) {
n += p.print(':');
}
} else if (f == longest_start) {
if (longest_start == 0) {
n += p.print(':');
}
n += p.print(':');
}
}
// add a zone if zone-id is non-zero (causes exception on recent IDF builds)
// if (_zone > 0 && includeZone) {
// n += p.print('%');
// char if_name[NETIF_NAMESIZE];
// netif_index_to_name(_zone, if_name);
// n += p.print(if_name);
// }
// In the interim, we just output the index number
if (_zone > 0 && includeZone) {
n += p.print('%');
// look for the interface name
for (netif *intf = netif_list; intf != nullptr; intf = intf->next) {
if (_zone - 1 == intf->num) {
n += p.print(intf->name[0]);
n += p.print(intf->name[1]);
break;
}
}
n += p.print(_zone - 1);
}
return n;
}
// IPv4
for (int i = 0; i < 3; i++) {
n += p.print(_address.bytes[IPADDRESS_V4_BYTES_INDEX + i], DEC);
n += p.print('.');
}
n += p.print(_address.bytes[IPADDRESS_V4_BYTES_INDEX + 3], DEC);
return n;
}
IPAddress::IPAddress(const ip_addr_t *addr) {
from_ip_addr_t(addr);
}
void IPAddress::to_ip_addr_t(ip_addr_t *addr) const {
#if CONFIG_LWIP_IPV6
if (_type == IPv6) {
addr->type = IPADDR_TYPE_V6;
addr->u_addr.ip6.addr[0] = _address.dword[0];
addr->u_addr.ip6.addr[1] = _address.dword[1];
addr->u_addr.ip6.addr[2] = _address.dword[2];
addr->u_addr.ip6.addr[3] = _address.dword[3];
#if LWIP_IPV6_SCOPES
addr->u_addr.ip6.zone = _zone;
#endif /* LWIP_IPV6_SCOPES */
} else {
addr->type = IPADDR_TYPE_V4;
addr->u_addr.ip4.addr = _address.dword[IPADDRESS_V4_DWORD_INDEX];
}
#else
addr->addr = _address.dword[IPADDRESS_V4_DWORD_INDEX];
#endif
}
IPAddress &IPAddress::from_ip_addr_t(const ip_addr_t *addr) {
#if CONFIG_LWIP_IPV6
if (addr->type == IPADDR_TYPE_V6) {
_type = IPv6;
_address.dword[0] = addr->u_addr.ip6.addr[0];
_address.dword[1] = addr->u_addr.ip6.addr[1];
_address.dword[2] = addr->u_addr.ip6.addr[2];
_address.dword[3] = addr->u_addr.ip6.addr[3];
#if LWIP_IPV6_SCOPES
_zone = addr->u_addr.ip6.zone;
#endif /* LWIP_IPV6_SCOPES */
} else {
#endif
_type = IPv4;
memset(_address.bytes, 0, sizeof(_address.bytes));
#if CONFIG_LWIP_IPV6
_address.dword[IPADDRESS_V4_DWORD_INDEX] = addr->u_addr.ip4.addr;
#else
_address.dword[IPADDRESS_V4_DWORD_INDEX] = addr->addr;
#endif
#if CONFIG_LWIP_IPV6
}
#endif
return *this;
}
#if CONFIG_LWIP_IPV6
esp_ip6_addr_type_t IPAddress::addr_type() const {
if (_type != IPv6) {
return ESP_IP6_ADDR_IS_UNKNOWN;
}
ip_addr_t addr;
to_ip_addr_t(&addr);
return esp_netif_ip6_get_addr_type((esp_ip6_addr_t *)(&(addr.u_addr.ip6)));
}
#endif
#if CONFIG_LWIP_IPV6
const IPAddress IN6ADDR_ANY(IPv6);
#endif
const IPAddress INADDR_NONE(0, 0, 0, 0);
+140
View File
@@ -0,0 +1,140 @@
/*
IPAddress.h - Base class that provides IPAddress
Copyright (c) 2011 Adrian McEwen. All right 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 <stdint.h>
#include "Printable.h"
#include "WString.h"
#include "lwip/ip_addr.h"
#include "esp_netif_ip_addr.h"
#include "sdkconfig.h"
#define IPADDRESS_V4_BYTES_INDEX 12
#define IPADDRESS_V4_DWORD_INDEX 3
// A class to make it easier to handle and pass around IP addresses
enum IPType {
IPv4,
IPv6
};
class IPAddress : public Printable {
private:
union {
uint8_t bytes[16];
uint32_t dword[4];
} _address;
IPType _type;
uint8_t _zone;
// Access the raw byte array containing the address. Because this returns a pointer
// to the internal structure rather than a copy of the address this function should only
// be used when you know that the usage of the returned uint8_t* will be transient and not
// stored.
uint8_t *raw_address() {
return _type == IPv4 ? &_address.bytes[IPADDRESS_V4_BYTES_INDEX] : _address.bytes;
}
public:
// Constructors
// Default IPv4
IPAddress();
IPAddress(IPType ip_type);
IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
IPAddress(
uint8_t o1, uint8_t o2, uint8_t o3, uint8_t o4, uint8_t o5, uint8_t o6, uint8_t o7, uint8_t o8, uint8_t o9, uint8_t o10, uint8_t o11, uint8_t o12,
uint8_t o13, uint8_t o14, uint8_t o15, uint8_t o16, uint8_t z = 0
);
// IPv4; see implementation note
IPAddress(uint32_t address);
// Default IPv4
IPAddress(const uint8_t *address);
IPAddress(IPType ip_type, const uint8_t *address, uint8_t z = 0);
// If IPv4 fails tries IPv6 see fromString function
IPAddress(const char *address);
IPAddress(const IPAddress &address);
bool fromString(const char *address);
bool fromString(const String &address) {
return fromString(address.c_str());
}
// Overloaded cast operator to allow IPAddress objects to be used where a uint32_t is expected
// NOTE: IPv4 only; see implementation note
operator uint32_t() const {
return _type == IPv4 ? _address.dword[IPADDRESS_V4_DWORD_INDEX] : 0;
};
bool operator==(const IPAddress &addr) const;
bool operator!=(const IPAddress &addr) const {
return !(*this == addr);
};
// NOTE: IPv4 only; we don't know the length of the pointer
bool operator==(const uint8_t *addr) const;
// Overloaded index operator to allow getting and setting individual octets of the address
uint8_t operator[](int index) const;
uint8_t &operator[](int index);
// Overloaded copy operators to allow initialization of IPAddress objects from other types
// NOTE: IPv4 only
IPAddress &operator=(const uint8_t *address);
// NOTE: IPv4 only; see implementation note
IPAddress &operator=(uint32_t address);
// If IPv4 fails tries IPv6 see fromString function
IPAddress &operator=(const char *address);
IPAddress &operator=(const IPAddress &address);
virtual size_t printTo(Print &p) const;
String toString(bool includeZone = false) const;
IPType type() const {
return _type;
}
// Espresif LwIP conversions
IPAddress(const ip_addr_t *addr);
void to_ip_addr_t(ip_addr_t *addr) const;
IPAddress &from_ip_addr_t(const ip_addr_t *addr);
#if CONFIG_LWIP_IPV6
esp_ip6_addr_type_t addr_type() const;
#endif
uint8_t zone() const {
return (type() == IPv6) ? _zone : 0;
}
size_t printTo(Print &p, bool includeZone) const;
friend class UDP;
friend class Client;
friend class Server;
friend class EthernetClass;
friend class DhcpClass;
friend class DNSClient;
protected:
bool fromString4(const char *address);
bool fromString6(const char *address);
};
extern const IPAddress IN6ADDR_ANY;
extern const IPAddress INADDR_NONE;
+87
View File
@@ -0,0 +1,87 @@
/*
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
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 "HEXBuilder.h"
#include "MD5Builder.h"
void MD5Builder::begin(void) {
memset(_buf, 0x00, ESP_ROM_MD5_DIGEST_LEN);
esp_rom_md5_init(&_ctx);
}
void MD5Builder::add(const uint8_t *data, size_t len) {
esp_rom_md5_update(&_ctx, data, len);
}
bool MD5Builder::addStream(Stream &stream, const size_t maxLen) {
const int buf_size = 512;
int maxLengthLeft = maxLen;
uint8_t *buf = (uint8_t *)malloc(buf_size);
if (!buf) {
return false;
}
int bytesAvailable = stream.available();
while ((bytesAvailable > 0) && (maxLengthLeft > 0)) {
// determine number of bytes to read
int readBytes = bytesAvailable;
if (readBytes > maxLengthLeft) {
readBytes = maxLengthLeft; // read only until max_len
}
if (readBytes > buf_size) {
readBytes = buf_size; // not read more the buffer can handle
}
// read data and check if we got something
int numBytesRead = stream.readBytes(buf, readBytes);
if (numBytesRead < 1) {
free(buf);
return false;
}
// Update MD5 with buffer payload
esp_rom_md5_update(&_ctx, buf, numBytesRead);
// update available number of bytes
maxLengthLeft -= numBytesRead;
bytesAvailable = stream.available();
}
free(buf);
return true;
}
void MD5Builder::calculate(void) {
esp_rom_md5_final(_buf, &_ctx);
}
void MD5Builder::getBytes(uint8_t *output) {
memcpy(output, _buf, ESP_ROM_MD5_DIGEST_LEN);
}
void MD5Builder::getChars(char *output) {
bytes2hex(output, ESP_ROM_MD5_DIGEST_LEN * 2 + 1, _buf, ESP_ROM_MD5_DIGEST_LEN);
}
String MD5Builder::toString(void) {
char out[(ESP_ROM_MD5_DIGEST_LEN * 2) + 1];
getChars(out);
return String(out);
}
+52
View File
@@ -0,0 +1,52 @@
/*
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
This file is part of the esp32 core for Arduino environment.
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
Modified 10 Jan 2024 by Lucas Saavedra Vaz (Use abstract class HashBuilder)
*/
#ifndef MD5Builder_h
#define MD5Builder_h
#include <WString.h>
#include <Stream.h>
#include "esp_system.h"
#include "esp_rom_md5.h"
#include "HashBuilder.h"
class MD5Builder : public HashBuilder {
private:
md5_context_t _ctx;
uint8_t _buf[ESP_ROM_MD5_DIGEST_LEN];
public:
using HashBuilder::add;
void begin(void) override;
void add(const uint8_t *data, size_t len) override;
bool addStream(Stream &stream, const size_t maxLen) override;
void calculate(void) override;
void getBytes(uint8_t *output) override;
void getChars(char *output) override;
String toString(void) override;
size_t getHashSize() const override {
return ESP_ROM_MD5_DIGEST_LEN;
}
};
#endif
+246
View File
@@ -0,0 +1,246 @@
#include <MacAddress.h>
#include <stdio.h>
#include <Print.h>
//Default constructor, blank mac address.
MacAddress::MacAddress() : MacAddress(MAC6) {}
MacAddress::MacAddress(MACType mac_type) {
_type = mac_type;
memset(_mac.bytes, 0, sizeof(_mac.bytes));
}
MacAddress::MacAddress(MACType mac_type, uint64_t mac) {
_type = mac_type;
_mac.val = mac;
}
MacAddress::MacAddress(MACType mac_type, const uint8_t *macbytearray) {
_type = mac_type;
memset(_mac.bytes, 0, sizeof(_mac.bytes));
if (_type == MAC6) {
memcpy(_mac.bytes, macbytearray, 6);
} else {
memcpy(_mac.bytes, macbytearray, 8);
}
}
MacAddress::MacAddress(const char *macstr) {
fromString(macstr);
}
MacAddress::MacAddress(const String &macstr) {
fromString(macstr.c_str());
}
#ifdef __GXX_EXPERIMENTAL_CXX0X__
MacAddress::MacAddress(std::initializer_list<uint8_t> list) {
size_t size = list.size();
memset(_mac.bytes, 0, sizeof(_mac.bytes));
if (size == 6) {
_type = MAC6;
} else if (size == 8) {
_type = MAC8;
} else {
// Default to MAC6 and keep the rest of the bytes as 0
_type = MAC6;
return;
}
memcpy(_mac.bytes, list.begin(), size);
}
#endif
MacAddress::MacAddress(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5, uint8_t b6) {
_type = MAC6;
memset(_mac.bytes, 0, sizeof(_mac.bytes));
_mac.bytes[0] = b1;
_mac.bytes[1] = b2;
_mac.bytes[2] = b3;
_mac.bytes[3] = b4;
_mac.bytes[4] = b5;
_mac.bytes[5] = b6;
}
MacAddress::MacAddress(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5, uint8_t b6, uint8_t b7, uint8_t b8) {
_type = MAC8;
_mac.bytes[0] = b1;
_mac.bytes[1] = b2;
_mac.bytes[2] = b3;
_mac.bytes[3] = b4;
_mac.bytes[4] = b5;
_mac.bytes[5] = b6;
_mac.bytes[6] = b7;
_mac.bytes[7] = b8;
}
//Parse user entered string into MAC address
bool MacAddress::fromString(const char *buf) {
if (strlen(buf) == 17) {
return fromString6(buf);
} else if (strlen(buf) == 23) {
return fromString8(buf);
}
return false;
}
//Parse user entered string into MAC address
bool MacAddress::fromString6(const char *buf) {
char cs[18]; // 17 + 1 for null terminator
char *token;
char *next; //Unused but required
int i;
strncpy(cs, buf, sizeof(cs) - 1); //strtok modifies the buffer: copy to working buffer.
for (i = 0; i < 6; i++) {
token = strtok((i == 0) ? cs : NULL, ":"); //Find first or next token
if (!token) { //No more tokens found
return false;
}
_mac.bytes[i] = strtol(token, &next, 16);
}
_type = MAC6;
return true;
}
bool MacAddress::fromString8(const char *buf) {
char cs[24]; // 23 + 1 for null terminator
char *token;
char *next; //Unused but required
int i;
strncpy(cs, buf, sizeof(cs) - 1); //strtok modifies the buffer: copy to working buffer.
for (i = 0; i < 8; i++) {
token = strtok((i == 0) ? cs : NULL, ":"); //Find first or next token
if (!token) { //No more tokens found
return false;
}
_mac.bytes[i] = strtol(token, &next, 16);
}
_type = MAC8;
return true;
}
//Copy MAC into byte array
void MacAddress::toBytes(uint8_t *buf) {
if (_type == MAC6) {
memcpy(buf, _mac.bytes, 6);
} else {
memcpy(buf, _mac.bytes, sizeof(_mac.bytes));
}
}
//Print MAC address into a C string.
//MAC: Buffer must be at least 18 chars
int MacAddress::toString(char *buf) {
if (_type == MAC6) {
return sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X", _mac.bytes[0], _mac.bytes[1], _mac.bytes[2], _mac.bytes[3], _mac.bytes[4], _mac.bytes[5]);
} else {
return sprintf(
buf, "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", _mac.bytes[0], _mac.bytes[1], _mac.bytes[2], _mac.bytes[3], _mac.bytes[4], _mac.bytes[5], _mac.bytes[6],
_mac.bytes[7]
);
}
}
String MacAddress::toString() const {
uint8_t bytes = (_type == MAC6) ? 18 : 24;
char buf[bytes];
if (_type == MAC6) {
snprintf(buf, sizeof(buf), "%02X:%02X:%02X:%02X:%02X:%02X", _mac.bytes[0], _mac.bytes[1], _mac.bytes[2], _mac.bytes[3], _mac.bytes[4], _mac.bytes[5]);
} else {
snprintf(
buf, sizeof(buf), "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", _mac.bytes[0], _mac.bytes[1], _mac.bytes[2], _mac.bytes[3], _mac.bytes[4], _mac.bytes[5],
_mac.bytes[6], _mac.bytes[7]
);
}
return String(buf);
}
uint64_t MacAddress::Value() {
return _mac.val;
}
//Allow getting individual octets of the address. e.g. uint8_t b0 = ma[0];
uint8_t MacAddress::operator[](int index) const {
index = EnforceIndexBounds(index);
return _mac.bytes[index];
}
//Allow setting individual octets of the address. e.g. ma[2] = 255;
uint8_t &MacAddress::operator[](int index) {
index = EnforceIndexBounds(index);
return _mac.bytes[index];
}
//Overloaded copy operator: init MacAddress object from byte array
MacAddress &MacAddress::operator=(const uint8_t *macbytearray) {
// 6-bytes MacAddress only
_type = MAC6;
memset(_mac.bytes, 0, sizeof(_mac.bytes));
memcpy(_mac.bytes, macbytearray, 6);
return *this;
}
//Overloaded copy operator: init MacAddress object from uint64_t
MacAddress &MacAddress::operator=(uint64_t macval) {
// 6-bytes MacAddress only
_type = MAC6;
_mac.val = macval;
return *this;
}
//Compare class to byte array
bool MacAddress::operator==(const uint8_t *macbytearray) const {
return !memcmp(_mac.bytes, macbytearray, 6);
}
//Allow comparing value of two classes
bool MacAddress::operator==(const MacAddress &mac2) const {
return _mac.val == mac2._mac.val;
}
//Type converter object to uint64_t [same as .Value()]
MacAddress::operator uint64_t() const {
return _mac.val;
}
//Type converter object to read only pointer to mac bytes. e.g. const uint8_t *ip_8 = ma;
MacAddress::operator const uint8_t *() const {
return _mac.bytes;
}
//Type converter object to read only pointer to mac value. e.g. const uint32_t *ip_64 = ma;
MacAddress::operator const uint64_t *() const {
return &_mac.val;
}
size_t MacAddress::printTo(Print &p) const {
uint8_t bytes = (_type == MAC6) ? 6 : 8;
size_t n = 0;
for (int i = 0; i < bytes; i++) {
if (i) {
n += p.print(':');
}
n += p.printf("%02X", _mac.bytes[i]);
}
return n;
}
//Bounds checking
int MacAddress::EnforceIndexBounds(int i) const {
if (i < 0) {
return 0;
}
if (_type == MAC6) {
if (i >= 6) {
return 5;
}
} else {
if (i >= 8) {
return 7;
}
}
return i;
}
+113
View File
@@ -0,0 +1,113 @@
//-----------------------------------------------------------------------------
// MacAddress.h - class to make it easier to handle BSSID and MAC addresses.
//
// Copyright 2022 David McCurley
// Modified by Espressif Systems 2024
//
// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//-----------------------------------------------------------------------------
#ifndef MacAddress_h
#define MacAddress_h
#include <stdint.h>
#include <WString.h>
#include <Printable.h>
#ifdef __GXX_EXPERIMENTAL_CXX0X__
#include <initializer_list>
#endif
enum MACType {
MAC6,
MAC8
};
// A class to make it easier to handle and pass around MAC addresses, supporting both 6-byte and 8-byte MAC addresses.
class MacAddress : public Printable {
private:
union {
uint8_t bytes[8];
uint64_t val;
} _mac;
MACType _type;
public:
//Default MAC6
MacAddress();
MacAddress(MACType mac_type);
MacAddress(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5, uint8_t b6);
MacAddress(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5, uint8_t b6, uint8_t b7, uint8_t b8);
MacAddress(MACType mac_type, uint64_t mac);
MacAddress(MACType mac_type, const uint8_t *macbytearray);
//Default MAC6
MacAddress(uint64_t mac) : MacAddress(MAC6, mac) {}
MacAddress(const uint8_t *macbytearray) : MacAddress(MAC6, macbytearray) {}
MacAddress(const char *macstr);
MacAddress(const String &macstr);
#ifdef __GXX_EXPERIMENTAL_CXX0X__
// Initializer list constructor for {0xAA, 0xBB, ...} syntax
// This has higher priority than String conversion, preventing ambiguity
MacAddress(std::initializer_list<uint8_t> list);
#endif
virtual ~MacAddress() {}
bool fromString(const char *buf);
bool fromString(const String &macstr) {
return fromString(macstr.c_str());
}
void toBytes(uint8_t *buf);
int toString(char *buf);
String toString() const;
uint64_t Value();
uint8_t operator[](int index) const;
uint8_t &operator[](int index);
//MAC6 only
MacAddress &operator=(const uint8_t *macbytearray);
MacAddress &operator=(uint64_t macval);
bool operator==(const uint8_t *macbytearray) const;
bool operator==(const MacAddress &mac2) const;
operator uint64_t() const;
operator const uint8_t *() const;
operator const uint64_t *() const;
virtual size_t printTo(Print &p) const;
// future use in Arduino Networking
/*
friend class EthernetClass;
friend class UDP;
friend class Client;
friend class Server;
friend class DhcpClass;
friend class DNSClient;
*/
protected:
bool fromString6(const char *buf);
bool fromString8(const char *buf);
private:
int EnforceIndexBounds(int i) const;
};
#endif
+345
View File
@@ -0,0 +1,345 @@
/*
Print.cpp - Base class that provides print() and println()
Copyright (c) 2008 David A. Mellis. All right 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
Modified 23 November 2006 by David A. Mellis
Modified December 2014 by Ivan Grokhotkov
Modified May 2015 by Michael C. Miller - ESP31B progmem support
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "Arduino.h"
#include "Print.h"
extern "C" {
#include "time.h"
}
// Public Methods //////////////////////////////////////////////////////////////
/* default implementation: may be overridden */
size_t Print::write(const uint8_t *buffer, size_t size) {
size_t n = 0;
while (size--) {
n += write(*buffer++);
}
return n;
}
size_t Print::vprintf(const char *format, va_list arg) {
char loc_buf[64];
char *temp = loc_buf;
va_list copy;
va_copy(copy, arg);
int len = vsnprintf(temp, sizeof(loc_buf), format, copy);
va_end(copy);
if (len < 0) {
va_end(arg);
return 0;
}
if (len >= (int)sizeof(loc_buf)) { // comparison of same sign type for the compiler
temp = (char *)malloc(len + 1);
if (temp == NULL) {
va_end(arg);
return 0;
}
len = vsnprintf(temp, len + 1, format, arg);
}
va_end(arg);
len = write((uint8_t *)temp, len);
if (temp != loc_buf) {
free(temp);
}
return len;
}
size_t Print::printf(const __FlashStringHelper *ifsh, ...) {
va_list arg;
va_start(arg, ifsh);
const char *format = (reinterpret_cast<const char *>(ifsh));
size_t ret = vprintf(format, arg);
va_end(arg);
return ret;
}
size_t Print::printf(const char *format, ...) {
va_list arg;
va_start(arg, format);
size_t ret = vprintf(format, arg);
va_end(arg);
return ret;
}
size_t Print::print(const String &s) {
return write(s.c_str(), s.length());
}
size_t Print::print(const char str[]) {
return write(str);
}
size_t Print::print(char c) {
return write(c);
}
size_t Print::print(unsigned char b, int base) {
return print((unsigned long)b, base);
}
size_t Print::print(int n, int base) {
return print((long)n, base);
}
size_t Print::print(unsigned int n, int base) {
return print((unsigned long)n, base);
}
size_t Print::print(long n, int base) {
int t = 0;
if (base == 10 && n < 0) {
t = print('-');
n = -n;
}
return printNumber(static_cast<unsigned long>(n), base) + t;
}
size_t Print::print(unsigned long n, int base) {
if (base == 0) {
return write(n);
} else {
return printNumber(n, base);
}
}
size_t Print::print(long long n, int base) {
int t = 0;
if (base == 10 && n < 0) {
t = print('-');
n = -n;
}
return printNumber(static_cast<unsigned long long>(n), base) + t;
}
size_t Print::print(unsigned long long n, int base) {
if (base == 0) {
return write(n);
} else {
return printNumber(n, base);
}
}
size_t Print::print(double n, int digits) {
return printFloat(n, digits);
}
size_t Print::print(const Printable &x) {
return x.printTo(*this);
}
size_t Print::print(struct tm *timeinfo, const char *format) {
const char *f = format;
if (!f) {
f = "%c";
}
char buf[64];
size_t written = strftime(buf, 64, f, timeinfo);
if (written == 0) {
return written;
}
return print(buf);
}
size_t Print::println(void) {
return print("\r\n");
}
size_t Print::println(const String &s) {
size_t n = print(s);
n += println();
return n;
}
size_t Print::println(const char c[]) {
size_t n = print(c);
n += println();
return n;
}
size_t Print::println(char c) {
size_t n = print(c);
n += println();
return n;
}
size_t Print::println(unsigned char b, int base) {
size_t n = print(b, base);
n += println();
return n;
}
size_t Print::println(int num, int base) {
size_t n = print(num, base);
n += println();
return n;
}
size_t Print::println(unsigned int num, int base) {
size_t n = print(num, base);
n += println();
return n;
}
size_t Print::println(long num, int base) {
size_t n = print(num, base);
n += println();
return n;
}
size_t Print::println(unsigned long num, int base) {
size_t n = print(num, base);
n += println();
return n;
}
size_t Print::println(long long num, int base) {
size_t n = print(num, base);
n += println();
return n;
}
size_t Print::println(unsigned long long num, int base) {
size_t n = print(num, base);
n += println();
return n;
}
size_t Print::println(double num, int digits) {
size_t n = print(num, digits);
n += println();
return n;
}
size_t Print::println(const Printable &x) {
size_t n = print(x);
n += println();
return n;
}
size_t Print::println(struct tm *timeinfo, const char *format) {
size_t n = print(timeinfo, format);
n += println();
return n;
}
// Private Methods /////////////////////////////////////////////////////////////
size_t Print::printNumber(unsigned long n, uint8_t base) {
char buf[8 * sizeof(n) + 1]; // Assumes 8-bit chars plus zero byte.
char *str = &buf[sizeof(buf) - 1];
*str = '\0';
// prevent crash if called with base == 1
if (base < 2) {
base = 10;
}
do {
char c = n % base;
n /= base;
*--str = c < 10 ? c + '0' : c + 'A' - 10;
} while (n);
return write(str);
}
size_t Print::printNumber(unsigned long long n, uint8_t base) {
char buf[8 * sizeof(n) + 1]; // Assumes 8-bit chars plus zero byte.
char *str = &buf[sizeof(buf) - 1];
*str = '\0';
// prevent crash if called with base == 1
if (base < 2) {
base = 10;
}
do {
auto m = n;
n /= base;
char c = m - base * n;
*--str = c < 10 ? c + '0' : c + 'A' - 10;
} while (n);
return write(str);
}
size_t Print::printFloat(double number, uint8_t digits) {
size_t n = 0;
if (isnan(number)) {
return print("nan");
}
if (isinf(number)) {
return print("inf");
}
if (number > 4294967040.0) {
return print("ovf"); // constant determined empirically
}
if (number < -4294967040.0) {
return print("ovf"); // constant determined empirically
}
// Handle negative numbers
if (number < 0.0) {
n += print('-');
number = -number;
}
// Round correctly so that print(1.999, 2) prints as "2.00"
double rounding = 0.5;
for (uint8_t i = 0; i < digits; ++i) {
rounding /= 10.0;
}
number += rounding;
// Extract the integer part of the number and print it
unsigned long int_part = (unsigned long)number;
double remainder = number - (double)int_part;
n += print(int_part);
// Print the decimal point, but only if there are digits beyond
if (digits > 0) {
n += print(".");
}
// Extract digits from the remainder one at a time
while (digits-- > 0) {
remainder *= 10.0;
int toPrint = int(remainder);
n += print(toPrint);
remainder -= toPrint;
}
return n;
}
+118
View File
@@ -0,0 +1,118 @@
/*
Print.h - Base class that provides print() and println()
Copyright (c) 2008 David A. Mellis. All right 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
*/
#ifndef Print_h
#define Print_h
#include <stdint.h>
#include <stdio.h>
#include <stddef.h>
#include "WString.h"
#include "Printable.h"
#define DEC 10
#define HEX 16
#define OCT 8
#define BIN 2
class Print {
private:
int write_error;
size_t printNumber(unsigned long, uint8_t);
size_t printNumber(unsigned long long, uint8_t);
size_t printFloat(double, uint8_t);
protected:
void setWriteError(int err = 1) {
write_error = err;
}
public:
Print() : write_error(0) {}
virtual ~Print() {}
int getWriteError() {
return write_error;
}
void clearWriteError() {
setWriteError(0);
}
virtual size_t write(uint8_t) = 0;
size_t write(const char *str) {
if (str == NULL) {
return 0;
}
return write((const uint8_t *)str, strlen(str));
}
virtual size_t write(const uint8_t *buffer, size_t size);
size_t write(const char *buffer, size_t size) {
return write((const uint8_t *)buffer, size);
}
size_t vprintf(const char *format, va_list arg);
size_t printf(const char *format, ...) __attribute__((format(printf, 2, 3)));
size_t printf(const __FlashStringHelper *ifsh, ...);
// add availableForWrite to make compatible with Arduino Print.h
// default to zero, meaning "a single write may block"
// should be overridden by subclasses with buffering
virtual int availableForWrite() {
return 0;
}
size_t print(const __FlashStringHelper *ifsh) {
return print(reinterpret_cast<const char *>(ifsh));
}
size_t print(const String &);
size_t print(const char[]);
size_t print(char);
size_t print(unsigned char, int = DEC);
size_t print(int, int = DEC);
size_t print(unsigned int, int = DEC);
size_t print(long, int = DEC);
size_t print(unsigned long, int = DEC);
size_t print(long long, int = DEC);
size_t print(unsigned long long, int = DEC);
size_t print(double, int = 2);
size_t print(const Printable &);
size_t print(struct tm *timeinfo, const char *format = NULL);
size_t println(const __FlashStringHelper *ifsh) {
return println(reinterpret_cast<const char *>(ifsh));
}
size_t println(const String &s);
size_t println(const char[]);
size_t println(char);
size_t println(unsigned char, int = DEC);
size_t println(int, int = DEC);
size_t println(unsigned int, int = DEC);
size_t println(long, int = DEC);
size_t println(unsigned long, int = DEC);
size_t println(long long, int = DEC);
size_t println(unsigned long long, int = DEC);
size_t println(double, int = 2);
size_t println(const Printable &);
size_t println(struct tm *timeinfo, const char *format = NULL);
size_t println(void);
virtual void flush() { /* Empty implementation for backward compatibility */ }
};
#endif
+39
View File
@@ -0,0 +1,39 @@
/*
Printable.h - Interface class that allows printing of complex types
Copyright (c) 2011 Adrian McEwen. All right 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
*/
#ifndef Printable_h
#define Printable_h
#include <stdlib.h>
class Print;
/** The Printable class provides a way for new classes to allow themselves to be printed.
By deriving from Printable and implementing the printTo method, it will then be possible
for users to print out instances of this class by passing them into the usual
Print::print and Print::println methods.
*/
class Printable {
public:
virtual ~Printable() {}
virtual size_t printTo(Print &p) const = 0;
};
#endif
+30
View File
@@ -0,0 +1,30 @@
/*
Server.h - Base class that provides Server
Copyright (c) 2011 Adrian McEwen. All right 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
*/
#ifndef server_h
#define server_h
#include "Print.h"
class Server : public Print {
public:
virtual void begin() = 0;
};
#endif
+318
View File
@@ -0,0 +1,318 @@
/*
Stream.cpp - adds parsing methods to Stream class
Copyright (c) 2008 David A. Mellis. All right 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
Created July 2011
parsing functions based on TextFinder library by Michael Margolis
findMulti/findUntil routines written by Jim Leonard/Xuth
*/
#include "Arduino.h"
#include "Stream.h"
#define PARSE_TIMEOUT 1000 // default number of milli-seconds to wait
// private method to read stream with timeout
int Stream::timedRead() {
int c;
_startMillis = millis();
do {
c = read();
if (c >= 0) {
return c;
}
} while (millis() - _startMillis < _timeout);
return -1; // -1 indicates timeout
}
// private method to peek stream with timeout
int Stream::timedPeek() {
int c;
_startMillis = millis();
do {
c = peek();
if (c >= 0) {
return c;
}
} while (millis() - _startMillis < _timeout);
return -1; // -1 indicates timeout
}
// returns peek of the next digit in the stream or -1 if timeout
// discards non-numeric characters
int Stream::peekNextDigit(LookaheadMode lookahead, bool detectDecimal) {
int c;
while (1) {
c = timedPeek();
if (c < 0 || c == '-' || (c >= '0' && c <= '9') || (detectDecimal && c == '.')) {
return c;
}
switch (lookahead) {
case SKIP_NONE: return -1; // Fail code.
case SKIP_WHITESPACE:
switch (c) {
case ' ':
case '\t':
case '\r':
case '\n': break;
default: return -1; // Fail code.
}
case SKIP_ALL: break;
}
read(); // discard non-numeric
}
}
// Public Methods
//////////////////////////////////////////////////////////////
void Stream::setTimeout(unsigned long timeout) // sets the maximum number of milliseconds to wait
{
_timeout = timeout;
}
// find returns true if the target string is found
bool Stream::find(const char *target) {
return findUntil(target, strlen(target), NULL, 0);
}
// reads data from the stream until the target string of given length is found
// returns true if target string is found, false if timed out
bool Stream::find(const char *target, size_t length) {
return findUntil(target, length, NULL, 0);
}
// as find but search ends if the terminator string is found
bool Stream::findUntil(const char *target, const char *terminator) {
return findUntil(target, strlen(target), terminator, strlen(terminator));
}
// reads data from the stream until the target string of the given length is found
// search terminated if the terminator string is found
// returns true if target string is found, false if terminated or timed out
bool Stream::findUntil(const char *target, size_t targetLen, const char *terminator, size_t termLen) {
if (terminator == NULL) {
MultiTarget t[1] = {{target, targetLen, 0}};
return findMulti(t, 1) == 0;
} else {
MultiTarget t[2] = {{target, targetLen, 0}, {terminator, termLen, 0}};
return findMulti(t, 2) == 0;
}
}
// returns the first valid (long) integer value from the current position.
// lookahead determines how parseInt looks ahead in the stream.
// See LookaheadMode enumeration at the top of the file.
// Lookahead is terminated by the first character that is not a valid part of an integer.
// Once parsing commences, 'ignore' will be skipped in the stream.
long Stream::parseInt(LookaheadMode lookahead, char ignore) {
bool isNegative = false;
long value = 0;
int c;
c = peekNextDigit(lookahead, false);
// ignore non numeric leading characters
if (c < 0) {
return 0; // zero returned if timeout
}
do {
if ((char)c == ignore)
; // ignore this character
else if (c == '-') {
isNegative = true;
} else if (c >= '0' && c <= '9') { // is c a digit?
value = value * 10 + c - '0';
}
read(); // consume the character we got with peek
c = timedPeek();
} while ((c >= '0' && c <= '9') || (char)c == ignore);
if (isNegative) {
value = -value;
}
return value;
}
// as parseInt but returns a floating point value
float Stream::parseFloat(LookaheadMode lookahead, char ignore) {
bool isNegative = false;
bool isFraction = false;
double value = 0.0;
int c;
double fraction = 1.0;
c = peekNextDigit(lookahead, true);
// ignore non numeric leading characters
if (c < 0) {
return 0; // zero returned if timeout
}
do {
if ((char)c == ignore)
; // ignore
else if (c == '-') {
isNegative = true;
} else if (c == '.') {
isFraction = true;
} else if (c >= '0' && c <= '9') { // is c a digit?
if (isFraction) {
fraction *= 0.1;
value = value + fraction * (c - '0');
} else {
value = value * 10 + c - '0';
}
}
read(); // consume the character we got with peek
c = timedPeek();
} while ((c >= '0' && c <= '9') || (c == '.' && !isFraction) || (char)c == ignore);
if (isNegative) {
value = -value;
}
return value;
}
// read characters from stream into buffer
// terminates if length characters have been read, or timeout (see setTimeout)
// returns the number of characters placed in the buffer
// the buffer is NOT null terminated.
//
size_t Stream::readBytes(char *buffer, size_t length) {
size_t count = 0;
while (count < length) {
int c = timedRead();
if (c < 0) {
break;
}
*buffer++ = (char)c;
count++;
}
return count;
}
// as readBytes with terminator character
// terminates if length characters have been read, timeout, or if the terminator character detected
// returns the number of characters placed in the buffer (0 means no valid data found)
size_t Stream::readBytesUntil(char terminator, char *buffer, size_t length) {
size_t index = 0;
while (index < length) {
int c = timedRead();
if (c < 0 || (char)c == terminator) {
break;
}
*buffer++ = (char)c;
index++;
}
return index; // return number of characters, not including null terminator
}
String Stream::readString() {
String ret;
int c = timedRead();
while (c >= 0) {
ret += (char)c;
c = timedRead();
}
return ret;
}
String Stream::readStringUntil(char terminator) {
String ret;
int c = timedRead();
while (c >= 0 && (char)c != terminator) {
ret += (char)c;
c = timedRead();
}
return ret;
}
int Stream::findMulti(struct Stream::MultiTarget *targets, int tCount) {
// any zero length target string automatically matches and would make
// a mess of the rest of the algorithm.
for (struct MultiTarget *t = targets; t < targets + tCount; ++t) {
if (t->len <= 0) {
return t - targets;
}
}
while (1) {
int c = timedRead();
if (c < 0) {
return -1;
}
for (struct MultiTarget *t = targets; t < targets + tCount; ++t) {
// the simple case is if we match, deal with that first.
if ((char)c == t->str[t->index]) {
if (++t->index == t->len) {
return t - targets;
} else {
continue;
}
}
// if not we need to walk back and see if we could have matched further
// down the stream (ie '1112' doesn't match the first position in '11112'
// but it will match the second position so we can't just reset the current
// index to 0 when we find a mismatch.
if (t->index == 0) {
continue;
}
int origIndex = t->index;
do {
--t->index;
// first check if current char works against the new current index
if ((char)c != t->str[t->index]) {
continue;
}
// if it's the only char then we're good, nothing more to check
if (t->index == 0) {
t->index++;
break;
}
// otherwise we need to check the rest of the found string
int diff = origIndex - t->index;
size_t i;
for (i = 0; i < t->index; ++i) {
if (t->str[i] != t->str[i + diff]) {
break;
}
}
// if we successfully got through the previous loop then our current
// index is good.
if (i == t->index) {
t->index++;
break;
}
// otherwise we just try the next index
} while (t->index);
}
}
// unreachable
return -1;
}
+148
View File
@@ -0,0 +1,148 @@
/*
Stream.h - base class for character-based streams.
Copyright (c) 2010 David A. Mellis. All right 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
parsing functions based on TextFinder library by Michael Margolis
*/
#pragma once
#include <inttypes.h>
#include "Print.h"
// compatibility macros for testing
/*
#define getInt() parseInt()
#define getInt(ignore) parseInt(ignore)
#define getFloat() parseFloat()
#define getFloat(ignore) parseFloat(ignore)
#define getString( pre_string, post_string, buffer, length)
readBytesBetween( pre_string, terminator, buffer, length)
*/
// This enumeration provides the lookahead options for parseInt(), parseFloat()
// The rules set out here are used until either the first valid character is found
// or a time out occurs due to lack of input.
enum LookaheadMode {
SKIP_ALL, // All invalid characters are ignored.
SKIP_NONE, // Nothing is skipped, and the stream is not touched unless the first waiting character is valid.
SKIP_WHITESPACE // Only tabs, spaces, line feeds & carriage returns are skipped.
};
#define NO_IGNORE_CHAR '\x01' // a char not found in a valid ASCII numeric field
class Stream : public Print {
protected:
unsigned long _timeout; // number of milliseconds to wait for the next char before aborting timed read
unsigned long _startMillis; // used for timeout measurement
int timedRead(); // private method to read stream with timeout
int timedPeek(); // private method to peek stream with timeout
int peekNextDigit(LookaheadMode lookahead, bool detectDecimal); // returns the next numeric digit in the stream or -1 if timeout
public:
virtual int available() = 0;
virtual int read() = 0;
virtual int peek() = 0;
Stream() {
_timeout = 1000;
}
// parsing methods
void setTimeout(unsigned long timeout); // sets maximum milliseconds to wait for stream data, default is 1 second
unsigned long getTimeout(void) {
return _timeout;
}
bool find(const char *target); // reads data from the stream until the target string is found
bool find(const uint8_t *target) {
return find((const char *)target);
}
// returns true if target string is found, false if timed out (see setTimeout)
bool find(const char *target, size_t length); // reads data from the stream until the target string of given length is found
bool find(const uint8_t *target, size_t length) {
return find((const char *)target, length);
}
// returns true if target string is found, false if timed out
bool find(char target) {
return find(&target, 1);
}
bool findUntil(const char *target, const char *terminator); // as find but search ends if the terminator string is found
bool findUntil(const uint8_t *target, const char *terminator) {
return findUntil((const char *)target, terminator);
}
bool findUntil(const char *target, size_t targetLen, const char *terminate, size_t termLen); // as above but search ends if the terminate string is found
bool findUntil(const uint8_t *target, size_t targetLen, const char *terminate, size_t termLen) {
return findUntil((const char *)target, targetLen, terminate, termLen);
}
long parseInt(LookaheadMode lookahead = SKIP_ALL, char ignore = NO_IGNORE_CHAR);
// returns the first valid (long) integer value from the current position.
// lookahead determines how parseInt looks ahead in the stream.
// See LookaheadMode enumeration at the top of the file.
// Lookahead is terminated by the first character that is not a valid part of an integer.
// Once parsing commences, 'ignore' will be skipped in the stream.
float parseFloat(LookaheadMode lookahead = SKIP_ALL, char ignore = NO_IGNORE_CHAR);
// float version of parseInt
virtual size_t readBytes(char *buffer, size_t length); // read chars from stream into buffer
virtual size_t readBytes(uint8_t *buffer, size_t length) {
return readBytes((char *)buffer, length);
}
// terminates if length characters have been read or timeout (see setTimeout)
// returns the number of characters placed in the buffer (0 means no valid data found)
size_t readBytesUntil(char terminator, char *buffer, size_t length); // as readBytes with terminator character
size_t readBytesUntil(char terminator, uint8_t *buffer, size_t length) {
return readBytesUntil(terminator, (char *)buffer, length);
}
// terminates if length characters have been read, timeout, or if the terminator character detected
// returns the number of characters placed in the buffer (0 means no valid data found)
// Arduino String functions to be added here
virtual String readString();
String readStringUntil(char terminator);
protected:
long parseInt(char ignore) {
return parseInt(SKIP_ALL, ignore);
}
float parseFloat(char ignore) {
return parseFloat(SKIP_ALL, ignore);
}
// These overload exists for compatibility with any class that has derived
// Stream and used parseFloat/Int with a custom ignore character. To keep
// the public API simple, these overload remains protected.
struct MultiTarget {
const char *str; // string you're searching for
size_t len; // length of string you're searching for
size_t index; // index used by the search routine.
};
// This allows you to search for an arbitrary number of strings.
// Returns index of the target that is found first or -1 if timeout occurs.
int findMulti(struct MultiTarget *targets, int tCount);
};
#undef NO_IGNORE_CHAR
+64
View File
@@ -0,0 +1,64 @@
/**
StreamString.cpp
Copyright (c) 2015 Markus Sattler. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
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 "StreamString.h"
size_t StreamString::write(const uint8_t *data, size_t size) {
if (size && data) {
const unsigned int newlen = length() + size;
if (reserve(newlen + 1)) {
memcpy((void *)(wbuffer() + len()), (const void *)data, size);
setLen(newlen);
*(wbuffer() + newlen) = 0x00; // add null for string end
return size;
}
}
return 0;
}
size_t StreamString::write(uint8_t data) {
return concat((char)data);
}
int StreamString::available() {
return length();
}
int StreamString::read() {
if (length()) {
char c = charAt(0);
remove(0, 1);
return c;
}
return -1;
}
int StreamString::peek() {
if (length()) {
char c = charAt(0);
return c;
}
return -1;
}
void StreamString::flush() {}
+38
View File
@@ -0,0 +1,38 @@
/**
StreamString.h
Copyright (c) 2015 Markus Sattler. 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
*/
#ifndef STREAMSTRING_H_
#define STREAMSTRING_H_
#include "Stream.h"
#include "WString.h"
class StreamString : public Stream, public String {
public:
size_t write(const uint8_t *buffer, size_t size) override;
size_t write(uint8_t data) override;
int available() override;
int read() override;
int peek() override;
void flush() override;
};
#endif /* STREAMSTRING_H_ */
+152
View File
@@ -0,0 +1,152 @@
#include <Arduino.h>
#include "esp32-hal-ledc.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#if SOC_LEDC_SUPPORTED
static TaskHandle_t _tone_task = NULL;
static QueueHandle_t _tone_queue = NULL;
static int8_t _pin = -1;
static uint8_t _channel = 255;
typedef enum {
TONE_START,
TONE_END
} tone_cmd_t;
typedef struct {
tone_cmd_t tone_cmd;
uint8_t pin;
unsigned int frequency;
unsigned long duration;
} tone_msg_t;
#ifdef SOC_LEDC_SUPPORT_HS_MODE
#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM << 1)
#else
#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM)
#endif
static void tone_task(void *) {
tone_msg_t tone_msg;
while (1) {
xQueueReceive(_tone_queue, &tone_msg, portMAX_DELAY);
switch (tone_msg.tone_cmd) {
case TONE_START:
log_d("Task received from queue TONE_START: pin=%d, frequency=%u Hz, duration=%lu ms", tone_msg.pin, tone_msg.frequency, tone_msg.duration);
if (_pin == -1) {
bool ret = true;
if (_channel == 255) {
ret = ledcAttach(tone_msg.pin, tone_msg.frequency, 10);
} else {
ret = ledcAttachChannel(tone_msg.pin, tone_msg.frequency, 10, _channel);
}
if (!ret) {
log_e("Tone start failed");
break;
}
_pin = tone_msg.pin;
}
ledcWriteTone(tone_msg.pin, tone_msg.frequency);
if (tone_msg.duration) {
delay(tone_msg.duration);
ledcWriteTone(tone_msg.pin, 0);
}
break;
case TONE_END:
log_d("Task received from queue TONE_END: pin=%d", tone_msg.pin);
ledcWriteTone(tone_msg.pin, 0);
ledcDetach(tone_msg.pin);
_pin = -1;
break;
default:; // do nothing
} // switch
} // infinite loop
}
static int tone_init() {
if (_tone_queue == NULL) {
log_v("Creating tone queue");
_tone_queue = xQueueCreate(128, sizeof(tone_msg_t));
if (_tone_queue == NULL) {
log_e("Could not create tone queue");
return 0; // ERR
}
log_v("Tone queue created");
}
if (_tone_task == NULL) {
log_v("Creating tone task");
xTaskCreate(
tone_task, // Function to implement the task
"toneTask", // Name of the task
3500, // Stack size in words
NULL, // Task input parameter
10, // Priority of the task must be higher than Arduino task
&_tone_task // Task handle.
);
if (_tone_task == NULL) {
log_e("Could not create tone task");
return 0; // ERR
}
log_v("Tone task created");
}
return 1; // OK
}
void noTone(uint8_t pin) {
log_d("noTone was called");
if (_pin == pin) {
if (tone_init()) {
tone_msg_t tone_msg = {
.tone_cmd = TONE_END,
.pin = pin,
.frequency = 0, // Ignored
.duration = 0, // Ignored
};
xQueueReset(_tone_queue); // clear queue
xQueueSend(_tone_queue, &tone_msg, portMAX_DELAY);
}
} else {
log_e("Tone is not running on given pin %d", pin);
}
}
// parameters:
// pin - pin number which will output the signal
// frequency - PWM frequency in Hz
// duration - time in ms - how long will the signal be outputted.
// If not provided, or 0 you must manually call noTone to end output
void tone(uint8_t pin, unsigned int frequency, unsigned long duration) {
log_d("pin=%d, frequency=%u Hz, duration=%lu ms", pin, frequency, duration);
if (_pin == -1 || _pin == pin) {
if (tone_init()) {
tone_msg_t tone_msg = {
.tone_cmd = TONE_START,
.pin = pin,
.frequency = frequency,
.duration = duration,
};
xQueueSend(_tone_queue, &tone_msg, portMAX_DELAY);
return;
}
} else {
log_e("Tone is still running on pin %d, call noTone(%d) first!", _pin, _pin);
return;
}
}
void setToneChannel(uint8_t channel) {
if (channel >= LEDC_CHANNELS) {
log_e("Channel %u is not available (maximum %u)!", channel, LEDC_CHANNELS);
return;
}
_channel = channel;
}
#endif /* SOC_LEDC_SUPPORTED */
+358
View File
@@ -0,0 +1,358 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "USB.h"
#if SOC_USB_OTG_SUPPORTED
#if CONFIG_TINYUSB_ENABLED
#include "pins_arduino.h"
#include "esp32-hal.h"
#include "esp32-hal-tinyusb.h"
#include "common/tusb_common.h"
#include "StreamString.h"
#include "rom/ets_sys.h"
#include "esp_mac.h"
#ifndef USB_VID
#define USB_VID USB_ESPRESSIF_VID
#endif
#ifndef USB_PID
#define USB_PID 0x0002
#endif
#ifndef USB_MANUFACTURER
#define USB_MANUFACTURER "Espressif Systems"
#endif
#ifndef USB_PRODUCT
#define USB_PRODUCT ARDUINO_BOARD
#endif
#ifndef USB_SERIAL
#if CONFIG_IDF_TARGET_ESP32S3
#define USB_SERIAL "__MAC__"
#else
#define USB_SERIAL "0"
#endif
#endif
#ifndef USB_WEBUSB_ENABLED
#define USB_WEBUSB_ENABLED false
#endif
#ifndef USB_WEBUSB_URL
#define USB_WEBUSB_URL "https://docs.espressif.com/projects/arduino-esp32/en/latest/_static/webusb.html"
#endif
#if CFG_TUD_DFU
__attribute__((weak)) uint16_t load_dfu_ota_descriptor(uint8_t *dst, uint8_t *itf) {
return 0;
}
#elif CFG_TUD_DFU_RUNTIME
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)
uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB DFU_RT");
uint8_t descriptor[TUD_DFU_RT_DESC_LEN] = {// Interface number, string index, attributes, detach timeout, transfer size */
TUD_DFU_RT_DESCRIPTOR(*itf, str_index, DFU_ATTRS, 700, 64)
};
*itf += 1;
memcpy(dst, descriptor, TUD_DFU_RT_DESC_LEN);
return TUD_DFU_RT_DESC_LEN;
}
#endif /* CFG_TUD_DFU_RUNTIME */
#if CFG_TUD_DFU_RUNTIME
// Invoked on DFU_DETACH request to reboot to the bootloader
void tud_dfu_runtime_reboot_to_dfu_cb(void) {
usb_persist_restart(RESTART_BOOTLOADER_DFU);
}
#endif /* CFG_TUD_DFU_RUNTIME */
ESP_EVENT_DEFINE_BASE(ARDUINO_USB_EVENTS);
static esp_event_loop_handle_t arduino_usb_event_loop_handle = NULL;
esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait) {
if (arduino_usb_event_loop_handle == NULL) {
return ESP_FAIL;
}
return esp_event_post_to(arduino_usb_event_loop_handle, event_base, event_id, event_data, event_data_size, ticks_to_wait);
}
esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg) {
if (arduino_usb_event_loop_handle == NULL) {
return ESP_FAIL;
}
return esp_event_handler_register_with(arduino_usb_event_loop_handle, event_base, event_id, event_handler, event_handler_arg);
}
static bool tinyusb_device_mounted = false;
static bool tinyusb_device_suspended = false;
// Invoked when device is mounted (configured)
void tud_mount_cb(void) {
tinyusb_device_mounted = true;
arduino_usb_event_data_t p;
p.suspend.remote_wakeup_en = 0;
arduino_usb_event_post(ARDUINO_USB_EVENTS, ARDUINO_USB_STARTED_EVENT, &p, sizeof(arduino_usb_event_data_t), portMAX_DELAY);
}
// Invoked when device is unmounted
void tud_umount_cb(void) {
tinyusb_device_mounted = false;
arduino_usb_event_data_t p;
p.suspend.remote_wakeup_en = 0;
arduino_usb_event_post(ARDUINO_USB_EVENTS, ARDUINO_USB_STOPPED_EVENT, &p, sizeof(arduino_usb_event_data_t), portMAX_DELAY);
}
// Invoked when usb bus is suspended
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
void tud_suspend_cb(bool remote_wakeup_en) {
tinyusb_device_suspended = true;
arduino_usb_event_data_t p;
p.suspend.remote_wakeup_en = remote_wakeup_en;
arduino_usb_event_post(ARDUINO_USB_EVENTS, ARDUINO_USB_SUSPEND_EVENT, &p, sizeof(arduino_usb_event_data_t), portMAX_DELAY);
}
// Invoked when usb bus is resumed
void tud_resume_cb(void) {
tinyusb_device_suspended = false;
arduino_usb_event_data_t p;
p.suspend.remote_wakeup_en = 0;
arduino_usb_event_post(ARDUINO_USB_EVENTS, ARDUINO_USB_RESUME_EVENT, &p, sizeof(arduino_usb_event_data_t), portMAX_DELAY);
}
ESPUSB::ESPUSB(size_t task_stack_size, uint8_t event_task_priority)
: vid(USB_VID), pid(USB_PID), product_name(USB_PRODUCT), manufacturer_name(USB_MANUFACTURER), serial_number(USB_SERIAL), fw_version(0x0100),
usb_version(0x0200) // at least 2.1 or 3.x for BOS & webUSB
,
usb_class(TUSB_CLASS_MISC), usb_subclass(MISC_SUBCLASS_COMMON), usb_protocol(MISC_PROTOCOL_IAD), usb_attributes(TUSB_DESC_CONFIG_ATT_SELF_POWERED),
usb_power_ma(500), webusb_enabled(USB_WEBUSB_ENABLED), webusb_url(USB_WEBUSB_URL), _started(false), _task_stack_size(task_stack_size),
_event_task_priority(event_task_priority) {
if (!arduino_usb_event_loop_handle) {
esp_event_loop_args_t event_task_args = {
.queue_size = 5,
.task_name = "arduino_usb_events",
.task_priority = _event_task_priority,
.task_stack_size = _task_stack_size,
.task_core_id = tskNO_AFFINITY
};
if (esp_event_loop_create(&event_task_args, &arduino_usb_event_loop_handle) != ESP_OK) {
log_e("esp_event_loop_create failed");
}
}
}
ESPUSB::~ESPUSB() {
if (arduino_usb_event_loop_handle) {
esp_event_loop_delete(arduino_usb_event_loop_handle);
arduino_usb_event_loop_handle = NULL;
}
}
bool ESPUSB::begin() {
if (!_started) {
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
if (serial_number == "__MAC__") {
StreamString s;
uint8_t m[6];
esp_efuse_mac_get_default(m);
s.printf("%02X%02X%02X%02X%02X%02X", m[0], m[1], m[2], m[3], m[4], m[5]);
serial_number = s;
}
#endif
tinyusb_device_config_t tinyusb_device_config = {
.vid = vid,
.pid = pid,
.product_name = product_name.c_str(),
.manufacturer_name = manufacturer_name.c_str(),
.serial_number = serial_number.c_str(),
.fw_version = fw_version,
.usb_version = usb_version,
.usb_class = usb_class,
.usb_subclass = usb_subclass,
.usb_protocol = usb_protocol,
.usb_attributes = usb_attributes,
.usb_power_ma = usb_power_ma,
.webusb_enabled = webusb_enabled,
.webusb_url = webusb_url.c_str()
};
_started = tinyusb_init(&tinyusb_device_config) == ESP_OK;
}
return _started;
}
void ESPUSB::onEvent(esp_event_handler_t callback) {
onEvent(ARDUINO_USB_ANY_EVENT, callback);
}
void ESPUSB::onEvent(arduino_usb_event_t event, esp_event_handler_t callback) {
arduino_usb_event_handler_register_with(ARDUINO_USB_EVENTS, event, callback, this);
}
ESPUSB::operator bool() const {
return _started && tinyusb_device_mounted;
}
bool ESPUSB::enableDFU() {
#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;
}
bool ESPUSB::VID(uint16_t v) {
if (!_started) {
vid = v;
}
return !_started;
}
uint16_t ESPUSB::VID(void) {
return vid;
}
bool ESPUSB::PID(uint16_t p) {
if (!_started) {
pid = p;
}
return !_started;
}
uint16_t ESPUSB::PID(void) {
return pid;
}
bool ESPUSB::firmwareVersion(uint16_t version) {
if (!_started) {
fw_version = version;
}
return !_started;
}
uint16_t ESPUSB::firmwareVersion(void) {
return fw_version;
}
bool ESPUSB::usbVersion(uint16_t version) {
if (!_started) {
usb_version = version;
}
return !_started;
}
uint16_t ESPUSB::usbVersion(void) {
return usb_version;
}
bool ESPUSB::usbPower(uint16_t mA) {
if (!_started) {
usb_power_ma = mA;
}
return !_started;
}
uint16_t ESPUSB::usbPower(void) {
return usb_power_ma;
}
bool ESPUSB::usbClass(uint8_t _class) {
if (!_started) {
usb_class = _class;
}
return !_started;
}
uint8_t ESPUSB::usbClass(void) {
return usb_class;
}
bool ESPUSB::usbSubClass(uint8_t subClass) {
if (!_started) {
usb_subclass = subClass;
}
return !_started;
}
uint8_t ESPUSB::usbSubClass(void) {
return usb_subclass;
}
bool ESPUSB::usbProtocol(uint8_t protocol) {
if (!_started) {
usb_protocol = protocol;
}
return !_started;
}
uint8_t ESPUSB::usbProtocol(void) {
return usb_protocol;
}
bool ESPUSB::usbAttributes(uint8_t attr) {
if (!_started) {
usb_attributes = attr;
}
return !_started;
}
uint8_t ESPUSB::usbAttributes(void) {
return usb_attributes;
}
bool ESPUSB::webUSB(bool enabled) {
if (!_started) {
webusb_enabled = enabled;
if (enabled && usb_version < 0x0210) {
usb_version = 0x0210;
}
}
return !_started;
}
bool ESPUSB::webUSB(void) {
return webusb_enabled;
}
bool ESPUSB::productName(const char *name) {
if (!_started) {
product_name = name;
}
return !_started;
}
const char *ESPUSB::productName(void) {
return product_name.c_str();
}
bool ESPUSB::manufacturerName(const char *name) {
if (!_started) {
manufacturer_name = name;
}
return !_started;
}
const char *ESPUSB::manufacturerName(void) {
return manufacturer_name.c_str();
}
bool ESPUSB::serialNumber(const char *name) {
if (!_started) {
serial_number = name;
}
return !_started;
}
const char *ESPUSB::serialNumber(void) {
return serial_number.c_str();
}
bool ESPUSB::webUSBURL(const char *name) {
if (!_started) {
webusb_url = name;
}
return !_started;
}
const char *ESPUSB::webUSBURL(void) {
return webusb_url.c_str();
}
ESPUSB USB;
#endif /* CONFIG_TINYUSB_ENABLED */
#endif /* SOC_USB_OTG_SUPPORTED */
+123
View File
@@ -0,0 +1,123 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "soc/soc_caps.h"
#if SOC_USB_OTG_SUPPORTED
#include "sdkconfig.h"
#if CONFIG_TINYUSB_ENABLED
#include "esp_event.h"
#include "USBCDC.h"
#include "Arduino.h" // defines ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE and ARDUINO_SERIAL_EVENT_TASK_PRIORITY
#define ARDUINO_USB_ON_BOOT (ARDUINO_USB_CDC_ON_BOOT | ARDUINO_USB_MSC_ON_BOOT | ARDUINO_USB_DFU_ON_BOOT)
ESP_EVENT_DECLARE_BASE(ARDUINO_USB_EVENTS);
typedef enum {
ARDUINO_USB_ANY_EVENT = ESP_EVENT_ANY_ID,
ARDUINO_USB_STARTED_EVENT = 0,
ARDUINO_USB_STOPPED_EVENT,
ARDUINO_USB_SUSPEND_EVENT,
ARDUINO_USB_RESUME_EVENT,
ARDUINO_USB_MAX_EVENT,
} arduino_usb_event_t;
typedef union {
struct {
bool remote_wakeup_en;
} suspend;
} arduino_usb_event_data_t;
class ESPUSB {
public:
ESPUSB(size_t event_task_stack_size = ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE, uint8_t event_task_priority = ARDUINO_SERIAL_EVENT_TASK_PRIORITY);
~ESPUSB();
void onEvent(esp_event_handler_t callback);
void onEvent(arduino_usb_event_t event, esp_event_handler_t callback);
bool VID(uint16_t v);
uint16_t VID(void);
bool PID(uint16_t p);
uint16_t PID(void);
bool firmwareVersion(uint16_t version);
uint16_t firmwareVersion(void);
bool usbVersion(uint16_t version);
uint16_t usbVersion(void);
bool usbPower(uint16_t mA);
uint16_t usbPower(void);
bool usbClass(uint8_t _class);
uint8_t usbClass(void);
bool usbSubClass(uint8_t subClass);
uint8_t usbSubClass(void);
bool usbProtocol(uint8_t protocol);
uint8_t usbProtocol(void);
bool usbAttributes(uint8_t attr);
uint8_t usbAttributes(void);
bool webUSB(bool enabled);
bool webUSB(void);
bool productName(const char *name);
const char *productName(void);
bool manufacturerName(const char *name);
const char *manufacturerName(void);
bool serialNumber(const char *name);
const char *serialNumber(void);
bool webUSBURL(const char *name);
const char *webUSBURL(void);
bool enableDFU();
bool begin();
operator bool() const;
private:
uint16_t vid;
uint16_t pid;
String product_name;
String manufacturer_name;
String serial_number;
uint16_t fw_version;
uint16_t usb_version;
uint8_t usb_class;
uint8_t usb_subclass;
uint8_t usb_protocol;
uint8_t usb_attributes;
uint16_t usb_power_ma;
bool webusb_enabled;
String webusb_url;
bool _started;
size_t _task_stack_size;
uint8_t _event_task_priority;
};
extern ESPUSB USB;
#endif /* CONFIG_TINYUSB_ENABLED */
#endif /* SOC_USB_OTG_SUPPORTED */
+477
View File
@@ -0,0 +1,477 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "USBCDC.h"
#if SOC_USB_OTG_SUPPORTED
#include "USB.h"
#if CONFIG_TINYUSB_CDC_ENABLED
#include "esp32-hal-tinyusb.h"
#include "rom/ets_sys.h"
ESP_EVENT_DEFINE_BASE(ARDUINO_USB_CDC_EVENTS);
esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait);
esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg);
USBCDC *devices[CFG_TUD_CDC];
static uint16_t load_cdc_descriptor(uint8_t *dst, uint8_t *itf) {
uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB CDC");
uint8_t descriptor[TUD_CDC_DESC_LEN] = {// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_CDC_DESCRIPTOR(*itf, str_index, 0x85, CFG_TUD_ENDOINT_SIZE, 0x03, 0x84, CFG_TUD_ENDOINT_SIZE)
};
*itf += 2;
memcpy(dst, descriptor, TUD_CDC_DESC_LEN);
return TUD_CDC_DESC_LEN;
}
static uint16_t load_cdc_descriptor2(uint8_t *dst, uint8_t *itf) {
uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB CDC2");
uint8_t ep_ntfy = tinyusb_get_free_in_endpoint();
TU_VERIFY(ep_ntfy != 0);
uint8_t ep_in = tinyusb_get_free_in_endpoint();
TU_VERIFY(ep_in != 0);
uint8_t ep_out = tinyusb_get_free_out_endpoint();
TU_VERIFY(ep_out != 0);
uint8_t descriptor[TUD_CDC_DESC_LEN] = {
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_CDC_DESCRIPTOR(*itf, str_index, (uint8_t)(0x80 | ep_ntfy), CFG_TUD_ENDOINT_SIZE, ep_out, (uint8_t)(0x80 | ep_in), CFG_TUD_ENDOINT_SIZE)
};
*itf += 2;
memcpy(dst, descriptor, TUD_CDC_DESC_LEN);
return TUD_CDC_DESC_LEN;
}
// Invoked when line state DTR & RTS are changed via SET_CONTROL_LINE_STATE
void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
//log_v("ITF: %u, DTR: %u, RTS: %u", itf, dtr, rts);
if (itf < CFG_TUD_CDC && devices[itf] != NULL) {
devices[itf]->_onLineState(dtr, rts);
}
}
// Invoked when line coding is change via SET_LINE_CODING
void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const *p_line_coding) {
//log_v("ITF: %u, BITRATE: %lu, STOP_BITS: %u, PARITY: %u, DATA_BITS: %u", itf, p_line_coding->bit_rate, p_line_coding->stop_bits, p_line_coding->parity, p_line_coding->data_bits);
if (itf < CFG_TUD_CDC && devices[itf] != NULL) {
devices[itf]->_onLineCoding(p_line_coding->bit_rate, p_line_coding->stop_bits, p_line_coding->parity, p_line_coding->data_bits);
}
}
// Invoked when received new data
void tud_cdc_rx_cb(uint8_t itf) {
//log_v("ITF: %u", itf);
if (itf < CFG_TUD_CDC && devices[itf] != NULL) {
devices[itf]->_onRX();
}
}
// Invoked when received send break
void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms) {
//log_v("itf: %u, duration_ms: %u", itf, duration_ms);
}
// Invoked when space becomes available in TX buffer
void tud_cdc_tx_complete_cb(uint8_t itf) {
if (itf < CFG_TUD_CDC && devices[itf] != NULL) {
devices[itf]->_onTX();
}
}
static void ARDUINO_ISR_ATTR cdc0_write_char(char c) {
if (CFG_TUD_CDC && devices[0] != NULL) {
tud_cdc_n_write_char(0, c);
}
}
static void usb_unplugged_cb(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) {
((USBCDC *)arg)->_onUnplugged();
}
USBCDC::USBCDC(uint8_t itfn)
: itf(itfn), bit_rate(0), stop_bits(0), parity(0), data_bits(0), dtr(false), rts(false), connected(false), reboot_enable(true), rx_queue(NULL), tx_lock(NULL),
tx_timeout_ms(250) {
if (itf < CFG_TUD_CDC) {
if (itf == 0) {
tinyusb_enable_interface(USB_INTERFACE_CDC, TUD_CDC_DESC_LEN, load_cdc_descriptor);
} else {
tinyusb_enable_interface(USB_INTERFACE_CDC2, TUD_CDC_DESC_LEN, load_cdc_descriptor2);
}
arduino_usb_event_handler_register_with(ARDUINO_USB_EVENTS, ARDUINO_USB_STOPPED_EVENT, usb_unplugged_cb, this);
} else {
log_e("Maximum of %u CDC devices are supported", CFG_TUD_CDC);
}
}
USBCDC::~USBCDC() {
end();
}
void USBCDC::onEvent(esp_event_handler_t callback) {
onEvent(ARDUINO_USB_CDC_ANY_EVENT, callback);
}
void USBCDC::onEvent(arduino_usb_cdc_event_t event, esp_event_handler_t callback) {
arduino_usb_event_handler_register_with(ARDUINO_USB_CDC_EVENTS, event, callback, this);
}
size_t USBCDC::setRxBufferSize(size_t rx_queue_len) {
size_t currentQueueSize = rx_queue ? uxQueueSpacesAvailable(rx_queue) + uxQueueMessagesWaiting(rx_queue) : 0;
if (rx_queue_len != currentQueueSize) {
QueueHandle_t new_rx_queue = NULL;
if (rx_queue_len) {
new_rx_queue = xQueueCreate(rx_queue_len, sizeof(uint8_t));
if (!new_rx_queue) {
log_e("CDC Queue creation failed.");
return 0;
}
if (rx_queue) {
size_t copySize = uxQueueMessagesWaiting(rx_queue);
if (copySize > 0) {
for (size_t i = 0; i < copySize; i++) {
uint8_t ch = 0;
xQueueReceive(rx_queue, &ch, 0);
if (!xQueueSend(new_rx_queue, &ch, 0)) {
arduino_usb_cdc_event_data_t p;
p.rx_overflow.dropped_bytes = copySize - i;
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_RX_OVERFLOW_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
log_e("CDC RX Overflow.");
break;
}
}
}
vQueueDelete(rx_queue);
}
rx_queue = new_rx_queue;
return rx_queue_len;
} else {
if (rx_queue) {
vQueueDelete(rx_queue);
rx_queue = NULL;
}
}
}
return rx_queue_len;
}
void USBCDC::begin(unsigned long baud) {
if (itf >= CFG_TUD_CDC) {
return;
}
if (tx_lock == NULL) {
tx_lock = xSemaphoreCreateMutex();
}
// if rx_queue was set before begin(), keep it
if (!rx_queue) {
setRxBufferSize(256); //default if not preset
}
devices[itf] = this;
}
void USBCDC::end() {
if (itf >= CFG_TUD_CDC) {
return;
}
connected = false;
devices[itf] = NULL;
setRxBufferSize(0);
if (tx_lock != NULL) {
vSemaphoreDelete(tx_lock);
tx_lock = NULL;
}
}
void USBCDC::setTxTimeoutMs(uint32_t timeout) {
tx_timeout_ms = timeout;
}
void USBCDC::_onUnplugged(void) {
if (connected) {
connected = false;
dtr = false;
rts = false;
arduino_usb_cdc_event_data_t p;
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_DISCONNECTED_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
}
}
enum {
CDC_LINE_IDLE,
CDC_LINE_1,
CDC_LINE_2,
CDC_LINE_3
};
void USBCDC::_onLineState(bool _dtr, bool _rts) {
static uint8_t lineState = CDC_LINE_IDLE;
if (dtr == _dtr && rts == _rts) {
return; // Skip duplicate events
}
dtr = _dtr;
rts = _rts;
if (reboot_enable) {
if (!dtr && rts) {
if (lineState == CDC_LINE_IDLE) {
lineState++;
if (connected) {
connected = false;
arduino_usb_cdc_event_data_t p;
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_DISCONNECTED_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
}
// } else if(lineState == CDC_LINE_2){//esptool.js
// lineState++;
} else {
lineState = CDC_LINE_IDLE;
}
} else if (dtr && rts) {
if (lineState == CDC_LINE_1) {
lineState++;
} else {
lineState = CDC_LINE_IDLE;
}
} else if (dtr && !rts) {
if (lineState == CDC_LINE_2) {
lineState++;
// } else if(lineState == CDC_LINE_IDLE){//esptool.js
// lineState++;
} else {
lineState = CDC_LINE_IDLE;
}
} else if (!dtr && !rts) {
if (lineState == CDC_LINE_3) {
usb_persist_restart(RESTART_BOOTLOADER);
} else {
lineState = CDC_LINE_IDLE;
}
}
}
if (lineState == CDC_LINE_IDLE) {
if (dtr && rts && !connected) {
connected = true;
arduino_usb_cdc_event_data_t p;
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_CONNECTED_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
} else if (!dtr && connected) {
connected = false;
arduino_usb_cdc_event_data_t p;
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_DISCONNECTED_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
}
arduino_usb_cdc_event_data_t l;
l.line_state.dtr = dtr;
l.line_state.rts = rts;
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_LINE_STATE_EVENT, &l, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
}
}
void USBCDC::_onLineCoding(uint32_t _bit_rate, uint8_t _stop_bits, uint8_t _parity, uint8_t _data_bits) {
if (bit_rate != _bit_rate || data_bits != _data_bits || stop_bits != _stop_bits || parity != _parity) {
// ArduinoIDE sends LineCoding with 1200bps baud to reset the device
if (reboot_enable && _bit_rate == 1200) {
usb_persist_restart(RESTART_BOOTLOADER);
} else {
bit_rate = _bit_rate;
data_bits = _data_bits;
stop_bits = _stop_bits;
parity = _parity;
arduino_usb_cdc_event_data_t p;
p.line_coding.bit_rate = bit_rate;
p.line_coding.data_bits = data_bits;
p.line_coding.stop_bits = stop_bits;
p.line_coding.parity = parity;
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_LINE_CODING_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
}
}
}
void USBCDC::_onRX() {
arduino_usb_cdc_event_data_t p;
uint8_t buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE + 1];
uint32_t count = tud_cdc_n_read(itf, buf, CONFIG_TINYUSB_CDC_RX_BUFSIZE);
for (uint32_t i = 0; i < count; i++) {
if (rx_queue == NULL || !xQueueSend(rx_queue, buf + i, 10)) {
p.rx_overflow.dropped_bytes = count - i;
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_RX_OVERFLOW_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
log_e("CDC RX Overflow.");
count = i;
break;
}
}
if (count) {
p.rx.len = count;
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_RX_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
}
}
void USBCDC::_onTX() {
arduino_usb_cdc_event_data_t p;
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_TX_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
}
void USBCDC::enableReboot(bool enable) {
reboot_enable = enable;
}
bool USBCDC::rebootEnabled(void) {
return reboot_enable;
}
int USBCDC::available(void) {
if (itf >= CFG_TUD_CDC || rx_queue == NULL) {
return -1;
}
return uxQueueMessagesWaiting(rx_queue);
}
int USBCDC::peek(void) {
if (itf >= CFG_TUD_CDC || rx_queue == NULL) {
return -1;
}
uint8_t c;
if (xQueuePeek(rx_queue, &c, 0)) {
return c;
}
return -1;
}
int USBCDC::read(void) {
if (itf >= CFG_TUD_CDC || rx_queue == NULL) {
return -1;
}
uint8_t c = 0;
if (xQueueReceive(rx_queue, &c, 0)) {
return c;
}
return -1;
}
size_t USBCDC::read(uint8_t *buffer, size_t size) {
if (itf >= CFG_TUD_CDC || rx_queue == NULL) {
return -1;
}
uint8_t c = 0;
size_t count = 0;
while (count < size && xQueueReceive(rx_queue, &c, 0)) {
buffer[count++] = c;
}
return count;
}
void USBCDC::flush(void) {
if (itf >= CFG_TUD_CDC || tx_lock == NULL || !tud_cdc_n_connected(itf)) {
return;
}
if (xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS) {
return;
}
tud_cdc_n_write_flush(itf);
xSemaphoreGive(tx_lock);
}
int USBCDC::availableForWrite(void) {
if (itf >= CFG_TUD_CDC || tx_lock == NULL || !tud_cdc_n_connected(itf)) {
return 0;
}
if (xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS) {
return 0;
}
size_t a = tud_cdc_n_write_available(itf);
xSemaphoreGive(tx_lock);
return a;
}
size_t USBCDC::write(const uint8_t *buffer, size_t size) {
if (itf >= CFG_TUD_CDC || tx_lock == NULL || buffer == NULL || size == 0 || !tud_cdc_n_connected(itf)) {
return 0;
}
if (xPortInIsrContext()) {
BaseType_t taskWoken = false;
if (xSemaphoreTakeFromISR(tx_lock, &taskWoken) != pdPASS) {
return 0;
}
} else if (xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS) {
return 0;
}
size_t to_send = size, so_far = 0;
// writeTimeout will prevent that TinyUSB failure locks the while(to_send) loop
uint32_t writeTimeout = millis() + tx_timeout_ms;
while (to_send) {
if (!tud_cdc_n_connected(itf) || (int32_t)(millis() - writeTimeout) >= 0) {
log_e("USB is disconnected or CDC writing has timed out.");
size = so_far;
break;
}
size_t space = tud_cdc_n_write_available(itf);
if (!space) {
tud_cdc_n_write_flush(itf);
continue;
}
if (space > to_send) {
space = to_send;
}
size_t sent = tud_cdc_n_write(itf, buffer + so_far, space);
if (sent) {
so_far += sent;
to_send -= sent;
tud_cdc_n_write_flush(itf);
} else {
size = so_far;
break;
}
}
if (xPortInIsrContext()) {
BaseType_t taskWoken = false;
xSemaphoreGiveFromISR(tx_lock, &taskWoken);
} else {
xSemaphoreGive(tx_lock);
}
return size;
}
size_t USBCDC::write(uint8_t c) {
return write(&c, 1);
}
uint32_t USBCDC::baudRate() {
return bit_rate;
}
void USBCDC::setDebugOutput(bool en) {
if (itf) {
return;
}
if (en) {
uartSetDebug(NULL);
ets_install_putc2((void (*)(char)) & cdc0_write_char);
} else {
ets_install_putc2(NULL);
}
ets_install_putc1(NULL); // closes UART log output
}
USBCDC::operator bool() const {
if (itf >= CFG_TUD_CDC) {
return false;
}
return connected;
}
#if !ARDUINO_USB_MODE && ARDUINO_USB_CDC_ON_BOOT // Native USB CDC selected
// USBSerial is always available to be used
USBCDC USBSerial(0);
#endif
#endif /* CONFIG_TINYUSB_CDC_ENABLED */
#endif /* SOC_USB_OTG_SUPPORTED */
+144
View File
@@ -0,0 +1,144 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "soc/soc_caps.h"
#if SOC_USB_OTG_SUPPORTED
#include "sdkconfig.h"
#if CONFIG_TINYUSB_CDC_ENABLED
#include <inttypes.h>
#include "esp_event.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "Stream.h"
ESP_EVENT_DECLARE_BASE(ARDUINO_USB_CDC_EVENTS);
typedef enum {
ARDUINO_USB_CDC_ANY_EVENT = ESP_EVENT_ANY_ID,
ARDUINO_USB_CDC_CONNECTED_EVENT = 0,
ARDUINO_USB_CDC_DISCONNECTED_EVENT,
ARDUINO_USB_CDC_LINE_STATE_EVENT,
ARDUINO_USB_CDC_LINE_CODING_EVENT,
ARDUINO_USB_CDC_RX_EVENT,
ARDUINO_USB_CDC_TX_EVENT,
ARDUINO_USB_CDC_RX_OVERFLOW_EVENT,
ARDUINO_USB_CDC_MAX_EVENT,
} arduino_usb_cdc_event_t;
typedef union {
struct {
bool dtr;
bool rts;
} line_state;
struct {
uint32_t bit_rate;
uint8_t stop_bits; ///< 0: 1 stop bit - 1: 1.5 stop bits - 2: 2 stop bits
uint8_t parity; ///< 0: None - 1: Odd - 2: Even - 3: Mark - 4: Space
uint8_t data_bits; ///< can be 5, 6, 7, 8 or 16
} line_coding;
struct {
size_t len;
} rx;
struct {
size_t dropped_bytes;
} rx_overflow;
} arduino_usb_cdc_event_data_t;
class USBCDC : public Stream {
public:
USBCDC(uint8_t itf = 0);
~USBCDC();
void onEvent(esp_event_handler_t callback);
void onEvent(arduino_usb_cdc_event_t event, esp_event_handler_t callback);
size_t setRxBufferSize(size_t size);
void setTxTimeoutMs(uint32_t timeout);
void begin(unsigned long baud = 0);
void end();
int available(void);
int availableForWrite(void);
int peek(void);
int read(void);
size_t read(uint8_t *buffer, size_t size);
size_t write(uint8_t);
size_t write(const uint8_t *buffer, size_t size);
void flush(void);
inline size_t read(char *buffer, size_t size) {
return read((uint8_t *)buffer, size);
}
inline size_t write(const char *buffer, size_t size) {
return write((uint8_t *)buffer, size);
}
inline size_t write(const char *s) {
return write((uint8_t *)s, strlen(s));
}
inline size_t write(unsigned long n) {
return write((uint8_t)n);
}
inline size_t write(long n) {
return write((uint8_t)n);
}
inline size_t write(unsigned int n) {
return write((uint8_t)n);
}
inline size_t write(int n) {
return write((uint8_t)n);
}
uint32_t baudRate();
void setDebugOutput(bool);
operator bool() const;
void enableReboot(bool enable);
bool rebootEnabled(void);
//internal methods
void _onDFU(void);
void _onLineState(bool _dtr, bool _rts);
void _onLineCoding(uint32_t _bit_rate, uint8_t _stop_bits, uint8_t _parity, uint8_t _data_bits);
void _onRX(void);
void _onTX(void);
void _onUnplugged(void);
protected:
uint8_t itf;
uint32_t bit_rate;
uint8_t stop_bits; ///< 0: 1 stop bit - 1: 1.5 stop bits - 2: 2 stop bits
uint8_t parity; ///< 0: None - 1: Odd - 2: Even - 3: Mark - 4: Space
uint8_t data_bits; ///< can be 5, 6, 7, 8 or 16
bool dtr;
bool rts;
bool connected;
bool reboot_enable;
QueueHandle_t rx_queue;
SemaphoreHandle_t tx_lock;
uint32_t tx_timeout_ms;
};
#if !ARDUINO_USB_MODE && ARDUINO_USB_CDC_ON_BOOT // Native USB CDC selected
#ifndef USB_SERIAL_IS_DEFINED
#define USB_SERIAL_IS_DEFINED 1
#endif
// USBSerial is always available to be used
extern USBCDC USBSerial;
#endif
#endif /* CONFIG_TINYUSB_CDC_ENABLED */
#endif /* SOC_USB_OTG_SUPPORTED */
+267
View File
@@ -0,0 +1,267 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "USBMSC.h"
#if SOC_USB_OTG_SUPPORTED
#if CONFIG_TINYUSB_MSC_ENABLED
#include "esp32-hal-tinyusb.h"
extern "C" uint16_t tusb_msc_load_descriptor(uint8_t *dst, uint8_t *itf) {
uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB MSC");
uint8_t ep_num = tinyusb_get_free_duplex_endpoint();
TU_VERIFY(ep_num != 0);
uint8_t descriptor[TUD_MSC_DESC_LEN] = {// Interface number, string index, EP Out & EP In address, EP size
TUD_MSC_DESCRIPTOR(*itf, str_index, ep_num, (uint8_t)(0x80 | ep_num), CFG_TUD_ENDOINT_SIZE)
};
*itf += 1;
memcpy(dst, descriptor, TUD_MSC_DESC_LEN);
return TUD_MSC_DESC_LEN;
}
typedef struct {
bool media_present;
bool is_writable;
uint8_t vendor_id[8];
uint8_t product_id[16];
uint8_t product_rev[4];
uint16_t block_size;
uint32_t block_count;
bool (*start_stop)(uint8_t power_condition, bool start, bool load_eject);
int32_t (*read)(uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize);
int32_t (*write)(uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize);
} msc_lun_t;
static const uint8_t MSC_MAX_LUN = 3;
static uint8_t MSC_ACTIVE_LUN = 0;
static msc_lun_t msc_luns[MSC_MAX_LUN];
static void cplstr(void *dst, const void *src, size_t max_len) {
if (!src || !dst || !max_len) {
return;
}
size_t l = strlen((const char *)src);
if (l > max_len) {
l = max_len;
}
memcpy(dst, src, l);
}
// Invoked when received GET_MAX_LUN request, required for multiple LUNs implementation
uint8_t tud_msc_get_maxlun_cb(void) {
log_v("%u", MSC_ACTIVE_LUN);
return MSC_ACTIVE_LUN;
}
// Invoked when received SCSI_CMD_INQUIRY
// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) {
log_v("[%u]", lun);
cplstr(vendor_id, msc_luns[lun].vendor_id, 8);
cplstr(product_id, msc_luns[lun].product_id, 16);
cplstr(product_rev, msc_luns[lun].product_rev, 4);
}
// Invoked when received Test Unit Ready command.
// return true allowing host to read/write this LUN e.g SD card inserted
bool tud_msc_test_unit_ready_cb(uint8_t lun) {
log_v("[%u]: %u", lun, msc_luns[lun].media_present);
return msc_luns[lun].media_present; // RAM disk is always ready
}
// 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
void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size) {
log_v("[%u]", lun);
if (!msc_luns[lun].media_present) {
*block_count = 0;
*block_size = 0;
return;
}
*block_count = msc_luns[lun].block_count;
*block_size = msc_luns[lun].block_size;
}
// Invoked when received Start Stop Unit command
// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
// - Start = 1 : active mode, if load_eject = 1 : load disk storage
bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) {
log_v("[%u] power: %u, start: %u, eject: %u", lun, power_condition, start, load_eject);
if (msc_luns[lun].start_stop) {
return msc_luns[lun].start_stop(power_condition, start, load_eject);
}
return true;
}
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize) {
log_v("[%u], lba: %u, offset: %u, bufsize: %u", lun, lba, offset, bufsize);
if (!msc_luns[lun].media_present) {
return 0;
}
if (msc_luns[lun].read) {
return msc_luns[lun].read(lba, offset, buffer, bufsize);
}
return 0;
}
// Callback invoked when received WRITE10 command.
// Process data in buffer to disk's storage and return number of written bytes
int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) {
log_v("[%u], lba: %u, offset: %u, bufsize: %u", lun, lba, offset, bufsize);
if (!msc_luns[lun].media_present) {
return 0;
}
if (msc_luns[lun].write) {
return msc_luns[lun].write(lba, offset, buffer, bufsize);
}
return 0;
}
// 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
int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize) {
// read10 & write10 has their own callback and MUST not be handled here
log_v("[%u] cmd: %u, bufsize: %u", lun, scsi_cmd[0], bufsize);
void const *response = NULL;
uint16_t resplen = 0;
// most scsi handled is input
bool in_xfer = true;
if (!msc_luns[lun].media_present) {
return -1;
}
switch (scsi_cmd[0]) {
case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
// Host is about to read/write etc ... better not to disconnect disk
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;
}
bool tud_msc_is_writable_cb(uint8_t lun) {
log_v("[%u]: %u", lun, msc_luns[lun].is_writable);
return msc_luns[lun].is_writable; // RAM disk is always ready
}
USBMSC::USBMSC() {
if (MSC_ACTIVE_LUN < MSC_MAX_LUN) {
_lun = MSC_ACTIVE_LUN;
MSC_ACTIVE_LUN++;
msc_luns[_lun].media_present = false;
msc_luns[_lun].is_writable = true;
msc_luns[_lun].vendor_id[0] = 0;
msc_luns[_lun].product_id[0] = 0;
msc_luns[_lun].product_rev[0] = 0;
msc_luns[_lun].block_size = 0;
msc_luns[_lun].block_count = 0;
msc_luns[_lun].start_stop = NULL;
msc_luns[_lun].read = NULL;
msc_luns[_lun].write = NULL;
}
if (_lun == 0) {
tinyusb_enable_interface(USB_INTERFACE_MSC, TUD_MSC_DESC_LEN, tusb_msc_load_descriptor);
}
}
USBMSC::~USBMSC() {
end();
}
bool USBMSC::begin(uint32_t block_count, uint16_t block_size) {
msc_luns[_lun].block_size = block_size;
msc_luns[_lun].block_count = block_count;
if (!msc_luns[_lun].block_size || !msc_luns[_lun].block_count || !msc_luns[_lun].read || !msc_luns[_lun].write) {
return false;
}
return true;
}
void USBMSC::end() {
msc_luns[_lun].media_present = false;
msc_luns[_lun].is_writable = false;
msc_luns[_lun].vendor_id[0] = 0;
msc_luns[_lun].product_id[0] = 0;
msc_luns[_lun].product_rev[0] = 0;
msc_luns[_lun].block_size = 0;
msc_luns[_lun].block_count = 0;
msc_luns[_lun].start_stop = NULL;
msc_luns[_lun].read = NULL;
msc_luns[_lun].write = NULL;
}
void USBMSC::vendorID(const char *vid) {
cplstr(msc_luns[_lun].vendor_id, vid, 8);
}
void USBMSC::productID(const char *pid) {
cplstr(msc_luns[_lun].product_id, pid, 16);
}
void USBMSC::productRevision(const char *rev) {
cplstr(msc_luns[_lun].product_rev, rev, 4);
}
void USBMSC::onStartStop(msc_start_stop_cb cb) {
msc_luns[_lun].start_stop = cb;
}
void USBMSC::onRead(msc_read_cb cb) {
msc_luns[_lun].read = cb;
}
void USBMSC::onWrite(msc_write_cb cb) {
msc_luns[_lun].write = cb;
}
void USBMSC::isWritable(bool is_writable) {
msc_luns[_lun].is_writable = is_writable;
}
void USBMSC::mediaPresent(bool media_present) {
msc_luns[_lun].media_present = media_present;
}
#endif /* CONFIG_TINYUSB_MSC_ENABLED */
#endif /* SOC_USB_OTG_SUPPORTED */
+57
View File
@@ -0,0 +1,57 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "soc/soc_caps.h"
#if SOC_USB_OTG_SUPPORTED
#include <stdint.h>
#include <stdbool.h>
#include "sdkconfig.h"
#if CONFIG_TINYUSB_MSC_ENABLED
// Invoked when received Start Stop Unit command
// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
// - Start = 1 : active mode, if load_eject = 1 : load disk storage
typedef bool (*msc_start_stop_cb)(uint8_t power_condition, bool start, bool load_eject);
// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
typedef int32_t (*msc_read_cb)(uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize);
// Process data in buffer to disk's storage and return number of written bytes
typedef int32_t (*msc_write_cb)(uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize);
class USBMSC {
public:
USBMSC();
~USBMSC();
bool begin(uint32_t block_count, uint16_t block_size);
void end();
void vendorID(const char *vid); //max 8 chars
void productID(const char *pid); //max 16 chars
void productRevision(const char *ver); //max 4 chars
void mediaPresent(bool media_present);
void isWritable(bool is_writable);
void onStartStop(msc_start_stop_cb cb);
void onRead(msc_read_cb cb);
void onWrite(msc_write_cb cb);
private:
uint8_t _lun;
};
#endif /* CONFIG_TINYUSB_MSC_ENABLED */
#endif /* SOC_USB_OTG_SUPPORTED */
+94
View File
@@ -0,0 +1,94 @@
/*
* Udp.cpp: Library to send/receive UDP packets.
*
* NOTE: UDP is fast, but has some important limitations (thanks to Warren Gray for mentioning these)
* 1) UDP does not guarantee the order in which assembled UDP packets are received. This
* might not happen often in practice, but in larger network topologies, a UDP
* packet can be received out of sequence.
* 2) UDP does not guard against lost packets - so packets *can* disappear without the sender being
* aware of it. Again, this may not be a concern in practice on small local networks.
* For more information, see http://www.cafeaulait.org/course/week12/35.html
*
* MIT License:
* Copyright (c) 2008 Bjoern Hartmann
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* bjoern@cs.stanford.edu 12/30/2008
*/
#ifndef udp_h
#define udp_h
#include <Stream.h>
#include <IPAddress.h>
class UDP : public Stream {
public:
virtual uint8_t begin(uint16_t) = 0; // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use
virtual uint8_t beginMulticast(IPAddress, uint16_t) {
return 0;
} // initialize, start listening on specified multicast IP address and port. Returns 1 if successful, 0 on failure
virtual void stop() = 0; // Finish with the UDP socket
// Sending UDP packets
// Start building up a packet to send to the remote host specific in ip and port
// Returns 1 if successful, 0 if there was a problem with the supplied IP address or port
virtual int beginPacket(IPAddress ip, uint16_t port) = 0;
// Start building up a packet to send to the remote host specific in host and port
// Returns 1 if successful, 0 if there was a problem resolving the hostname or port
virtual int beginPacket(const char *host, uint16_t port) = 0;
// Finish off this packet and send it
// Returns 1 if the packet was sent successfully, 0 if there was an error
virtual int endPacket() = 0;
// Write a single byte into the packet
virtual size_t write(uint8_t) = 0;
// Write size bytes from buffer into the packet
virtual size_t write(const uint8_t *buffer, size_t size) = 0;
// Start processing the next available incoming packet
// Returns the size of the packet in bytes, or 0 if no packets are available
virtual int parsePacket() = 0;
// Number of bytes remaining in the current packet
virtual int available() = 0;
// Read a single byte from the current packet
virtual int read() = 0;
// Read up to len bytes from the current packet and place them into buffer
// Returns the number of bytes read, or 0 if none are available
virtual int read(unsigned char *buffer, size_t len) = 0;
// Read up to len characters from the current packet and place them into buffer
// Returns the number of characters read, or 0 if none are available
virtual int read(char *buffer, size_t len) = 0;
// Return the next byte from the current packet without moving on to the next byte
virtual int peek() = 0;
virtual void flush() = 0; // Finish reading the current packet
// Return the IP address of the host who sent the current incoming packet
virtual IPAddress remoteIP() = 0;
// Return the port of the host who sent the current incoming packet
virtual uint16_t remotePort() = 0;
protected:
uint8_t *rawIPAddress(IPAddress &addr) {
return addr.raw_address();
}
};
#endif
+138
View File
@@ -0,0 +1,138 @@
/*
WCharacter.h - Character utility functions for Wiring & Arduino
Copyright (c) 2010 Hernando Barragan. All right 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
*/
#ifndef Character_h
#define Character_h
#include <ctype.h>
#define isascii(__c) ((unsigned)(__c) <= 0177)
#define toascii(__c) ((__c) & 0177)
// WCharacter.h prototypes
inline boolean isAlphaNumeric(int c) __attribute__((always_inline));
inline boolean isAlpha(int c) __attribute__((always_inline));
inline boolean isAscii(int c) __attribute__((always_inline));
inline boolean isWhitespace(int c) __attribute__((always_inline));
inline boolean isControl(int c) __attribute__((always_inline));
inline boolean isDigit(int c) __attribute__((always_inline));
inline boolean isGraph(int c) __attribute__((always_inline));
inline boolean isLowerCase(int c) __attribute__((always_inline));
inline boolean isPrintable(int c) __attribute__((always_inline));
inline boolean isPunct(int c) __attribute__((always_inline));
inline boolean isSpace(int c) __attribute__((always_inline));
inline boolean isUpperCase(int c) __attribute__((always_inline));
inline boolean isHexadecimalDigit(int c) __attribute__((always_inline));
inline int toAscii(int c) __attribute__((always_inline));
inline int toLowerCase(int c) __attribute__((always_inline));
inline int toUpperCase(int c) __attribute__((always_inline));
// Checks for an alphanumeric character.
// It is equivalent to (isalpha(c) || isdigit(c)).
inline boolean isAlphaNumeric(int c) {
return (isalnum(c) == 0 ? false : true);
}
// Checks for an alphabetic character.
// It is equivalent to (isupper(c) || islower(c)).
inline boolean isAlpha(int c) {
return (isalpha(c) == 0 ? false : true);
}
// Checks whether c is a 7-bit unsigned char value
// that fits into the ASCII character set.
inline boolean isAscii(int c) {
return (isascii(c) == 0 ? false : true);
}
// Checks for a blank character, that is, a space or a tab.
inline boolean isWhitespace(int c) {
return (isblank(c) == 0 ? false : true);
}
// Checks for a control character.
inline boolean isControl(int c) {
return (iscntrl(c) == 0 ? false : true);
}
// Checks for a digit (0 through 9).
inline boolean isDigit(int c) {
return (isdigit(c) == 0 ? false : true);
}
// Checks for any printable character except space.
inline boolean isGraph(int c) {
return (isgraph(c) == 0 ? false : true);
}
// Checks for a lower-case character.
inline boolean isLowerCase(int c) {
return (islower(c) == 0 ? false : true);
}
// Checks for any printable character including space.
inline boolean isPrintable(int c) {
return (isprint(c) == 0 ? false : true);
}
// Checks for any printable character which is not a space
// or an alphanumeric character.
inline boolean isPunct(int c) {
return (ispunct(c) == 0 ? false : true);
}
// Checks for white-space characters. For the avr-libc library,
// these are: space, formfeed ('\f'), newline ('\n'), carriage
// return ('\r'), horizontal tab ('\t'), and vertical tab ('\v').
inline boolean isSpace(int c) {
return (isspace(c) == 0 ? false : true);
}
// Checks for an uppercase letter.
inline boolean isUpperCase(int c) {
return (isupper(c) == 0 ? false : true);
}
// Checks for a hexadecimal digits, i.e. one of 0 1 2 3 4 5 6 7
// 8 9 a b c d e f A B C D E F.
inline boolean isHexadecimalDigit(int c) {
return (isxdigit(c) == 0 ? false : true);
}
// Converts c to a 7-bit unsigned char value that fits into the
// ASCII character set, by clearing the high-order bits.
inline int toAscii(int c) {
return toascii(c);
}
// Warning:
// Many people will be unhappy if you use this function.
// This function will convert accented letters into random
// characters.
// Converts the letter c to lower case, if possible.
inline int toLowerCase(int c) {
return tolower(c);
}
// Converts the letter c to upper case, if possible.
inline int toUpperCase(int c) {
return toupper(c);
}
#endif
+89
View File
@@ -0,0 +1,89 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Wiring project - http://wiring.org.co
Copyright (c) 2004-06 Hernando Barragan
Modified 13 August 2006, David A. Mellis for Arduino - http://www.arduino.cc/
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., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
$Id$
*/
extern "C" {
#include <stdlib.h>
#include "esp_system.h"
}
#include "esp32-hal-log.h"
#include "esp_random.h"
// Allows the user to choose between Real Hardware
// or Software Pseudo random generators for the
// Arduino random() functions
static bool s_useRandomHW = true;
void useRealRandomGenerator(bool useRandomHW) {
s_useRandomHW = useRandomHW;
}
// Calling randomSeed() will force the
// Pseudo Random generator like in
// Arduino mainstream API
void randomSeed(unsigned long seed) {
if (seed != 0) {
srand(seed);
s_useRandomHW = false;
}
}
long random(long howsmall, long howbig);
long random(long howbig) {
if (howbig == 0) {
return 0;
}
if (howbig < 0) {
return (random(0, -howbig));
}
// if randomSeed was called, fall back to software PRNG
uint32_t val = (s_useRandomHW) ? esp_random() : rand();
return val % howbig;
}
long random(long howsmall, long howbig) {
if (howsmall >= howbig) {
return howsmall;
}
long diff = howbig - howsmall;
return random(diff) + howsmall;
}
long map(long x, long in_min, long in_max, long out_min, long out_max) {
const long run = in_max - in_min;
if (run == 0) {
log_e("map(): Invalid input range, min == max");
return -1; // AVR returns -1, SAM returns 0
}
const long rise = out_max - out_min;
const long delta = x - in_min;
return (delta * rise) / run + out_min;
}
uint16_t makeWord(uint16_t w) {
return w;
}
uint16_t makeWord(uint8_t h, uint8_t l) {
return (h << 8) | l;
}
+934
View File
@@ -0,0 +1,934 @@
/*
WString.cpp - String library for Wiring & Arduino
...mostly rewritten by Paul Stoffregen...
Copyright (c) 2009-10 Hernando Barragan. All rights reserved.
Copyright 2011, Paul Stoffregen, paul@pjrc.com
Modified by Ivan Grokhotkov, 2014 - esp8266 support
Modified by Michael C. Miller, 2015 - esp8266 progmem support
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 "WString.h"
#include "stdlib_noniso.h"
#include "esp32-hal-log.h"
/*********************************************/
/* Constructors */
/*********************************************/
String::String(const char *cstr) {
init();
if (cstr) {
copy(cstr, strlen(cstr));
}
}
String::String(const char *cstr, unsigned int length) {
init();
if (cstr) {
copy(cstr, length);
}
}
String::String(const String &value) {
init();
*this = value;
}
#ifdef __GXX_EXPERIMENTAL_CXX0X__
String::String(String &&rval) {
init();
move(rval);
}
String::String(StringSumHelper &&rval) {
init();
move(rval);
}
String::String(std::initializer_list<char> list) {
init();
if (list.size() > 0) {
copy(list.begin(), list.size());
}
}
#endif
String::String(char c) {
init();
char buf[] = {c, '\0'};
*this = buf;
}
String::String(unsigned char value, unsigned char base) {
init();
char buf[1 + 8 * sizeof(unsigned char)];
utoa(value, buf, base);
*this = buf;
}
String::String(int value, unsigned char base) {
init();
char buf[2 + 8 * sizeof(int)];
itoa(value, buf, base);
*this = buf;
}
String::String(unsigned int value, unsigned char base) {
init();
char buf[1 + 8 * sizeof(unsigned int)];
utoa(value, buf, base);
*this = buf;
}
String::String(long value, unsigned char base) {
init();
char buf[2 + 8 * sizeof(long)];
ltoa(value, buf, base);
*this = buf;
}
String::String(unsigned long value, unsigned char base) {
init();
char buf[1 + 8 * sizeof(unsigned long)];
ultoa(value, buf, base);
*this = buf;
}
String::String(float value, unsigned int decimalPlaces) {
init();
char *buf = (char *)malloc(decimalPlaces + 42);
if (buf) {
*this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf);
free(buf);
} else {
*this = "nan";
log_e("No enough memory for the operation.");
}
}
String::String(double value, unsigned int decimalPlaces) {
init();
char *buf = (char *)malloc(decimalPlaces + 312);
if (buf) {
*this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf);
free(buf);
} else {
*this = "nan";
log_e("No enough memory for the operation.");
}
}
String::String(long long value, unsigned char base) {
init();
char buf[2 + 8 * sizeof(long long)];
lltoa(value, buf, base);
*this = buf;
}
String::String(unsigned long long value, unsigned char base) {
init();
char buf[1 + 8 * sizeof(unsigned long long)];
ulltoa(value, buf, base);
*this = buf;
}
String::~String() {
invalidate();
}
/*********************************************/
/* Memory Management */
/*********************************************/
inline void String::init(void) {
setSSO(false);
setBuffer(nullptr);
setCapacity(0);
setLen(0);
}
void String::invalidate(void) {
if (!isSSO() && wbuffer()) {
free(wbuffer());
}
init();
}
bool String::reserve(unsigned int size) {
if (buffer() && capacity() >= size) {
return true;
}
if (changeBuffer(size)) {
if (len() == 0) {
wbuffer()[0] = 0;
}
return true;
}
return false;
}
bool String::changeBuffer(unsigned int maxStrLen) {
// Can we use SSO here to avoid allocation?
if (maxStrLen < sizeof(sso.buff) - 1) {
if (isSSO() || !buffer()) {
// Already using SSO, nothing to do
size_t oldLen = len();
setSSO(true);
setLen(oldLen);
} else { // if bufptr && !isSSO()
// Using bufptr, need to shrink into sso.buff
char temp[sizeof(sso.buff)];
memcpy(temp, buffer(), maxStrLen);
free(wbuffer());
size_t oldLen = len();
setSSO(true);
memcpy(wbuffer(), temp, maxStrLen);
setLen(oldLen);
}
return true;
}
// Fallthrough to normal allocator
size_t newSize = (maxStrLen + 16) & (~0xf);
// Make sure we can fit newsize in the buffer
if (newSize > CAPACITY_MAX) {
return false;
}
size_t oldLen = len();
char *newbuffer = (char *)realloc(isSSO() ? nullptr : wbuffer(), newSize);
if (newbuffer) {
size_t oldSize = capacity() + 1; // include NULL.
if (isSSO()) {
// Copy the SSO buffer into allocated space
memmove(newbuffer, sso.buff, sizeof(sso.buff));
}
if (newSize > oldSize) {
memset(newbuffer + oldSize, 0, newSize - oldSize);
}
setSSO(false);
setCapacity(newSize - 1);
setBuffer(newbuffer);
setLen(oldLen); // Needed in case of SSO where len() never existed
return true;
}
return false;
}
/*********************************************/
/* Copy and Move */
/*********************************************/
String &String::copy(const char *cstr, unsigned int length) {
if (cstr == nullptr || !reserve(length)) {
invalidate();
return *this;
}
memmove(wbuffer(), cstr, length);
setLen(length);
return *this;
}
#ifdef __GXX_EXPERIMENTAL_CXX0X__
void String::move(String &rhs) {
if (buffer()) {
if (capacity() >= rhs.len()) {
// Use case: When 'reserve()' was called and the first
// assignment/append is the return value of a function.
if (rhs.len() && rhs.buffer()) {
memmove(wbuffer(), rhs.buffer(), rhs.length());
}
setLen(rhs.len());
rhs.invalidate();
return;
}
if (!isSSO()) {
free(wbuffer());
setBuffer(nullptr);
}
}
if (rhs.isSSO()) {
setSSO(true);
memmove(sso.buff, rhs.sso.buff, sizeof(sso.buff));
} else {
setSSO(false);
setBuffer(rhs.wbuffer());
}
setCapacity(rhs.capacity());
setLen(rhs.len());
rhs.init();
}
#endif
String &String::operator=(const String &rhs) {
if (this == &rhs) {
return *this;
}
return copy(rhs.buffer(), rhs.len());
}
#ifdef __GXX_EXPERIMENTAL_CXX0X__
String &String::operator=(String &&rval) {
if (this != &rval) {
move(rval);
}
return *this;
}
String &String::operator=(StringSumHelper &&rval) {
if (this != &rval) {
move(rval);
}
return *this;
}
#endif
String &String::operator=(const char *cstr) {
const uint32_t length = cstr ? strlen(cstr) : 0u;
return copy(cstr, length);
}
/*********************************************/
/* concat */
/*********************************************/
bool String::concat(const String &s) {
// Special case if we're concatting ourself (s += s;) since we may end up
// realloc'ing the buffer and moving s.buffer in the method called
if (&s == this) {
if (s.len() == 0) {
return true;
}
if (!s.buffer()) {
return false;
}
unsigned int newlen = 2 * len();
if (!reserve(newlen)) {
return false;
}
memmove(wbuffer() + len(), buffer(), len());
setLen(newlen);
return true;
}
return concat(s.buffer(), s.len());
}
bool String::concat(const char *cstr, unsigned int length) {
unsigned int newlen = len() + length;
if (!cstr) {
return false;
}
if (length == 0) {
return true;
}
if (!reserve(newlen)) {
return false;
}
if (cstr >= wbuffer() && cstr < wbuffer() + len()) {
// compatible with SSO in ram #6155 (case "x += x.c_str()")
memmove(wbuffer() + len(), cstr, length);
} else {
// compatible with source in flash #6367
memcpy_P(wbuffer() + len(), cstr, length);
}
setLen(newlen);
return true;
}
bool String::concat(const char *cstr) {
if (!cstr) {
return false;
}
return concat(cstr, strlen(cstr));
}
bool String::concat(char c) {
char buf[] = {c, '\0'};
return concat(buf, 1);
}
bool String::concat(unsigned char num) {
char buf[1 + 3 * sizeof(unsigned char)];
utoa(num, buf, 10);
return concat(buf, strlen(buf));
}
bool String::concat(int num) {
char buf[2 + 3 * sizeof(int)];
itoa(num, buf, 10);
return concat(buf, strlen(buf));
}
bool String::concat(unsigned int num) {
char buf[1 + 3 * sizeof(unsigned int)];
utoa(num, buf, 10);
return concat(buf, strlen(buf));
}
bool String::concat(long num) {
char buf[2 + 3 * sizeof(long)];
ltoa(num, buf, 10);
return concat(buf, strlen(buf));
}
bool String::concat(unsigned long num) {
char buf[1 + 3 * sizeof(unsigned long)];
ultoa(num, buf, 10);
return concat(buf, strlen(buf));
}
bool String::concat(long long num) {
char buf[2 + 3 * sizeof(long long)];
lltoa(num, buf, 10);
return concat(buf, strlen(buf));
}
bool String::concat(unsigned long long num) {
char buf[1 + 3 * sizeof(unsigned long long)];
ulltoa(num, buf, 10);
return concat(buf, strlen(buf));
}
bool String::concat(float num) {
char buf[20];
char *string = dtostrf(num, 4, 2, buf);
return concat(string, strlen(string));
}
bool String::concat(double num) {
char buf[20];
char *string = dtostrf(num, 4, 2, buf);
return concat(string, strlen(string));
}
/*********************************************/
/* Concatenate */
/*********************************************/
StringSumHelper &operator+(const StringSumHelper &lhs, const String &rhs) {
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
if (!a.concat(rhs.buffer(), rhs.len())) {
a.invalidate();
}
return a;
}
StringSumHelper &operator+(const StringSumHelper &lhs, const char *cstr) {
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
if (!cstr || !a.concat(cstr, strlen(cstr))) {
a.invalidate();
}
return a;
}
StringSumHelper &operator+(const StringSumHelper &lhs, char c) {
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
if (!a.concat(c)) {
a.invalidate();
}
return a;
}
StringSumHelper &operator+(const StringSumHelper &lhs, unsigned char num) {
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
if (!a.concat(num)) {
a.invalidate();
}
return a;
}
StringSumHelper &operator+(const StringSumHelper &lhs, int num) {
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
if (!a.concat(num)) {
a.invalidate();
}
return a;
}
StringSumHelper &operator+(const StringSumHelper &lhs, unsigned int num) {
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
if (!a.concat(num)) {
a.invalidate();
}
return a;
}
StringSumHelper &operator+(const StringSumHelper &lhs, long num) {
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
if (!a.concat(num)) {
a.invalidate();
}
return a;
}
StringSumHelper &operator+(const StringSumHelper &lhs, unsigned long num) {
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
if (!a.concat(num)) {
a.invalidate();
}
return a;
}
StringSumHelper &operator+(const StringSumHelper &lhs, float num) {
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
if (!a.concat(num)) {
a.invalidate();
}
return a;
}
StringSumHelper &operator+(const StringSumHelper &lhs, double num) {
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
if (!a.concat(num)) {
a.invalidate();
}
return a;
}
StringSumHelper &operator+(const StringSumHelper &lhs, long long num) {
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
if (!a.concat(num)) {
a.invalidate();
}
return a;
}
StringSumHelper &operator+(const StringSumHelper &lhs, unsigned long long num) {
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
if (!a.concat(num)) {
a.invalidate();
}
return a;
}
/*********************************************/
/* Comparison */
/*********************************************/
int String::compareTo(const String &s) const {
if (!buffer() || !s.buffer()) {
if (s.buffer() && s.len() > 0) {
return 0 - *(unsigned char *)s.buffer();
}
if (buffer() && len() > 0) {
return *(unsigned char *)buffer();
}
return 0;
}
return strcmp(buffer(), s.buffer());
}
bool String::equals(const String &s2) const {
return (len() == s2.len() && compareTo(s2) == 0);
}
bool String::equals(const char *cstr) const {
if (len() == 0) {
return (cstr == NULL || *cstr == 0);
}
if (cstr == NULL) {
return buffer()[0] == 0;
}
return strcmp(buffer(), cstr) == 0;
}
bool String::operator<(const String &rhs) const {
return compareTo(rhs) < 0;
}
bool String::operator>(const String &rhs) const {
return compareTo(rhs) > 0;
}
bool String::operator<=(const String &rhs) const {
return compareTo(rhs) <= 0;
}
bool String::operator>=(const String &rhs) const {
return compareTo(rhs) >= 0;
}
bool String::equalsIgnoreCase(const String &s2) const {
if (this == &s2) {
return true;
}
if (len() != s2.len()) {
return false;
}
if (len() == 0) {
return true;
}
const char *p1 = buffer();
const char *p2 = s2.buffer();
while (*p1) {
if (tolower(*p1++) != tolower(*p2++)) {
return false;
}
}
return true;
}
unsigned char String::equalsConstantTime(const String &s2) const {
// To avoid possible time-based attacks present function
// compares given strings in a constant time.
if (len() != s2.len()) {
return 0;
}
//at this point lengths are the same
if (len() == 0) {
return 1;
}
//at this point lengths are the same and non-zero
const char *p1 = buffer();
const char *p2 = s2.buffer();
unsigned int equalchars = 0;
unsigned int diffchars = 0;
while (*p1) {
if (*p1 == *p2) {
++equalchars;
} else {
++diffchars;
}
++p1;
++p2;
}
//the following should force a constant time eval of the condition without a compiler "logical shortcut"
unsigned char equalcond = (equalchars == len());
unsigned char diffcond = (diffchars == 0);
return (equalcond & diffcond); //bitwise AND
}
bool String::startsWith(const String &s2) const {
if (len() < s2.len()) {
return false;
}
return startsWith(s2, 0);
}
bool String::startsWith(const String &s2, unsigned int offset) const {
if (offset > (unsigned)(len() - s2.len()) || !buffer() || !s2.buffer()) {
return false;
}
return strncmp(&buffer()[offset], s2.buffer(), s2.len()) == 0;
}
bool String::endsWith(const String &s2) const {
if (len() < s2.len() || !buffer() || !s2.buffer()) {
return false;
}
return strcmp(&buffer()[len() - s2.len()], s2.buffer()) == 0;
}
/*********************************************/
/* Character Access */
/*********************************************/
char String::charAt(unsigned int loc) const {
return operator[](loc);
}
void String::setCharAt(unsigned int loc, char c) {
if (loc < len()) {
wbuffer()[loc] = c;
}
}
char &String::operator[](unsigned int index) {
static char dummy_writable_char;
if (index >= len() || !buffer()) {
dummy_writable_char = 0;
return dummy_writable_char;
}
return wbuffer()[index];
}
char String::operator[](unsigned int index) const {
if (index >= len() || !buffer()) {
return 0;
}
return buffer()[index];
}
void String::getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index) const {
if (!bufsize || !buf) {
return;
}
if (index >= len()) {
buf[0] = 0;
return;
}
unsigned int n = bufsize - 1;
if (n > len() - index) {
n = len() - index;
}
strncpy((char *)buf, buffer() + index, n);
buf[n] = 0;
}
/*********************************************/
/* Search */
/*********************************************/
int String::indexOf(char c) const {
return indexOf(c, 0);
}
int String::indexOf(char ch, unsigned int fromIndex) const {
if (fromIndex >= len()) {
return -1;
}
const char *temp = strchr(buffer() + fromIndex, ch);
if (temp == NULL) {
return -1;
}
return temp - buffer();
}
int String::indexOf(const String &s2) const {
return indexOf(s2, 0);
}
int String::indexOf(const String &s2, unsigned int fromIndex) const {
if (fromIndex >= len()) {
return -1;
}
const char *found = strstr(buffer() + fromIndex, s2.buffer());
if (found == NULL) {
return -1;
}
return found - buffer();
}
int String::lastIndexOf(char theChar) const {
return lastIndexOf(theChar, len() - 1);
}
int String::lastIndexOf(char ch, unsigned int fromIndex) const {
if (fromIndex >= len()) {
return -1;
}
char tempchar = buffer()[fromIndex + 1];
wbuffer()[fromIndex + 1] = '\0';
char *temp = strrchr(wbuffer(), ch);
wbuffer()[fromIndex + 1] = tempchar;
if (temp == NULL) {
return -1;
}
const int rv = temp - buffer();
if (rv >= len()) {
return -1;
}
return rv;
}
int String::lastIndexOf(const String &s2) const {
return lastIndexOf(s2, len() - s2.len());
}
int String::lastIndexOf(const String &s2, unsigned int fromIndex) const {
if (s2.len() == 0 || len() == 0 || s2.len() > len()) {
return -1;
}
if (fromIndex >= len()) {
fromIndex = len() - 1;
}
int found = -1;
for (char *p = wbuffer(); p <= wbuffer() + fromIndex; p++) {
p = strstr(p, s2.buffer());
if (!p) {
break;
}
if ((unsigned int)(p - wbuffer()) <= fromIndex) {
found = p - buffer();
}
}
return found;
}
String String::substring(unsigned int left, unsigned int right) const {
if (left > right) {
unsigned int temp = right;
right = left;
left = temp;
}
String out;
if (left >= len()) {
return out;
}
if (right > len()) {
right = len();
}
out.copy(buffer() + left, right - left);
return out;
}
/*********************************************/
/* Modification */
/*********************************************/
void String::replace(char find, char replace) {
if (!buffer()) {
return;
}
for (char *p = wbuffer(); *p; p++) {
if (*p == find) {
*p = replace;
}
}
}
void String::replace(const String &find, const String &replace) {
if (len() == 0 || find.len() == 0) {
return;
}
int diff = replace.len() - find.len();
char *readFrom = wbuffer();
char *foundAt;
if (diff == 0) {
while ((foundAt = strstr(readFrom, find.buffer())) != NULL) {
memmove(foundAt, replace.buffer(), replace.len());
readFrom = foundAt + replace.len();
}
} else if (diff < 0) {
char *writeTo = wbuffer();
unsigned int l = len();
while ((foundAt = strstr(readFrom, find.buffer())) != NULL) {
unsigned int n = foundAt - readFrom;
memmove(writeTo, readFrom, n);
writeTo += n;
memmove(writeTo, replace.buffer(), replace.len());
writeTo += replace.len();
readFrom = foundAt + find.len();
l += diff;
}
memmove(writeTo, readFrom, strlen(readFrom) + 1);
setLen(l);
} else {
unsigned int size = len(); // compute size needed for result
while ((foundAt = strstr(readFrom, find.buffer())) != NULL) {
readFrom = foundAt + find.len();
size += diff;
}
if (size == len()) {
return;
}
if (size > capacity() && !changeBuffer(size)) {
log_w("String.Replace() Insufficient space to replace string");
return;
}
int index = len() - 1;
while (index >= 0 && (index = lastIndexOf(find, index)) >= 0) {
readFrom = wbuffer() + index + find.len();
memmove(readFrom + diff, readFrom, len() - (readFrom - buffer()));
int newLen = len() + diff;
memmove(wbuffer() + index, replace.buffer(), replace.len());
setLen(newLen);
wbuffer()[newLen] = 0;
index--;
}
}
}
void String::remove(unsigned int index) {
// Pass the biggest integer as the count. The remove method
// below will take care of truncating it at the end of the
// string.
remove(index, (unsigned int)-1);
}
void String::remove(unsigned int index, unsigned int count) {
if (index >= len()) {
return;
}
if (count <= 0) {
return;
}
if (count > len() - index) {
count = len() - index;
}
char *writeTo = wbuffer() + index;
unsigned int newlen = len() - count;
memmove(writeTo, wbuffer() + index + count, newlen - index);
setLen(newlen);
wbuffer()[newlen] = 0;
}
void String::toLowerCase(void) {
if (!buffer()) {
return;
}
for (char *p = wbuffer(); *p; p++) {
*p = tolower(*p);
}
}
void String::toUpperCase(void) {
if (!buffer()) {
return;
}
for (char *p = wbuffer(); *p; p++) {
*p = toupper(*p);
}
}
void String::trim(void) {
if (!buffer() || len() == 0) {
return;
}
char *begin = wbuffer();
while (isspace(*begin)) {
begin++;
}
char *end = wbuffer() + len() - 1;
while (isspace(*end) && end >= begin) {
end--;
}
unsigned int newlen = end + 1 - begin;
if (begin > buffer()) {
memmove(wbuffer(), begin, newlen);
}
setLen(newlen);
wbuffer()[newlen] = 0;
}
/*********************************************/
/* Parsing / Conversion */
/*********************************************/
long String::toInt(void) const {
if (buffer()) {
return atol(buffer());
}
return 0;
}
float String::toFloat(void) const {
if (buffer()) {
return static_cast<float>(atof(buffer()));
}
return 0;
}
double String::toDouble(void) const {
if (buffer()) {
return atof(buffer());
}
return 0.0;
}
// global empty string to allow returning const String& with nothing
const String emptyString;
+426
View File
@@ -0,0 +1,426 @@
/*
WString.h - String library for Wiring & Arduino
...mostly rewritten by Paul Stoffregen...
Copyright (c) 2009-10 Hernando Barragan. All right reserved.
Copyright 2011, Paul Stoffregen, paul@pjrc.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
*/
#ifndef String_class_h
#define String_class_h
#ifdef __cplusplus
#include <pgmspace.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <ctype.h>
#ifdef __GXX_EXPERIMENTAL_CXX0X__
#include <initializer_list>
#endif
// A pure abstract class forward used as a means to proide a unique pointer type
// but really is never defined.
class __FlashStringHelper;
#define FPSTR(str_pointer) (reinterpret_cast<const __FlashStringHelper *>(str_pointer))
#define F(string_literal) (FPSTR(PSTR(string_literal)))
// An inherited class for holding the result of a concatenation. These
// result objects are assumed to be writable by subsequent concatenations.
class StringSumHelper;
// The string class
class String {
// use a function pointer to allow for "if (s)" without the
// complications of an operator bool(). for more information, see:
// http://www.artima.com/cppsource/safebool.html
typedef void (String::*StringIfHelperType)() const;
void StringIfHelper() const {}
public:
// constructors
// creates a copy of the initial value.
// if the initial value is null or invalid, or if memory allocation
// fails, the string will be marked as invalid (i.e. "if (s)" will
// be false).
String(const char *cstr = "");
String(const char *cstr, unsigned int length);
#ifdef __GXX_EXPERIMENTAL_CXX0X__
String(const uint8_t *cstr, unsigned int length) : String(reinterpret_cast<const char *>(cstr), length) {}
String(std::initializer_list<char> list);
#endif
String(const String &str);
String(const __FlashStringHelper *str) : String(reinterpret_cast<const char *>(str)) {}
#ifdef __GXX_EXPERIMENTAL_CXX0X__
String(String &&rval);
String(StringSumHelper &&rval);
#endif
explicit String(char c);
explicit String(unsigned char, unsigned char base = 10);
explicit String(int, unsigned char base = 10);
explicit String(unsigned int, unsigned char base = 10);
explicit String(long, unsigned char base = 10);
explicit String(unsigned long, unsigned char base = 10);
explicit String(float, unsigned int decimalPlaces = 2);
explicit String(double, unsigned int decimalPlaces = 2);
explicit String(long long, unsigned char base = 10);
explicit String(unsigned long long, unsigned char base = 10);
~String(void);
// memory management
// return true on success, false on failure (in which case, the string
// is left unchanged). reserve(0), if successful, will validate an
// invalid string (i.e., "if (s)" will be true afterwards)
bool reserve(unsigned int size);
inline unsigned int length(void) const {
if (buffer()) {
return len();
} else {
return 0;
}
}
inline void clear(void) {
setLen(0);
}
inline bool isEmpty(void) const {
return length() == 0;
}
// creates a copy of the assigned value. if the value is null or
// invalid, or if the memory allocation fails, the string will be
// marked as invalid ("if (s)" will be false).
String &operator=(const String &rhs);
String &operator=(const char *cstr);
String &operator=(const __FlashStringHelper *str) {
return *this = reinterpret_cast<const char *>(str);
}
#ifdef __GXX_EXPERIMENTAL_CXX0X__
String &operator=(String &&rval);
String &operator=(StringSumHelper &&rval);
#endif
// concatenate (works w/ built-in types, same as assignment)
// returns true on success, false on failure (in which case, the string
// is left unchanged). if the argument is null or invalid, the
// concatenation is considered unsuccessful.
bool concat(const String &str);
bool concat(const char *cstr);
bool concat(const char *cstr, unsigned int length);
bool concat(const uint8_t *cstr, unsigned int length) {
return concat(reinterpret_cast<const char *>(cstr), length);
}
bool concat(char c);
bool concat(unsigned char c);
bool concat(int num);
bool concat(unsigned int num);
bool concat(long num);
bool concat(unsigned long num);
bool concat(float num);
bool concat(double num);
bool concat(long long num);
bool concat(unsigned long long num);
bool concat(const __FlashStringHelper *str) {
return concat(reinterpret_cast<const char *>(str));
}
// if there's not enough memory for the concatenated value, the string
// will be left unchanged (but this isn't signaled in any way)
String &operator+=(const String &rhs) {
concat(rhs);
return (*this);
}
String &operator+=(const char *cstr) {
concat(cstr);
return (*this);
}
String &operator+=(char c) {
concat(c);
return (*this);
}
String &operator+=(unsigned char num) {
concat(num);
return (*this);
}
String &operator+=(int num) {
concat(num);
return (*this);
}
String &operator+=(unsigned int num) {
concat(num);
return (*this);
}
String &operator+=(long num) {
concat(num);
return (*this);
}
String &operator+=(unsigned long num) {
concat(num);
return (*this);
}
String &operator+=(float num) {
concat(num);
return (*this);
}
String &operator+=(double num) {
concat(num);
return (*this);
}
String &operator+=(long long num) {
concat(num);
return (*this);
}
String &operator+=(unsigned long long num) {
concat(num);
return (*this);
}
String &operator+=(const __FlashStringHelper *str) {
return *this += reinterpret_cast<const char *>(str);
}
friend StringSumHelper &operator+(const StringSumHelper &lhs, const String &rhs);
friend StringSumHelper &operator+(const StringSumHelper &lhs, const char *cstr);
friend StringSumHelper &operator+(const StringSumHelper &lhs, char c);
friend StringSumHelper &operator+(const StringSumHelper &lhs, unsigned char num);
friend StringSumHelper &operator+(const StringSumHelper &lhs, int num);
friend StringSumHelper &operator+(const StringSumHelper &lhs, unsigned int num);
friend StringSumHelper &operator+(const StringSumHelper &lhs, long num);
friend StringSumHelper &operator+(const StringSumHelper &lhs, unsigned long num);
friend StringSumHelper &operator+(const StringSumHelper &lhs, float num);
friend StringSumHelper &operator+(const StringSumHelper &lhs, double num);
friend StringSumHelper &operator+(const StringSumHelper &lhs, long long num);
friend StringSumHelper &operator+(const StringSumHelper &lhs, unsigned long long num);
// comparison (only works w/ Strings and "strings")
operator StringIfHelperType() const {
return buffer() ? &String::StringIfHelper : 0;
}
int compareTo(const String &s) const;
bool equals(const String &s) const;
bool equals(const char *cstr) const;
bool operator==(const String &rhs) const {
return equals(rhs);
}
bool operator==(const char *cstr) const {
return equals(cstr);
}
bool operator!=(const String &rhs) const {
return !equals(rhs);
}
bool operator!=(const char *cstr) const {
return !equals(cstr);
}
bool operator<(const String &rhs) const;
bool operator>(const String &rhs) const;
bool operator<=(const String &rhs) const;
bool operator>=(const String &rhs) const;
bool equalsIgnoreCase(const String &s) const;
unsigned char equalsConstantTime(const String &s) const;
bool startsWith(const String &prefix) const;
bool startsWith(const char *prefix) const {
return this->startsWith(String(prefix));
}
bool startsWith(const __FlashStringHelper *prefix) const {
return this->startsWith(reinterpret_cast<const char *>(prefix));
}
bool startsWith(const String &prefix, unsigned int offset) const;
bool endsWith(const String &suffix) const;
bool endsWith(const char *suffix) const {
return this->endsWith(String(suffix));
}
bool endsWith(const __FlashStringHelper *suffix) const {
return this->endsWith(reinterpret_cast<const char *>(suffix));
}
// character access
char charAt(unsigned int index) const;
void setCharAt(unsigned int index, char c);
char operator[](unsigned int index) const;
char &operator[](unsigned int index);
void getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index = 0) const;
void toCharArray(char *buf, unsigned int bufsize, unsigned int index = 0) const {
getBytes((unsigned char *)buf, bufsize, index);
}
const char *c_str() const {
return buffer();
}
char *begin() {
return wbuffer();
}
char *end() {
return wbuffer() + length();
}
const char *begin() const {
return c_str();
}
const char *end() const {
return c_str() + length();
}
// search
int indexOf(char ch) const;
int indexOf(char ch, unsigned int fromIndex) const;
int indexOf(const String &str) const;
int indexOf(const String &str, unsigned int fromIndex) const;
int lastIndexOf(char ch) const;
int lastIndexOf(char ch, unsigned int fromIndex) const;
int lastIndexOf(const String &str) const;
int lastIndexOf(const String &str, unsigned int fromIndex) const;
String substring(unsigned int beginIndex) const {
return substring(beginIndex, len());
}
String substring(unsigned int beginIndex, unsigned int endIndex) const;
// modification
void replace(char find, char replace);
void replace(const String &find, const String &replace);
void replace(const char *find, const String &replace) {
this->replace(String(find), replace);
}
void replace(const __FlashStringHelper *find, const String &replace) {
this->replace(reinterpret_cast<const char *>(find), replace);
}
void replace(const char *find, const char *replace) {
this->replace(String(find), String(replace));
}
void replace(const __FlashStringHelper *find, const char *replace) {
this->replace(reinterpret_cast<const char *>(find), String(replace));
}
void replace(const __FlashStringHelper *find, const __FlashStringHelper *replace) {
this->replace(reinterpret_cast<const char *>(find), reinterpret_cast<const char *>(replace));
}
void remove(unsigned int index);
void remove(unsigned int index, unsigned int count);
void toLowerCase(void);
void toUpperCase(void);
void trim(void);
// parsing/conversion
long toInt(void) const;
float toFloat(void) const;
double toDouble(void) const;
protected:
// Contains the string info when we're not in SSO mode
struct _ptr {
char *buff;
uint32_t cap;
uint32_t len;
};
// This allows strings up up to 11 (10 + \0 termination) without any extra space.
enum {
SSOSIZE = sizeof(struct _ptr) + 4 - 1
}; // Characters to allocate space for SSO, must be 12 or more
struct _sso {
char buff[SSOSIZE];
unsigned char len : 7; // Ensure only one byte is allocated by GCC for the bitfields
unsigned char isSSO : 1;
} __attribute__((packed)); // Ensure that GCC doesn't expand the flag byte to a 32-bit word for alignment issues
#ifdef BOARD_HAS_PSRAM
enum {
CAPACITY_MAX = 3145728
};
#else
enum {
CAPACITY_MAX = 65535
};
#endif
union {
struct _ptr ptr;
struct _sso sso;
};
// Accessor functions
inline bool isSSO() const {
return sso.isSSO;
}
inline unsigned int len() const {
return isSSO() ? sso.len : ptr.len;
}
inline unsigned int capacity() const {
return isSSO() ? (unsigned int)SSOSIZE - 1 : ptr.cap;
} // Size of max string not including terminal NUL
inline void setSSO(bool set) {
sso.isSSO = set;
}
inline void setLen(int len) {
if (isSSO()) {
sso.len = len;
sso.buff[len] = 0;
} else {
ptr.len = len;
if (ptr.buff) {
ptr.buff[len] = 0;
}
}
}
inline void setCapacity(int cap) {
if (!isSSO()) {
ptr.cap = cap;
}
}
inline void setBuffer(char *buff) {
if (!isSSO()) {
ptr.buff = buff;
}
}
// Buffer accessor functions
inline const char *buffer() const {
return reinterpret_cast<const char *>(isSSO() ? sso.buff : ptr.buff);
}
inline char *wbuffer() const {
return isSSO() ? const_cast<char *>(sso.buff) : ptr.buff;
} // Writable version of buffer
protected:
void init(void);
void invalidate(void);
bool changeBuffer(unsigned int maxStrLen);
// copy and move
String &copy(const char *cstr, unsigned int length);
String &copy(const __FlashStringHelper *pstr, unsigned int length) {
return copy(reinterpret_cast<const char *>(pstr), length);
}
#ifdef __GXX_EXPERIMENTAL_CXX0X__
void move(String &rhs);
#endif
};
class StringSumHelper : public String {
public:
StringSumHelper(const String &s) : String(s) {}
StringSumHelper(const char *p) : String(p) {}
StringSumHelper(char c) : String(c) {}
StringSumHelper(unsigned char num) : String(num) {}
StringSumHelper(int num) : String(num) {}
StringSumHelper(unsigned int num) : String(num) {}
StringSumHelper(long num) : String(num) {}
StringSumHelper(unsigned long num) : String(num) {}
StringSumHelper(float num) : String(num) {}
StringSumHelper(double num) : String(num) {}
StringSumHelper(long long num) : String(num) {}
StringSumHelper(unsigned long long num) : String(num) {}
};
inline StringSumHelper &operator+(const StringSumHelper &lhs, const __FlashStringHelper *rhs) {
return lhs + reinterpret_cast<const char *>(rhs);
}
extern const String emptyString;
#endif // __cplusplus
#endif // String_class_h
+1
View File
@@ -0,0 +1 @@
#include "lwip/apps/sntp.h"
+61
View File
@@ -0,0 +1,61 @@
/**
* base64.cpp
*
* Created on: 09.12.2015
*
* Copyright (c) 2015 Markus Sattler. All rights reserved.
* This file is part of the ESP31B core for Arduino.
*
* 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"
extern "C" {
#include "libb64/cdecode.h"
#include "libb64/cencode.h"
}
#include "base64.h"
/**
* convert input data to base64
* @param data const uint8_t *
* @param length size_t
* @return String
*/
String base64::encode(const uint8_t *data, size_t length) {
size_t size = base64_encode_expected_len(length) + 1;
char *buffer = (char *)malloc(size);
if (buffer) {
base64_encodestate _state;
base64_init_encodestate(&_state);
int len = base64_encode_block((const char *)&data[0], length, &buffer[0], &_state);
len = base64_encode_blockend((buffer + len), &_state);
String base64 = String(buffer);
free(buffer);
return base64;
}
return String("-FAIL-");
}
/**
* convert input data to base64
* @param text const String&
* @return String
*/
String base64::encode(const String &text) {
return base64::encode((uint8_t *)text.c_str(), text.length());
}
+12
View File
@@ -0,0 +1,12 @@
#ifndef CORE_BASE64_H_
#define CORE_BASE64_H_
class base64 {
public:
static String encode(const uint8_t *data, size_t length);
static String encode(const String &text);
private:
};
#endif /* CORE_BASE64_H_ */
+552
View File
@@ -0,0 +1,552 @@
/*
binary.h - Definitions for binary constants
Deprecated -- use 0b binary literals instead
Copyright (c) 2006 David A. Mellis. All right 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
*/
#ifndef Binary_h
#define Binary_h
/* If supported, 0b binary literals are preferable to these constants.
* In that case, warn the user about these being deprecated (if possible). */
#if __cplusplus >= 201402L
/* C++14 introduces binary literals; C++11 introduces [[deprecated()]] */
#define DEPRECATED(x) [[deprecated("use " #x " instead")]]
#elif __GNUC__ >= 6
/* GCC 4.3 supports binary literals; GCC 6 supports __deprecated__ on enums*/
#define DEPRECATED(x) __attribute__((__deprecated__("use " #x " instead")))
#else
/* binary literals not supported, or "deprecated" warning not displayable */
#define DEPRECATED(x)
#endif
enum {
B0 DEPRECATED(0b0) = 0,
B00 DEPRECATED(0b00) = 0,
B000 DEPRECATED(0b000) = 0,
B0000 DEPRECATED(0b0000) = 0,
B00000 DEPRECATED(0b00000) = 0,
B000000 DEPRECATED(0b000000) = 0,
B0000000 DEPRECATED(0b0000000) = 0,
B00000000 DEPRECATED(0b00000000) = 0,
B1 DEPRECATED(0b1) = 1,
B01 DEPRECATED(0b01) = 1,
B001 DEPRECATED(0b001) = 1,
B0001 DEPRECATED(0b0001) = 1,
B00001 DEPRECATED(0b00001) = 1,
B000001 DEPRECATED(0b000001) = 1,
B0000001 DEPRECATED(0b0000001) = 1,
B00000001 DEPRECATED(0b00000001) = 1,
B10 DEPRECATED(0b10) = 2,
B010 DEPRECATED(0b010) = 2,
B0010 DEPRECATED(0b0010) = 2,
B00010 DEPRECATED(0b00010) = 2,
B000010 DEPRECATED(0b000010) = 2,
B0000010 DEPRECATED(0b0000010) = 2,
B00000010 DEPRECATED(0b00000010) = 2,
B11 DEPRECATED(0b11) = 3,
B011 DEPRECATED(0b011) = 3,
B0011 DEPRECATED(0b0011) = 3,
B00011 DEPRECATED(0b00011) = 3,
B000011 DEPRECATED(0b000011) = 3,
B0000011 DEPRECATED(0b0000011) = 3,
B00000011 DEPRECATED(0b00000011) = 3,
B100 DEPRECATED(0b100) = 4,
B0100 DEPRECATED(0b0100) = 4,
B00100 DEPRECATED(0b00100) = 4,
B000100 DEPRECATED(0b000100) = 4,
B0000100 DEPRECATED(0b0000100) = 4,
B00000100 DEPRECATED(0b00000100) = 4,
B101 DEPRECATED(0b101) = 5,
B0101 DEPRECATED(0b0101) = 5,
B00101 DEPRECATED(0b00101) = 5,
B000101 DEPRECATED(0b000101) = 5,
B0000101 DEPRECATED(0b0000101) = 5,
B00000101 DEPRECATED(0b00000101) = 5,
B110 DEPRECATED(0b110) = 6,
B0110 DEPRECATED(0b0110) = 6,
B00110 DEPRECATED(0b00110) = 6,
B000110 DEPRECATED(0b000110) = 6,
B0000110 DEPRECATED(0b0000110) = 6,
B00000110 DEPRECATED(0b00000110) = 6,
B111 DEPRECATED(0b111) = 7,
B0111 DEPRECATED(0b0111) = 7,
B00111 DEPRECATED(0b00111) = 7,
B000111 DEPRECATED(0b000111) = 7,
B0000111 DEPRECATED(0b0000111) = 7,
B00000111 DEPRECATED(0b00000111) = 7,
B1000 DEPRECATED(0b1000) = 8,
B01000 DEPRECATED(0b01000) = 8,
B001000 DEPRECATED(0b001000) = 8,
B0001000 DEPRECATED(0b0001000) = 8,
B00001000 DEPRECATED(0b00001000) = 8,
B1001 DEPRECATED(0b1001) = 9,
B01001 DEPRECATED(0b01001) = 9,
B001001 DEPRECATED(0b001001) = 9,
B0001001 DEPRECATED(0b0001001) = 9,
B00001001 DEPRECATED(0b00001001) = 9,
B1010 DEPRECATED(0b1010) = 10,
B01010 DEPRECATED(0b01010) = 10,
B001010 DEPRECATED(0b001010) = 10,
B0001010 DEPRECATED(0b0001010) = 10,
B00001010 DEPRECATED(0b00001010) = 10,
B1011 DEPRECATED(0b1011) = 11,
B01011 DEPRECATED(0b01011) = 11,
B001011 DEPRECATED(0b001011) = 11,
B0001011 DEPRECATED(0b0001011) = 11,
B00001011 DEPRECATED(0b00001011) = 11,
B1100 DEPRECATED(0b1100) = 12,
B01100 DEPRECATED(0b01100) = 12,
B001100 DEPRECATED(0b001100) = 12,
B0001100 DEPRECATED(0b0001100) = 12,
B00001100 DEPRECATED(0b00001100) = 12,
B1101 DEPRECATED(0b1101) = 13,
B01101 DEPRECATED(0b01101) = 13,
B001101 DEPRECATED(0b001101) = 13,
B0001101 DEPRECATED(0b0001101) = 13,
B00001101 DEPRECATED(0b00001101) = 13,
B1110 DEPRECATED(0b1110) = 14,
B01110 DEPRECATED(0b01110) = 14,
B001110 DEPRECATED(0b001110) = 14,
B0001110 DEPRECATED(0b0001110) = 14,
B00001110 DEPRECATED(0b00001110) = 14,
B1111 DEPRECATED(0b1111) = 15,
B01111 DEPRECATED(0b01111) = 15,
B001111 DEPRECATED(0b001111) = 15,
B0001111 DEPRECATED(0b0001111) = 15,
B00001111 DEPRECATED(0b00001111) = 15,
B10000 DEPRECATED(0b10000) = 16,
B010000 DEPRECATED(0b010000) = 16,
B0010000 DEPRECATED(0b0010000) = 16,
B00010000 DEPRECATED(0b00010000) = 16,
B10001 DEPRECATED(0b10001) = 17,
B010001 DEPRECATED(0b010001) = 17,
B0010001 DEPRECATED(0b0010001) = 17,
B00010001 DEPRECATED(0b00010001) = 17,
B10010 DEPRECATED(0b10010) = 18,
B010010 DEPRECATED(0b010010) = 18,
B0010010 DEPRECATED(0b0010010) = 18,
B00010010 DEPRECATED(0b00010010) = 18,
B10011 DEPRECATED(0b10011) = 19,
B010011 DEPRECATED(0b010011) = 19,
B0010011 DEPRECATED(0b0010011) = 19,
B00010011 DEPRECATED(0b00010011) = 19,
B10100 DEPRECATED(0b10100) = 20,
B010100 DEPRECATED(0b010100) = 20,
B0010100 DEPRECATED(0b0010100) = 20,
B00010100 DEPRECATED(0b00010100) = 20,
B10101 DEPRECATED(0b10101) = 21,
B010101 DEPRECATED(0b010101) = 21,
B0010101 DEPRECATED(0b0010101) = 21,
B00010101 DEPRECATED(0b00010101) = 21,
B10110 DEPRECATED(0b10110) = 22,
B010110 DEPRECATED(0b010110) = 22,
B0010110 DEPRECATED(0b0010110) = 22,
B00010110 DEPRECATED(0b00010110) = 22,
B10111 DEPRECATED(0b10111) = 23,
B010111 DEPRECATED(0b010111) = 23,
B0010111 DEPRECATED(0b0010111) = 23,
B00010111 DEPRECATED(0b00010111) = 23,
B11000 DEPRECATED(0b11000) = 24,
B011000 DEPRECATED(0b011000) = 24,
B0011000 DEPRECATED(0b0011000) = 24,
B00011000 DEPRECATED(0b00011000) = 24,
B11001 DEPRECATED(0b11001) = 25,
B011001 DEPRECATED(0b011001) = 25,
B0011001 DEPRECATED(0b0011001) = 25,
B00011001 DEPRECATED(0b00011001) = 25,
B11010 DEPRECATED(0b11010) = 26,
B011010 DEPRECATED(0b011010) = 26,
B0011010 DEPRECATED(0b0011010) = 26,
B00011010 DEPRECATED(0b00011010) = 26,
B11011 DEPRECATED(0b11011) = 27,
B011011 DEPRECATED(0b011011) = 27,
B0011011 DEPRECATED(0b0011011) = 27,
B00011011 DEPRECATED(0b00011011) = 27,
B11100 DEPRECATED(0b11100) = 28,
B011100 DEPRECATED(0b011100) = 28,
B0011100 DEPRECATED(0b0011100) = 28,
B00011100 DEPRECATED(0b00011100) = 28,
B11101 DEPRECATED(0b11101) = 29,
B011101 DEPRECATED(0b011101) = 29,
B0011101 DEPRECATED(0b0011101) = 29,
B00011101 DEPRECATED(0b00011101) = 29,
B11110 DEPRECATED(0b11110) = 30,
B011110 DEPRECATED(0b011110) = 30,
B0011110 DEPRECATED(0b0011110) = 30,
B00011110 DEPRECATED(0b00011110) = 30,
B11111 DEPRECATED(0b11111) = 31,
B011111 DEPRECATED(0b011111) = 31,
B0011111 DEPRECATED(0b0011111) = 31,
B00011111 DEPRECATED(0b00011111) = 31,
B100000 DEPRECATED(0b100000) = 32,
B0100000 DEPRECATED(0b0100000) = 32,
B00100000 DEPRECATED(0b00100000) = 32,
B100001 DEPRECATED(0b100001) = 33,
B0100001 DEPRECATED(0b0100001) = 33,
B00100001 DEPRECATED(0b00100001) = 33,
B100010 DEPRECATED(0b100010) = 34,
B0100010 DEPRECATED(0b0100010) = 34,
B00100010 DEPRECATED(0b00100010) = 34,
B100011 DEPRECATED(0b100011) = 35,
B0100011 DEPRECATED(0b0100011) = 35,
B00100011 DEPRECATED(0b00100011) = 35,
B100100 DEPRECATED(0b100100) = 36,
B0100100 DEPRECATED(0b0100100) = 36,
B00100100 DEPRECATED(0b00100100) = 36,
B100101 DEPRECATED(0b100101) = 37,
B0100101 DEPRECATED(0b0100101) = 37,
B00100101 DEPRECATED(0b00100101) = 37,
B100110 DEPRECATED(0b100110) = 38,
B0100110 DEPRECATED(0b0100110) = 38,
B00100110 DEPRECATED(0b00100110) = 38,
B100111 DEPRECATED(0b100111) = 39,
B0100111 DEPRECATED(0b0100111) = 39,
B00100111 DEPRECATED(0b00100111) = 39,
B101000 DEPRECATED(0b101000) = 40,
B0101000 DEPRECATED(0b0101000) = 40,
B00101000 DEPRECATED(0b00101000) = 40,
B101001 DEPRECATED(0b101001) = 41,
B0101001 DEPRECATED(0b0101001) = 41,
B00101001 DEPRECATED(0b00101001) = 41,
B101010 DEPRECATED(0b101010) = 42,
B0101010 DEPRECATED(0b0101010) = 42,
B00101010 DEPRECATED(0b00101010) = 42,
B101011 DEPRECATED(0b101011) = 43,
B0101011 DEPRECATED(0b0101011) = 43,
B00101011 DEPRECATED(0b00101011) = 43,
B101100 DEPRECATED(0b101100) = 44,
B0101100 DEPRECATED(0b0101100) = 44,
B00101100 DEPRECATED(0b00101100) = 44,
B101101 DEPRECATED(0b101101) = 45,
B0101101 DEPRECATED(0b0101101) = 45,
B00101101 DEPRECATED(0b00101101) = 45,
B101110 DEPRECATED(0b101110) = 46,
B0101110 DEPRECATED(0b0101110) = 46,
B00101110 DEPRECATED(0b00101110) = 46,
B101111 DEPRECATED(0b101111) = 47,
B0101111 DEPRECATED(0b0101111) = 47,
B00101111 DEPRECATED(0b00101111) = 47,
B110000 DEPRECATED(0b110000) = 48,
B0110000 DEPRECATED(0b0110000) = 48,
B00110000 DEPRECATED(0b00110000) = 48,
B110001 DEPRECATED(0b110001) = 49,
B0110001 DEPRECATED(0b0110001) = 49,
B00110001 DEPRECATED(0b00110001) = 49,
B110010 DEPRECATED(0b110010) = 50,
B0110010 DEPRECATED(0b0110010) = 50,
B00110010 DEPRECATED(0b00110010) = 50,
B110011 DEPRECATED(0b110011) = 51,
B0110011 DEPRECATED(0b0110011) = 51,
B00110011 DEPRECATED(0b00110011) = 51,
B110100 DEPRECATED(0b110100) = 52,
B0110100 DEPRECATED(0b0110100) = 52,
B00110100 DEPRECATED(0b00110100) = 52,
B110101 DEPRECATED(0b110101) = 53,
B0110101 DEPRECATED(0b0110101) = 53,
B00110101 DEPRECATED(0b00110101) = 53,
B110110 DEPRECATED(0b110110) = 54,
B0110110 DEPRECATED(0b0110110) = 54,
B00110110 DEPRECATED(0b00110110) = 54,
B110111 DEPRECATED(0b110111) = 55,
B0110111 DEPRECATED(0b0110111) = 55,
B00110111 DEPRECATED(0b00110111) = 55,
B111000 DEPRECATED(0b111000) = 56,
B0111000 DEPRECATED(0b0111000) = 56,
B00111000 DEPRECATED(0b00111000) = 56,
B111001 DEPRECATED(0b111001) = 57,
B0111001 DEPRECATED(0b0111001) = 57,
B00111001 DEPRECATED(0b00111001) = 57,
B111010 DEPRECATED(0b111010) = 58,
B0111010 DEPRECATED(0b0111010) = 58,
B00111010 DEPRECATED(0b00111010) = 58,
B111011 DEPRECATED(0b111011) = 59,
B0111011 DEPRECATED(0b0111011) = 59,
B00111011 DEPRECATED(0b00111011) = 59,
B111100 DEPRECATED(0b111100) = 60,
B0111100 DEPRECATED(0b0111100) = 60,
B00111100 DEPRECATED(0b00111100) = 60,
B111101 DEPRECATED(0b111101) = 61,
B0111101 DEPRECATED(0b0111101) = 61,
B00111101 DEPRECATED(0b00111101) = 61,
B111110 DEPRECATED(0b111110) = 62,
B0111110 DEPRECATED(0b0111110) = 62,
B00111110 DEPRECATED(0b00111110) = 62,
B111111 DEPRECATED(0b111111) = 63,
B0111111 DEPRECATED(0b0111111) = 63,
B00111111 DEPRECATED(0b00111111) = 63,
B1000000 DEPRECATED(0b1000000) = 64,
B01000000 DEPRECATED(0b01000000) = 64,
B1000001 DEPRECATED(0b1000001) = 65,
B01000001 DEPRECATED(0b01000001) = 65,
B1000010 DEPRECATED(0b1000010) = 66,
B01000010 DEPRECATED(0b01000010) = 66,
B1000011 DEPRECATED(0b1000011) = 67,
B01000011 DEPRECATED(0b01000011) = 67,
B1000100 DEPRECATED(0b1000100) = 68,
B01000100 DEPRECATED(0b01000100) = 68,
B1000101 DEPRECATED(0b1000101) = 69,
B01000101 DEPRECATED(0b01000101) = 69,
B1000110 DEPRECATED(0b1000110) = 70,
B01000110 DEPRECATED(0b01000110) = 70,
B1000111 DEPRECATED(0b1000111) = 71,
B01000111 DEPRECATED(0b01000111) = 71,
B1001000 DEPRECATED(0b1001000) = 72,
B01001000 DEPRECATED(0b01001000) = 72,
B1001001 DEPRECATED(0b1001001) = 73,
B01001001 DEPRECATED(0b01001001) = 73,
B1001010 DEPRECATED(0b1001010) = 74,
B01001010 DEPRECATED(0b01001010) = 74,
B1001011 DEPRECATED(0b1001011) = 75,
B01001011 DEPRECATED(0b01001011) = 75,
B1001100 DEPRECATED(0b1001100) = 76,
B01001100 DEPRECATED(0b01001100) = 76,
B1001101 DEPRECATED(0b1001101) = 77,
B01001101 DEPRECATED(0b01001101) = 77,
B1001110 DEPRECATED(0b1001110) = 78,
B01001110 DEPRECATED(0b01001110) = 78,
B1001111 DEPRECATED(0b1001111) = 79,
B01001111 DEPRECATED(0b01001111) = 79,
B1010000 DEPRECATED(0b1010000) = 80,
B01010000 DEPRECATED(0b01010000) = 80,
B1010001 DEPRECATED(0b1010001) = 81,
B01010001 DEPRECATED(0b01010001) = 81,
B1010010 DEPRECATED(0b1010010) = 82,
B01010010 DEPRECATED(0b01010010) = 82,
B1010011 DEPRECATED(0b1010011) = 83,
B01010011 DEPRECATED(0b01010011) = 83,
B1010100 DEPRECATED(0b1010100) = 84,
B01010100 DEPRECATED(0b01010100) = 84,
B1010101 DEPRECATED(0b1010101) = 85,
B01010101 DEPRECATED(0b01010101) = 85,
B1010110 DEPRECATED(0b1010110) = 86,
B01010110 DEPRECATED(0b01010110) = 86,
B1010111 DEPRECATED(0b1010111) = 87,
B01010111 DEPRECATED(0b01010111) = 87,
B1011000 DEPRECATED(0b1011000) = 88,
B01011000 DEPRECATED(0b01011000) = 88,
B1011001 DEPRECATED(0b1011001) = 89,
B01011001 DEPRECATED(0b01011001) = 89,
B1011010 DEPRECATED(0b1011010) = 90,
B01011010 DEPRECATED(0b01011010) = 90,
B1011011 DEPRECATED(0b1011011) = 91,
B01011011 DEPRECATED(0b01011011) = 91,
B1011100 DEPRECATED(0b1011100) = 92,
B01011100 DEPRECATED(0b01011100) = 92,
B1011101 DEPRECATED(0b1011101) = 93,
B01011101 DEPRECATED(0b01011101) = 93,
B1011110 DEPRECATED(0b1011110) = 94,
B01011110 DEPRECATED(0b01011110) = 94,
B1011111 DEPRECATED(0b1011111) = 95,
B01011111 DEPRECATED(0b01011111) = 95,
B1100000 DEPRECATED(0b1100000) = 96,
B01100000 DEPRECATED(0b01100000) = 96,
B1100001 DEPRECATED(0b1100001) = 97,
B01100001 DEPRECATED(0b01100001) = 97,
B1100010 DEPRECATED(0b1100010) = 98,
B01100010 DEPRECATED(0b01100010) = 98,
B1100011 DEPRECATED(0b1100011) = 99,
B01100011 DEPRECATED(0b01100011) = 99,
B1100100 DEPRECATED(0b1100100) = 100,
B01100100 DEPRECATED(0b01100100) = 100,
B1100101 DEPRECATED(0b1100101) = 101,
B01100101 DEPRECATED(0b01100101) = 101,
B1100110 DEPRECATED(0b1100110) = 102,
B01100110 DEPRECATED(0b01100110) = 102,
B1100111 DEPRECATED(0b1100111) = 103,
B01100111 DEPRECATED(0b01100111) = 103,
B1101000 DEPRECATED(0b1101000) = 104,
B01101000 DEPRECATED(0b01101000) = 104,
B1101001 DEPRECATED(0b1101001) = 105,
B01101001 DEPRECATED(0b01101001) = 105,
B1101010 DEPRECATED(0b1101010) = 106,
B01101010 DEPRECATED(0b01101010) = 106,
B1101011 DEPRECATED(0b1101011) = 107,
B01101011 DEPRECATED(0b01101011) = 107,
B1101100 DEPRECATED(0b1101100) = 108,
B01101100 DEPRECATED(0b01101100) = 108,
B1101101 DEPRECATED(0b1101101) = 109,
B01101101 DEPRECATED(0b01101101) = 109,
B1101110 DEPRECATED(0b1101110) = 110,
B01101110 DEPRECATED(0b01101110) = 110,
B1101111 DEPRECATED(0b1101111) = 111,
B01101111 DEPRECATED(0b01101111) = 111,
B1110000 DEPRECATED(0b1110000) = 112,
B01110000 DEPRECATED(0b01110000) = 112,
B1110001 DEPRECATED(0b1110001) = 113,
B01110001 DEPRECATED(0b01110001) = 113,
B1110010 DEPRECATED(0b1110010) = 114,
B01110010 DEPRECATED(0b01110010) = 114,
B1110011 DEPRECATED(0b1110011) = 115,
B01110011 DEPRECATED(0b01110011) = 115,
B1110100 DEPRECATED(0b1110100) = 116,
B01110100 DEPRECATED(0b01110100) = 116,
B1110101 DEPRECATED(0b1110101) = 117,
B01110101 DEPRECATED(0b01110101) = 117,
B1110110 DEPRECATED(0b1110110) = 118,
B01110110 DEPRECATED(0b01110110) = 118,
B1110111 DEPRECATED(0b1110111) = 119,
B01110111 DEPRECATED(0b01110111) = 119,
B1111000 DEPRECATED(0b1111000) = 120,
B01111000 DEPRECATED(0b01111000) = 120,
B1111001 DEPRECATED(0b1111001) = 121,
B01111001 DEPRECATED(0b01111001) = 121,
B1111010 DEPRECATED(0b1111010) = 122,
B01111010 DEPRECATED(0b01111010) = 122,
B1111011 DEPRECATED(0b1111011) = 123,
B01111011 DEPRECATED(0b01111011) = 123,
B1111100 DEPRECATED(0b1111100) = 124,
B01111100 DEPRECATED(0b01111100) = 124,
B1111101 DEPRECATED(0b1111101) = 125,
B01111101 DEPRECATED(0b01111101) = 125,
B1111110 DEPRECATED(0b1111110) = 126,
B01111110 DEPRECATED(0b01111110) = 126,
B1111111 DEPRECATED(0b1111111) = 127,
B01111111 DEPRECATED(0b01111111) = 127,
B10000000 DEPRECATED(0b10000000) = 128,
B10000001 DEPRECATED(0b10000001) = 129,
B10000010 DEPRECATED(0b10000010) = 130,
B10000011 DEPRECATED(0b10000011) = 131,
B10000100 DEPRECATED(0b10000100) = 132,
B10000101 DEPRECATED(0b10000101) = 133,
B10000110 DEPRECATED(0b10000110) = 134,
B10000111 DEPRECATED(0b10000111) = 135,
B10001000 DEPRECATED(0b10001000) = 136,
B10001001 DEPRECATED(0b10001001) = 137,
B10001010 DEPRECATED(0b10001010) = 138,
B10001011 DEPRECATED(0b10001011) = 139,
B10001100 DEPRECATED(0b10001100) = 140,
B10001101 DEPRECATED(0b10001101) = 141,
B10001110 DEPRECATED(0b10001110) = 142,
B10001111 DEPRECATED(0b10001111) = 143,
B10010000 DEPRECATED(0b10010000) = 144,
B10010001 DEPRECATED(0b10010001) = 145,
B10010010 DEPRECATED(0b10010010) = 146,
B10010011 DEPRECATED(0b10010011) = 147,
B10010100 DEPRECATED(0b10010100) = 148,
B10010101 DEPRECATED(0b10010101) = 149,
B10010110 DEPRECATED(0b10010110) = 150,
B10010111 DEPRECATED(0b10010111) = 151,
B10011000 DEPRECATED(0b10011000) = 152,
B10011001 DEPRECATED(0b10011001) = 153,
B10011010 DEPRECATED(0b10011010) = 154,
B10011011 DEPRECATED(0b10011011) = 155,
B10011100 DEPRECATED(0b10011100) = 156,
B10011101 DEPRECATED(0b10011101) = 157,
B10011110 DEPRECATED(0b10011110) = 158,
B10011111 DEPRECATED(0b10011111) = 159,
B10100000 DEPRECATED(0b10100000) = 160,
B10100001 DEPRECATED(0b10100001) = 161,
B10100010 DEPRECATED(0b10100010) = 162,
B10100011 DEPRECATED(0b10100011) = 163,
B10100100 DEPRECATED(0b10100100) = 164,
B10100101 DEPRECATED(0b10100101) = 165,
B10100110 DEPRECATED(0b10100110) = 166,
B10100111 DEPRECATED(0b10100111) = 167,
B10101000 DEPRECATED(0b10101000) = 168,
B10101001 DEPRECATED(0b10101001) = 169,
B10101010 DEPRECATED(0b10101010) = 170,
B10101011 DEPRECATED(0b10101011) = 171,
B10101100 DEPRECATED(0b10101100) = 172,
B10101101 DEPRECATED(0b10101101) = 173,
B10101110 DEPRECATED(0b10101110) = 174,
B10101111 DEPRECATED(0b10101111) = 175,
B10110000 DEPRECATED(0b10110000) = 176,
B10110001 DEPRECATED(0b10110001) = 177,
B10110010 DEPRECATED(0b10110010) = 178,
B10110011 DEPRECATED(0b10110011) = 179,
B10110100 DEPRECATED(0b10110100) = 180,
B10110101 DEPRECATED(0b10110101) = 181,
B10110110 DEPRECATED(0b10110110) = 182,
B10110111 DEPRECATED(0b10110111) = 183,
B10111000 DEPRECATED(0b10111000) = 184,
B10111001 DEPRECATED(0b10111001) = 185,
B10111010 DEPRECATED(0b10111010) = 186,
B10111011 DEPRECATED(0b10111011) = 187,
B10111100 DEPRECATED(0b10111100) = 188,
B10111101 DEPRECATED(0b10111101) = 189,
B10111110 DEPRECATED(0b10111110) = 190,
B10111111 DEPRECATED(0b10111111) = 191,
B11000000 DEPRECATED(0b11000000) = 192,
B11000001 DEPRECATED(0b11000001) = 193,
B11000010 DEPRECATED(0b11000010) = 194,
B11000011 DEPRECATED(0b11000011) = 195,
B11000100 DEPRECATED(0b11000100) = 196,
B11000101 DEPRECATED(0b11000101) = 197,
B11000110 DEPRECATED(0b11000110) = 198,
B11000111 DEPRECATED(0b11000111) = 199,
B11001000 DEPRECATED(0b11001000) = 200,
B11001001 DEPRECATED(0b11001001) = 201,
B11001010 DEPRECATED(0b11001010) = 202,
B11001011 DEPRECATED(0b11001011) = 203,
B11001100 DEPRECATED(0b11001100) = 204,
B11001101 DEPRECATED(0b11001101) = 205,
B11001110 DEPRECATED(0b11001110) = 206,
B11001111 DEPRECATED(0b11001111) = 207,
B11010000 DEPRECATED(0b11010000) = 208,
B11010001 DEPRECATED(0b11010001) = 209,
B11010010 DEPRECATED(0b11010010) = 210,
B11010011 DEPRECATED(0b11010011) = 211,
B11010100 DEPRECATED(0b11010100) = 212,
B11010101 DEPRECATED(0b11010101) = 213,
B11010110 DEPRECATED(0b11010110) = 214,
B11010111 DEPRECATED(0b11010111) = 215,
B11011000 DEPRECATED(0b11011000) = 216,
B11011001 DEPRECATED(0b11011001) = 217,
B11011010 DEPRECATED(0b11011010) = 218,
B11011011 DEPRECATED(0b11011011) = 219,
B11011100 DEPRECATED(0b11011100) = 220,
B11011101 DEPRECATED(0b11011101) = 221,
B11011110 DEPRECATED(0b11011110) = 222,
B11011111 DEPRECATED(0b11011111) = 223,
B11100000 DEPRECATED(0b11100000) = 224,
B11100001 DEPRECATED(0b11100001) = 225,
B11100010 DEPRECATED(0b11100010) = 226,
B11100011 DEPRECATED(0b11100011) = 227,
B11100100 DEPRECATED(0b11100100) = 228,
B11100101 DEPRECATED(0b11100101) = 229,
B11100110 DEPRECATED(0b11100110) = 230,
B11100111 DEPRECATED(0b11100111) = 231,
B11101000 DEPRECATED(0b11101000) = 232,
B11101001 DEPRECATED(0b11101001) = 233,
B11101010 DEPRECATED(0b11101010) = 234,
B11101011 DEPRECATED(0b11101011) = 235,
B11101100 DEPRECATED(0b11101100) = 236,
B11101101 DEPRECATED(0b11101101) = 237,
B11101110 DEPRECATED(0b11101110) = 238,
B11101111 DEPRECATED(0b11101111) = 239,
B11110000 DEPRECATED(0b11110000) = 240,
B11110001 DEPRECATED(0b11110001) = 241,
B11110010 DEPRECATED(0b11110010) = 242,
B11110011 DEPRECATED(0b11110011) = 243,
B11110100 DEPRECATED(0b11110100) = 244,
B11110101 DEPRECATED(0b11110101) = 245,
B11110110 DEPRECATED(0b11110110) = 246,
B11110111 DEPRECATED(0b11110111) = 247,
B11111000 DEPRECATED(0b11111000) = 248,
B11111001 DEPRECATED(0b11111001) = 249,
B11111010 DEPRECATED(0b11111010) = 250,
B11111011 DEPRECATED(0b11111011) = 251,
B11111100 DEPRECATED(0b11111100) = 252,
B11111101 DEPRECATED(0b11111101) = 253,
B11111110 DEPRECATED(0b11111110) = 254,
B11111111 DEPRECATED(0b11111111) = 255
};
#undef DEPRECATED
#endif
+289
View File
@@ -0,0 +1,289 @@
/*
cbuf.cpp - Circular buffer implementation
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
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 "cbuf.h"
#include "esp32-hal-log.h"
#if CONFIG_DISABLE_HAL_LOCKS
#define CBUF_MUTEX_CREATE()
#define CBUF_MUTEX_LOCK()
#define CBUF_MUTEX_UNLOCK()
#define CBUF_MUTEX_DELETE()
#else
#define CBUF_MUTEX_CREATE() \
if (_lock == NULL) { \
_lock = xSemaphoreCreateMutex(); \
if (_lock == NULL) { \
log_e("failed to create mutex"); \
} \
}
#define CBUF_MUTEX_LOCK() \
if (_lock != NULL) { \
xSemaphoreTakeRecursive(_lock, portMAX_DELAY); \
}
#define CBUF_MUTEX_UNLOCK() \
if (_lock != NULL) { \
xSemaphoreGiveRecursive(_lock); \
}
#define CBUF_MUTEX_DELETE() \
if (_lock != NULL) { \
SemaphoreHandle_t l = _lock; \
_lock = NULL; \
vSemaphoreDelete(l); \
}
#endif
cbuf::cbuf(size_t size) : next(NULL), has_peek(false), peek_byte(0), _buf(xRingbufferCreate(size, RINGBUF_TYPE_BYTEBUF)) {
if (_buf == NULL) {
log_e("failed to allocate ring buffer");
}
CBUF_MUTEX_CREATE();
}
cbuf::~cbuf() {
CBUF_MUTEX_LOCK();
if (_buf != NULL) {
RingbufHandle_t b = _buf;
_buf = NULL;
vRingbufferDelete(b);
}
CBUF_MUTEX_UNLOCK();
CBUF_MUTEX_DELETE();
}
size_t cbuf::resizeAdd(size_t addSize) {
return resize(size() + addSize);
}
size_t cbuf::resize(size_t newSize) {
CBUF_MUTEX_LOCK();
size_t _size = size();
if (newSize == _size) {
return _size;
}
// not lose any data
// if data can be lost use remove or flush before resize
size_t bytes_available = available();
if (newSize < bytes_available) {
CBUF_MUTEX_UNLOCK();
log_e("new size is less than the currently available data size");
return _size;
}
RingbufHandle_t newbuf = xRingbufferCreate(newSize, RINGBUF_TYPE_BYTEBUF);
if (newbuf == NULL) {
CBUF_MUTEX_UNLOCK();
log_e("failed to allocate new ring buffer");
return _size;
}
if (_buf != NULL) {
if (bytes_available) {
char *old_data = (char *)malloc(bytes_available);
if (old_data == NULL) {
vRingbufferDelete(newbuf);
CBUF_MUTEX_UNLOCK();
log_e("failed to allocate temporary buffer");
return _size;
}
bytes_available = read(old_data, bytes_available);
if (!bytes_available) {
free(old_data);
vRingbufferDelete(newbuf);
CBUF_MUTEX_UNLOCK();
log_e("failed to read previous data");
return _size;
}
if (xRingbufferSend(newbuf, (void *)old_data, bytes_available, 0) != pdTRUE) {
write(old_data, bytes_available);
free(old_data);
vRingbufferDelete(newbuf);
CBUF_MUTEX_UNLOCK();
log_e("failed to restore previous data");
return _size;
}
free(old_data);
}
RingbufHandle_t b = _buf;
_buf = newbuf;
vRingbufferDelete(b);
} else {
_buf = newbuf;
}
CBUF_MUTEX_UNLOCK();
return newSize;
}
size_t cbuf::available() const {
size_t available = 0;
if (_buf != NULL) {
vRingbufferGetInfo(_buf, NULL, NULL, NULL, NULL, (UBaseType_t *)&available);
}
if (has_peek) {
available++;
}
return available;
}
size_t cbuf::size() {
size_t _size = 0;
if (_buf != NULL) {
_size = xRingbufferGetMaxItemSize(_buf);
}
return _size;
}
size_t cbuf::room() const {
size_t _room = 0;
if (_buf != NULL) {
_room = xRingbufferGetCurFreeSize(_buf);
}
return _room;
}
bool cbuf::empty() const {
return available() == 0;
}
bool cbuf::full() const {
return room() == 0;
}
int cbuf::peek() {
if (!available()) {
return -1;
}
int c;
CBUF_MUTEX_LOCK();
if (has_peek) {
c = peek_byte;
} else {
c = read();
if (c >= 0) {
has_peek = true;
peek_byte = c;
}
}
CBUF_MUTEX_UNLOCK();
return c;
}
int cbuf::read() {
char result = 0;
if (!read(&result, 1)) {
return -1;
}
return static_cast<int>(result);
}
size_t cbuf::read(char *dst, size_t size) {
CBUF_MUTEX_LOCK();
size_t bytes_available = available();
if (!bytes_available || !size) {
CBUF_MUTEX_UNLOCK();
return 0;
}
if (has_peek) {
if (dst != NULL) {
*dst++ = peek_byte;
}
size--;
}
size_t size_read = 0;
if (size) {
size_t received_size = 0;
size_t size_to_read = (size < bytes_available) ? size : bytes_available;
uint8_t *received_buff = (uint8_t *)xRingbufferReceiveUpTo(_buf, &received_size, 0, size_to_read);
if (received_buff != NULL) {
if (dst != NULL) {
memcpy(dst, received_buff, received_size);
}
vRingbufferReturnItem(_buf, received_buff);
size_read = received_size;
size_to_read -= received_size;
// wrap around data
if (size_to_read) {
received_size = 0;
received_buff = (uint8_t *)xRingbufferReceiveUpTo(_buf, &received_size, 0, size_to_read);
if (received_buff != NULL) {
if (dst != NULL) {
memcpy(dst + size_read, received_buff, received_size);
}
vRingbufferReturnItem(_buf, received_buff);
size_read += received_size;
} else {
log_e("failed to read wrap around data from ring buffer");
}
}
} else {
log_e("failed to read from ring buffer");
}
}
if (has_peek) {
has_peek = false;
size_read++;
}
CBUF_MUTEX_UNLOCK();
return size_read;
}
size_t cbuf::write(char c) {
return write(&c, 1);
}
size_t cbuf::write(const char *src, size_t size) {
CBUF_MUTEX_LOCK();
size_t bytes_available = room();
if (!bytes_available || !size) {
CBUF_MUTEX_UNLOCK();
return 0;
}
size_t size_to_write = (size < bytes_available) ? size : bytes_available;
if (xRingbufferSend(_buf, (void *)src, size_to_write, 0) != pdTRUE) {
CBUF_MUTEX_UNLOCK();
log_e("failed to write to ring buffer");
return 0;
}
CBUF_MUTEX_UNLOCK();
return size_to_write;
}
void cbuf::flush() {
read(NULL, available());
}
size_t cbuf::remove(size_t size) {
CBUF_MUTEX_LOCK();
size_t bytes_available = available();
if (bytes_available && size) {
size_t size_to_remove = (size < bytes_available) ? size : bytes_available;
bytes_available -= read(NULL, size_to_remove);
}
CBUF_MUTEX_UNLOCK();
return bytes_available;
}
+65
View File
@@ -0,0 +1,65 @@
/*
cbuf.h - Circular buffer implementation
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
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 <stddef.h>
#include <stdint.h>
#include <string.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/ringbuf.h"
#include "freertos/semphr.h"
class cbuf {
public:
cbuf(size_t size);
~cbuf();
size_t resizeAdd(size_t addSize);
size_t resize(size_t newSize);
size_t available() const;
size_t size();
size_t room() const;
bool empty() const;
bool full() const;
int peek();
int read();
size_t read(char *dst, size_t size);
size_t write(char c);
size_t write(const char *src, size_t size);
void flush();
size_t remove(size_t size);
cbuf *next;
bool has_peek;
uint8_t peek_byte;
protected:
RingbufHandle_t _buf = NULL;
#if !CONFIG_DISABLE_HAL_LOCKS
SemaphoreHandle_t _lock = NULL;
#endif
};
+338
View File
@@ -0,0 +1,338 @@
#include "esp_heap_caps.h"
#include "esp_chip_info.h"
#include "esp_idf_version.h"
#include "esp_arduino_version.h"
#include "esp_rom_spiflash.h"
#include "esp_flash.h"
#include "esp_partition.h"
#include "esp_app_format.h"
#include "soc/efuse_reg.h"
#include "soc/rtc.h"
#include "soc/spi_reg.h"
#include "soc/soc.h"
#if CONFIG_IDF_TARGET_ESP32S2
#include "esp32s2/rom/spi_flash.h"
#endif
#include "esp_bit_defs.h"
#include "Arduino.h"
#include "esp32-hal-periman.h"
#include "chip-debug-report.h"
#define chip_report_printf log_printf
#define printMemCapsInfo(caps) _printMemCapsInfo(MALLOC_CAP_##caps, #caps)
#define b2kb(b) ((float)b / 1024.0)
#define b2mb(b) ((float)b / (1024.0 * 1024.0))
static void _printMemCapsInfo(uint32_t caps, const char *caps_str) {
multi_heap_info_t info;
size_t total = heap_caps_get_total_size(caps);
heap_caps_get_info(&info, caps);
chip_report_printf("%s Memory Info:\n", caps_str);
chip_report_printf("------------------------------------------\n");
chip_report_printf(" Total Size : %8u B (%6.1f KB)\n", total, b2kb(total));
chip_report_printf(" Free Bytes : %8u B (%6.1f KB)\n", info.total_free_bytes, b2kb(info.total_free_bytes));
chip_report_printf(" Allocated Bytes : %8u B (%6.1f KB)\n", info.total_allocated_bytes, b2kb(info.total_allocated_bytes));
chip_report_printf(" Minimum Free Bytes: %8u B (%6.1f KB)\n", info.minimum_free_bytes, b2kb(info.minimum_free_bytes));
chip_report_printf(" Largest Free Block: %8u B (%6.1f KB)\n", info.largest_free_block, b2kb(info.largest_free_block));
}
static void printPkgVersion(void) {
chip_report_printf(" Package : ");
#if CONFIG_IDF_TARGET_ESP32
uint32_t pkg_ver = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_PACKAGE);
switch (pkg_ver) {
case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDR2V3: chip_report_printf("D0WD-R2-V3"); break;
case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ6: chip_report_printf("D0WD-Q6"); break;
case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ5: chip_report_printf("D0WD-Q5"); break;
case EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5: chip_report_printf("D2WD-Q5"); break;
case EFUSE_RD_CHIP_VER_PKG_ESP32U4WDH: chip_report_printf("U4WD-H"); break;
case EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4: chip_report_printf("PICO-D4"); break;
case EFUSE_RD_CHIP_VER_PKG_ESP32PICOV302: chip_report_printf("PICO-V3-02"); break;
}
#elif CONFIG_IDF_TARGET_ESP32S2
uint32_t pkg_ver = REG_GET_FIELD(EFUSE_RD_MAC_SPI_SYS_3_REG, EFUSE_PKG_VERSION);
switch (pkg_ver) {
case 1: chip_report_printf("FH16"); break;
case 2: chip_report_printf("FH32"); break;
default: chip_report_printf("%lu", pkg_ver); break;
}
#elif CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6
uint32_t pkg_ver = REG_GET_FIELD(EFUSE_RD_MAC_SPI_SYS_3_REG, EFUSE_PKG_VERSION);
chip_report_printf("%lu", pkg_ver);
#elif CONFIG_IDF_TARGET_ESP32C2
uint32_t pkg_ver = REG_GET_FIELD(EFUSE_RD_BLK2_DATA1_REG, EFUSE_PKG_VERSION);
chip_report_printf("%lu", pkg_ver);
#elif CONFIG_IDF_TARGET_ESP32H2
uint32_t pkg_ver = REG_GET_FIELD(EFUSE_RD_MAC_SYS_4_REG, EFUSE_PKG_VERSION);
chip_report_printf("%lu", pkg_ver);
#elif CONFIG_IDF_TARGET_ESP32P4
uint32_t pkg_ver = REG_GET_FIELD(EFUSE_RD_MAC_SYS_2_REG, EFUSE_PKG_VERSION);
chip_report_printf("%lu", pkg_ver);
#elif CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61
uint32_t pkg_ver = REG_GET_FIELD(EFUSE_RD_MAC_SYS2_REG, EFUSE_PKG_VERSION);
chip_report_printf("%lu", pkg_ver);
#else
chip_report_printf("Unknown");
#endif
chip_report_printf("\n");
}
static void printChipInfo(void) {
esp_chip_info_t info;
esp_chip_info(&info);
chip_report_printf("Chip Info:\n");
chip_report_printf("------------------------------------------\n");
chip_report_printf(" Model : ");
switch (info.model) {
case CHIP_ESP32: chip_report_printf("ESP32\n"); break;
case CHIP_ESP32S2: chip_report_printf("ESP32-S2\n"); break;
case CHIP_ESP32S3: chip_report_printf("ESP32-S3\n"); break;
case CHIP_ESP32C2: chip_report_printf("ESP32-C2\n"); break;
case CHIP_ESP32C3: chip_report_printf("ESP32-C3\n"); break;
case CHIP_ESP32C6: chip_report_printf("ESP32-C6\n"); break;
case CHIP_ESP32H2: chip_report_printf("ESP32-H2\n"); break;
case CHIP_ESP32P4: chip_report_printf("ESP32-P4\n"); break;
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
case CHIP_ESP32C5: chip_report_printf("ESP32-C5\n"); break;
case CHIP_ESP32C61: chip_report_printf("ESP32-C61\n"); break;
case CHIP_ESP32H21: chip_report_printf("ESP32-H21\n"); break;
#endif
default: chip_report_printf("Unknown %d\n", info.model); break;
}
printPkgVersion();
chip_report_printf(" Revision : %.2f\n", (float)(info.revision) / 100.0);
chip_report_printf(" Cores : %d\n", info.cores);
rtc_cpu_freq_config_t conf;
rtc_clk_cpu_freq_get_config(&conf);
chip_report_printf(" CPU Frequency : %lu MHz\n", conf.freq_mhz);
chip_report_printf(" XTAL Frequency : %d MHz\n", rtc_clk_xtal_freq_get());
chip_report_printf(" Features Bitfield : %#010x\n", info.features);
chip_report_printf(" Embedded Flash : %s\n", (info.features & CHIP_FEATURE_EMB_FLASH) ? "Yes" : "No");
chip_report_printf(" Embedded PSRAM : %s\n", (info.features & CHIP_FEATURE_EMB_PSRAM) ? "Yes" : "No");
chip_report_printf(" 2.4GHz WiFi : %s\n", (info.features & CHIP_FEATURE_WIFI_BGN) ? "Yes" : "No");
chip_report_printf(" Classic BT : %s\n", (info.features & CHIP_FEATURE_BT) ? "Yes" : "No");
chip_report_printf(" BT Low Energy : %s\n", (info.features & CHIP_FEATURE_BLE) ? "Yes" : "No");
chip_report_printf(" IEEE 802.15.4 : %s\n", (info.features & CHIP_FEATURE_IEEE802154) ? "Yes" : "No");
}
static void printFlashInfo(void) {
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
#define ESP_FLASH_IMAGE_BASE 0x1000
#elif CONFIG_IDF_TARGET_ESP32P4
#define ESP_FLASH_IMAGE_BASE 0x2000
#else
#define ESP_FLASH_IMAGE_BASE 0x0000
#endif
// REG_SPI_BASE is not defined for S3/C3 ??
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3
#ifdef REG_SPI_BASE
#undef REG_SPI_BASE
#endif // REG_SPI_BASE
#define REG_SPI_BASE(i) (DR_REG_SPI1_BASE + (((i) > 1) ? (((i) * 0x1000) + 0x20000) : (((~(i)) & 1) * 0x1000)))
#endif // TARGET
chip_report_printf("Flash Info:\n");
chip_report_printf("------------------------------------------\n");
uint32_t hw_size = 1 << (g_rom_flashchip.device_id & 0xFF);
chip_report_printf(" Chip Size : %8lu B (%.0f MB)\n", hw_size, b2mb(hw_size));
chip_report_printf(" Block Size : %8lu B (%6.1f KB)\n", g_rom_flashchip.block_size, b2kb(g_rom_flashchip.block_size));
chip_report_printf(" Sector Size : %8lu B (%6.1f KB)\n", g_rom_flashchip.sector_size, b2kb(g_rom_flashchip.sector_size));
chip_report_printf(" Page Size : %8lu B (%6.1f KB)\n", g_rom_flashchip.page_size, b2kb(g_rom_flashchip.page_size));
// Runtime flash frequency detection from hardware registers
uint32_t actual_freq = ESP.getFlashFrequencyMHz();
uint8_t source_freq = ESP.getFlashSourceFrequencyMHz();
uint8_t divider = ESP.getFlashClockDivider();
chip_report_printf(" Bus Speed : %lu MHz\n", actual_freq);
chip_report_printf(" Flash Frequency : %lu MHz (source: %u MHz, divider: %u)\n", actual_freq, source_freq, divider);
chip_report_printf(" Bus Mode : ");
#if CONFIG_ESPTOOLPY_OCT_FLASH
chip_report_printf("OPI\n");
#elif CONFIG_ESPTOOLPY_FLASHMODE_QIO
chip_report_printf("QIO\n");
#elif CONFIG_ESPTOOLPY_FLASHMODE_QOUT
chip_report_printf("QOUT\n");
#elif CONFIG_ESPTOOLPY_FLASHMODE_DIO
chip_report_printf("DIO\n");
#elif CONFIG_ESPTOOLPY_FLASHMODE_DOUT
chip_report_printf("DOUT\n");
#endif
}
static void printPartitionsInfo(void) {
chip_report_printf("Partitions Info:\n");
chip_report_printf("------------------------------------------\n");
esp_partition_iterator_t iterator = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL);
if (iterator != NULL) {
esp_partition_iterator_t it = iterator;
while (it != NULL) {
const esp_partition_t *partition = esp_partition_get(it);
if (partition) {
chip_report_printf(" %17s : addr: 0x%08X, size: %7.1f KB", partition->label, partition->address, b2kb(partition->size));
if (partition->type == ESP_PARTITION_TYPE_APP) {
chip_report_printf(", type: APP");
if (partition->subtype == 0) {
chip_report_printf(", subtype: FACTORY");
} else if (partition->subtype >= 0x10 && partition->subtype < 0x20) {
chip_report_printf(", subtype: OTA_%lu", partition->subtype - 0x10);
} else if (partition->subtype == 0x20) {
chip_report_printf(", subtype: TEST");
} else {
chip_report_printf(", subtype: 0x%02X", partition->subtype);
}
} else {
chip_report_printf(", type: DATA");
chip_report_printf(", subtype: ");
switch (partition->subtype) {
case ESP_PARTITION_SUBTYPE_DATA_OTA: chip_report_printf("OTA"); break;
case ESP_PARTITION_SUBTYPE_DATA_PHY: chip_report_printf("PHY"); break;
case ESP_PARTITION_SUBTYPE_DATA_NVS: chip_report_printf("NVS"); break;
case ESP_PARTITION_SUBTYPE_DATA_COREDUMP: chip_report_printf("COREDUMP"); break;
case ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS: chip_report_printf("NVS_KEYS"); break;
case ESP_PARTITION_SUBTYPE_DATA_EFUSE_EM: chip_report_printf("EFUSE_EM"); break;
case ESP_PARTITION_SUBTYPE_DATA_UNDEFINED: chip_report_printf("UNDEFINED"); break;
case ESP_PARTITION_SUBTYPE_DATA_ESPHTTPD: chip_report_printf("ESPHTTPD"); break;
case ESP_PARTITION_SUBTYPE_DATA_FAT: chip_report_printf("FAT"); break;
case ESP_PARTITION_SUBTYPE_DATA_SPIFFS: chip_report_printf("SPIFFS"); break;
case ESP_PARTITION_SUBTYPE_DATA_LITTLEFS: chip_report_printf("LITTLEFS"); break;
default: chip_report_printf("0x%02X", partition->subtype); break;
}
}
chip_report_printf("\n");
}
it = esp_partition_next(it);
}
//esp_partition_iterator_release(iterator);
}
}
static void printSoftwareInfo(void) {
chip_report_printf("Software Info:\n");
chip_report_printf("------------------------------------------\n");
chip_report_printf(" Compile Date/Time : %s %s\n", __DATE__, __TIME__);
#ifdef ARDUINO_HOST_OS
chip_report_printf(" Compile Host OS : %s\n", ARDUINO_HOST_OS);
#endif
chip_report_printf(" ESP-IDF Version : %s\n", esp_get_idf_version());
chip_report_printf(" Arduino Version : %s\n", ESP_ARDUINO_VERSION_STR);
}
static void printBoardInfo(void) {
chip_report_printf("Board Info:\n");
chip_report_printf("------------------------------------------\n");
chip_report_printf(" Arduino Board : %s\n", ARDUINO_BOARD);
chip_report_printf(" Arduino Variant : %s\n", ARDUINO_VARIANT);
#ifdef ARDUINO_FQBN
chip_report_printf(" Arduino FQBN : %s\n", ARDUINO_FQBN);
#else
#ifdef CORE_DEBUG_LEVEL
chip_report_printf(" Core Debug Level : %d\n", CORE_DEBUG_LEVEL);
#endif
#ifdef ARDUINO_RUNNING_CORE
chip_report_printf(" Arduino Runs Core : %d\n", ARDUINO_RUNNING_CORE);
chip_report_printf(" Arduino Events on : %d\n", ARDUINO_EVENT_RUNNING_CORE);
#endif
#ifdef ARDUINO_USB_MODE
chip_report_printf(" Arduino USB Mode : %d\n", ARDUINO_USB_MODE);
#endif
#ifdef ARDUINO_USB_CDC_ON_BOOT
chip_report_printf(" CDC On Boot : %d\n", ARDUINO_USB_CDC_ON_BOOT);
#endif
#endif /* ARDUINO_FQBN */
}
static void printPerimanInfo(void) {
chip_report_printf("GPIO Info:\n");
chip_report_printf("------------------------------------------\n");
#if defined(BOARD_HAS_PIN_REMAP)
chip_report_printf(" DPIN|GPIO : BUS_TYPE[bus/unit][chan]\n");
#else
chip_report_printf(" GPIO : BUS_TYPE[bus/unit][chan]\n");
#endif
chip_report_printf(" -------------------------------------- \n");
for (uint8_t i = 0; i < SOC_GPIO_PIN_COUNT; i++) {
if (!perimanPinIsValid(i)) {
continue; //invalid pin
}
peripheral_bus_type_t type = perimanGetPinBusType(i);
if (type == ESP32_BUS_TYPE_INIT) {
continue; //unused pin
}
#if defined(BOARD_HAS_PIN_REMAP)
int dpin = gpioNumberToDigitalPin(i);
if (dpin < 0) {
continue; //pin is not exported
} else {
chip_report_printf(" D%-3d|%4u : ", dpin, i);
}
#else
chip_report_printf(" %4u : ", i);
#endif
const char *extra_type = perimanGetPinBusExtraType(i);
if (extra_type) {
chip_report_printf("%s", extra_type);
} else {
chip_report_printf("%s", perimanGetTypeName(type));
}
int8_t bus_number = perimanGetPinBusNum(i);
if (bus_number != -1) {
chip_report_printf("[%u]", bus_number);
}
int8_t bus_channel = perimanGetPinBusChannel(i);
if (bus_channel != -1) {
chip_report_printf("[%u]", bus_channel);
}
chip_report_printf("\n");
}
}
void printBeforeSetupInfo(void) {
#if ARDUINO_USB_CDC_ON_BOOT
Serial.begin();
Serial.setDebugOutput(true);
uint8_t t = 0;
while (!Serial && (t++ < 200)) {
delay(10); //wait up to 2 seconds for the IDE to connect
}
#endif
chip_report_printf("=========== Before Setup Start ===========\n");
printChipInfo();
chip_report_printf("------------------------------------------\n");
printMemCapsInfo(INTERNAL);
chip_report_printf("------------------------------------------\n");
if (psramFound()) {
printMemCapsInfo(SPIRAM);
chip_report_printf(" Bus Mode : ");
#if CONFIG_SPIRAM_MODE_OCT
chip_report_printf("OPI\n");
#else
chip_report_printf("QSPI\n");
#endif
chip_report_printf("------------------------------------------\n");
}
printFlashInfo();
chip_report_printf("------------------------------------------\n");
printPartitionsInfo();
chip_report_printf("------------------------------------------\n");
printSoftwareInfo();
chip_report_printf("------------------------------------------\n");
printBoardInfo();
chip_report_printf("============ Before Setup End ============\n");
delay(100); //allow the print to finish
}
void printAfterSetupInfo(void) {
chip_report_printf("=========== After Setup Start ============\n");
printMemCapsInfo(INTERNAL);
chip_report_printf("------------------------------------------\n");
if (psramFound()) {
printMemCapsInfo(SPIRAM);
chip_report_printf("------------------------------------------\n");
}
printPerimanInfo();
chip_report_printf("============ After Setup End =============\n");
delay(20); //allow the print to finish
}
+9
View File
@@ -0,0 +1,9 @@
/*
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
void printBeforeSetupInfo(void);
void printAfterSetupInfo(void);
+4
View File
@@ -0,0 +1,4 @@
#define ARDUINO_ESP32_GIT_VER 0xc3e5d72f
#define ARDUINO_ESP32_GIT_DESC 3.3.7-1-gc3e5d72fb
#define ARDUINO_ESP32_RELEASE_3_3_7
#define ARDUINO_ESP32_RELEASE "3_3_7"
+772
View File
@@ -0,0 +1,772 @@
// Copyright 2015-2023 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "esp32-hal-adc.h"
#if SOC_ADC_SUPPORTED
#include "esp32-hal.h"
#include "esp32-hal-periman.h"
#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_continuous.h"
#include "esp_adc/adc_cali_scheme.h"
#include "esp_heap_caps.h"
#if CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP32P4_REV_MIN_FULL >= 300
// NOTE: These weak definitions allow successful linkage if the real efuse calibration functions are missing.
// This is a workaround for the ESP32P4 rev 3.0+, which is missing efuse calibration functions in the IDF.
__attribute__((weak)) uint32_t esp_efuse_rtc_calib_get_ver(void) {
return 0;
}
__attribute__((weak)) uint32_t esp_efuse_rtc_calib_get_init_code(uint32_t atten, uint32_t *code) {
if (code) {
*code = 0;
}
return 0; // 0 means success in ESP-IDF conventions
}
__attribute__((weak)) uint32_t esp_efuse_rtc_calib_get_chan_compens(uint32_t atten, uint32_t *comp) {
if (comp) {
*comp = 0;
}
return 0;
}
__attribute__((weak)) uint32_t esp_efuse_rtc_calib_get_cal_voltage(uint32_t atten, uint32_t *voltage) {
if (voltage) {
*voltage = 0;
}
return 0;
}
#endif
// ESP32-C2 does not define those two for some reason
#ifndef SOC_ADC_DIGI_RESULT_BYTES
#define SOC_ADC_DIGI_RESULT_BYTES (4)
#endif
#ifndef SOC_ADC_DIGI_DATA_BYTES_PER_CONV
#define SOC_ADC_DIGI_DATA_BYTES_PER_CONV (4)
#endif
static uint8_t __analogAttenuation = ADC_11db;
static uint8_t __analogWidth = SOC_ADC_RTC_MAX_BITWIDTH;
static uint8_t __analogReturnedWidth = SOC_ADC_RTC_MAX_BITWIDTH;
typedef struct {
voidFuncPtr fn;
void *arg;
} interrupt_config_t;
typedef struct {
adc_oneshot_unit_handle_t adc_oneshot_handle;
adc_continuous_handle_t adc_continuous_handle;
interrupt_config_t adc_interrupt_handle;
adc_cali_handle_t adc_cali_handle;
uint32_t buffer_size;
uint32_t conversion_frame_size;
} adc_handle_t;
adc_handle_t adc_handle[SOC_ADC_PERIPH_NUM];
static bool adcDetachBus(void *pin) {
adc_channel_t adc_channel;
adc_unit_t adc_unit;
uint8_t used_channels = 0;
adc_oneshot_io_to_channel((int)(pin - 1), &adc_unit, &adc_channel);
for (uint8_t channel = 0; channel < SOC_ADC_CHANNEL_NUM(adc_unit); channel++) {
int io_pin;
adc_oneshot_channel_to_io(adc_unit, channel, &io_pin);
if (perimanGetPinBusType(io_pin) == ESP32_BUS_TYPE_ADC_ONESHOT) {
used_channels++;
}
}
if (used_channels == 1) { //only 1 channel is used
esp_err_t err = adc_oneshot_del_unit(adc_handle[adc_unit].adc_oneshot_handle);
if (err != ESP_OK) {
return false;
}
adc_handle[adc_unit].adc_oneshot_handle = NULL;
if (adc_handle[adc_unit].adc_cali_handle != NULL) {
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
err = adc_cali_delete_scheme_curve_fitting(adc_handle[adc_unit].adc_cali_handle);
if (err != ESP_OK) {
return false;
}
#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
err = adc_cali_delete_scheme_line_fitting(adc_handle[adc_unit].adc_cali_handle);
if (err != ESP_OK) {
return false;
}
#else
log_e("ADC Calibration scheme is not supported!");
return false;
#endif
}
adc_handle[adc_unit].adc_cali_handle = NULL;
}
return true;
}
esp_err_t __analogChannelConfig(adc_bitwidth_t width, adc_attenuation_t atten, int8_t pin) {
esp_err_t err = ESP_OK;
adc_oneshot_chan_cfg_t config = {
.bitwidth = width,
.atten = (atten & 3),
};
if (pin == -1) { //Reconfigure all used analog pins/channels
for (int adc_unit = 0; adc_unit < SOC_ADC_PERIPH_NUM; adc_unit++) {
if (adc_handle[adc_unit].adc_oneshot_handle != NULL) {
for (uint8_t channel = 0; channel < SOC_ADC_CHANNEL_NUM(adc_unit); channel++) {
int io_pin;
adc_oneshot_channel_to_io(adc_unit, channel, &io_pin);
if (perimanGetPinBusType(io_pin) == ESP32_BUS_TYPE_ADC_ONESHOT) {
err = adc_oneshot_config_channel(adc_handle[adc_unit].adc_oneshot_handle, channel, &config);
if (err != ESP_OK) {
log_e("adc_oneshot_config_channel failed with error: %d", err);
return err;
}
}
}
//ADC calibration reconfig only if all channels are updated
if (adc_handle[adc_unit].adc_cali_handle != NULL) {
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
log_d("Deleting ADC_UNIT_%d cali handle", adc_unit);
err = adc_cali_delete_scheme_curve_fitting(adc_handle[adc_unit].adc_cali_handle);
if (err != ESP_OK) {
log_e("adc_cali_delete_scheme_curve_fitting failed with error: %d", err);
return err;
}
adc_cali_curve_fitting_config_t cali_config = {
.unit_id = adc_unit,
.atten = atten,
.bitwidth = width,
};
log_d("Creating ADC_UNIT_%d curve cali handle", adc_unit);
err = adc_cali_create_scheme_curve_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle);
if (err != ESP_OK) {
log_e("adc_cali_create_scheme_curve_fitting failed with error: %d", err);
return err;
}
#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
log_d("Deleting ADC_UNIT_%d line cali handle", adc_unit);
err = adc_cali_delete_scheme_line_fitting(adc_handle[adc_unit].adc_cali_handle);
if (err != ESP_OK) {
log_e("adc_cali_delete_scheme_line_fitting failed with error: %d", err);
return err;
}
adc_cali_line_fitting_config_t cali_config = {
.unit_id = adc_unit,
.atten = atten,
.bitwidth = width,
};
log_d("Creating ADC_UNIT_%d line cali handle", adc_unit);
err = adc_cali_create_scheme_line_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle);
if (err != ESP_OK) {
log_e("adc_cali_create_scheme_line_fitting failed with error: %d", err);
return err;
}
#else
log_e("ADC Calibration scheme is not supported!");
return ESP_ERR_NOT_SUPPORTED;
#endif
}
}
}
//make it default for next channels
__analogWidth = width;
__analogAttenuation = atten;
} else { //Reconfigure single channel
if (perimanGetPinBusType(pin) == ESP32_BUS_TYPE_ADC_ONESHOT) {
adc_channel_t channel;
adc_unit_t adc_unit;
adc_oneshot_io_to_channel(pin, &adc_unit, &channel);
if (err != ESP_OK) {
log_e("Pin %u is not ADC pin!", pin);
return err;
}
err = adc_oneshot_config_channel(adc_handle[adc_unit].adc_oneshot_handle, channel, &config);
if (err != ESP_OK) {
log_e("adc_oneshot_config_channel failed with error: %d", err);
return err;
}
} else {
log_e("Pin is not configured as analog channel");
}
}
return ESP_OK;
}
static inline uint16_t mapResolution(uint16_t value) {
uint8_t from = __analogWidth;
if (from == __analogReturnedWidth) {
return value;
}
if (from > __analogReturnedWidth) {
return value >> (from - __analogReturnedWidth);
}
return value << (__analogReturnedWidth - from);
}
void __analogSetAttenuation(adc_attenuation_t attenuation) {
if (__analogChannelConfig(__analogWidth, attenuation, -1) != ESP_OK) {
log_e("__analogChannelConfig failed!");
}
}
#if CONFIG_IDF_TARGET_ESP32
void __analogSetWidth(uint8_t bits) {
if (bits < SOC_ADC_RTC_MIN_BITWIDTH) {
bits = SOC_ADC_RTC_MIN_BITWIDTH;
} else if (bits > SOC_ADC_RTC_MAX_BITWIDTH) {
bits = SOC_ADC_RTC_MAX_BITWIDTH;
}
if (__analogChannelConfig(bits, __analogAttenuation, -1) != ESP_OK) {
log_e("__analogChannelConfig failed!");
}
}
#endif
esp_err_t __analogInit(uint8_t pin, adc_channel_t channel, adc_unit_t adc_unit) {
esp_err_t err = ESP_OK;
if (adc_handle[adc_unit].adc_oneshot_handle == NULL) {
adc_oneshot_unit_init_cfg_t init_config1 = {
.unit_id = adc_unit,
.ulp_mode = ADC_ULP_MODE_DISABLE,
};
err = adc_oneshot_new_unit(&init_config1, &adc_handle[adc_unit].adc_oneshot_handle);
if (err != ESP_OK) {
log_e("adc_oneshot_new_unit failed with error: %d", err);
return err;
}
}
perimanSetBusDeinit(ESP32_BUS_TYPE_ADC_ONESHOT, adcDetachBus);
if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_ADC_ONESHOT, (void *)(pin + 1), adc_unit, channel)) {
adcDetachBus((void *)(pin + 1));
return err;
}
adc_oneshot_chan_cfg_t config = {
.bitwidth = __analogWidth,
.atten = __analogAttenuation,
};
err = adc_oneshot_config_channel(adc_handle[adc_unit].adc_oneshot_handle, channel, &config);
if (err != ESP_OK) {
log_e("adc_oneshot_config_channel failed with error: %d", err);
return err;
}
return ESP_OK;
}
void __analogSetPinAttenuation(uint8_t pin, adc_attenuation_t attenuation) {
if (__analogChannelConfig(__analogWidth, attenuation, pin) != ESP_OK) {
log_e("__analogChannelConfig failed!");
}
}
void __analogReadResolution(uint8_t bits) {
if (!bits || bits > 16) {
return;
}
__analogReturnedWidth = bits;
#if CONFIG_IDF_TARGET_ESP32
__analogSetWidth(bits); // hardware analog resolution from 9 to 12
#endif
}
uint16_t __analogRead(uint8_t pin) {
int value = 0;
adc_channel_t channel;
adc_unit_t adc_unit;
esp_err_t err = ESP_OK;
err = adc_oneshot_io_to_channel(pin, &adc_unit, &channel);
if (err != ESP_OK) {
log_e("Pin %u is not ADC pin!", pin);
return value;
}
if (perimanGetPinBus(pin, ESP32_BUS_TYPE_ADC_ONESHOT) == NULL) {
log_d("Calling __analogInit! pin = %d", pin);
err = __analogInit(pin, channel, adc_unit);
if (err != ESP_OK) {
log_e("Analog initialization failed!");
return value;
}
}
adc_oneshot_read(adc_handle[adc_unit].adc_oneshot_handle, channel, &value);
return mapResolution(value);
}
uint32_t __analogReadMilliVolts(uint8_t pin) {
int value = 0;
adc_channel_t channel;
adc_unit_t adc_unit;
esp_err_t err = ESP_OK;
adc_oneshot_io_to_channel(pin, &adc_unit, &channel);
if (err != ESP_OK) {
log_e("Pin %u is not ADC pin!", pin);
return value;
}
if (perimanGetPinBus(pin, ESP32_BUS_TYPE_ADC_ONESHOT) == NULL) {
err = __analogInit(pin, channel, adc_unit);
if (err != ESP_OK) {
log_e("Analog initialization failed!");
return value;
}
}
if (adc_handle[adc_unit].adc_cali_handle == NULL) {
log_d("Creating cali handle for ADC_%d", adc_unit);
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
adc_cali_curve_fitting_config_t cali_config = {
.unit_id = adc_unit,
.atten = __analogAttenuation,
.bitwidth = __analogWidth,
};
err = adc_cali_create_scheme_curve_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle);
#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
adc_cali_line_fitting_config_t cali_config = {
.unit_id = adc_unit,
.bitwidth = __analogWidth,
.atten = __analogAttenuation,
};
err = adc_cali_create_scheme_line_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle);
#else
log_e("ADC Calibration scheme is not supported!");
return value;
#endif
if (err != ESP_OK) {
log_e("adc_cali_create_scheme_x failed!");
return value;
}
}
err = adc_oneshot_get_calibrated_result(adc_handle[adc_unit].adc_oneshot_handle, adc_handle[adc_unit].adc_cali_handle, channel, &value);
if (err != ESP_OK) {
log_e("adc_oneshot_get_calibrated_result failed!");
return 0;
}
return value;
}
extern uint16_t analogRead(uint8_t pin) __attribute__((weak, alias("__analogRead")));
extern uint32_t analogReadMilliVolts(uint8_t pin) __attribute__((weak, alias("__analogReadMilliVolts")));
extern void analogReadResolution(uint8_t bits) __attribute__((weak, alias("__analogReadResolution")));
extern void analogSetAttenuation(adc_attenuation_t attenuation) __attribute__((weak, alias("__analogSetAttenuation")));
extern void analogSetPinAttenuation(uint8_t pin, adc_attenuation_t attenuation) __attribute__((weak, alias("__analogSetPinAttenuation")));
#if CONFIG_IDF_TARGET_ESP32
extern void analogSetWidth(uint8_t bits) __attribute__((weak, alias("__analogSetWidth")));
#endif
/*
* ADC Continuous mode
*/
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
#define ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE1
#define ADC_GET_CHANNEL(p_data) ((p_data)->type1.channel)
#define ADC_GET_DATA(p_data) ((p_data)->type1.data)
#else
#define ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE2
#define ADC_GET_CHANNEL(p_data) ((p_data)->type2.channel)
#define ADC_GET_DATA(p_data) ((p_data)->type2.data)
#endif
static uint8_t __adcContinuousAtten = ADC_11db;
static uint8_t __adcContinuousWidth = SOC_ADC_DIGI_MAX_BITWIDTH;
static uint8_t used_adc_channels = 0;
adc_continuous_result_t *adc_result = NULL;
static bool adcContinuousDetachBus(void *adc_unit_number) {
adc_unit_t adc_unit = (adc_unit_t)adc_unit_number - 1;
// Guard against double-cleanup: check if already cleaned up
if (adc_handle[adc_unit].adc_continuous_handle == NULL) {
return true;
}
// Clean up ADC driver
esp_err_t err = adc_continuous_deinit(adc_handle[adc_unit].adc_continuous_handle);
if (err != ESP_OK) {
return false;
}
adc_handle[adc_unit].adc_continuous_handle = NULL;
// Clean up calibration handle if exists
if (adc_handle[adc_unit].adc_cali_handle != NULL) {
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
err = adc_cali_delete_scheme_curve_fitting(adc_handle[adc_unit].adc_cali_handle);
if (err != ESP_OK) {
return false;
}
#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
err = adc_cali_delete_scheme_line_fitting(adc_handle[adc_unit].adc_cali_handle);
if (err != ESP_OK) {
return false;
}
#else
log_e("ADC Calibration scheme is not supported!");
return false;
#endif
adc_handle[adc_unit].adc_cali_handle = NULL;
}
// Don't call perimanClearPinBus() here - the peripheral manager already handles it.
// This callback is only responsible for cleaning up the IDF's ADC driver and calibration handles.
// It does NOT free the adc_result buffer. The caller is responsible for freeing adc_result.
return true;
}
bool IRAM_ATTR adcFnWrapper(adc_continuous_handle_t handle, const adc_continuous_evt_data_t *edata, void *args) {
interrupt_config_t *isr = (interrupt_config_t *)args;
//Check if edata->size matches conversion_frame_size, else just return from ISR
if (edata->size == adc_handle[0].conversion_frame_size) {
if (isr->fn) {
if (isr->arg) {
((voidFuncPtrArg)isr->fn)(isr->arg);
} else {
isr->fn();
}
}
}
return false;
}
esp_err_t __analogContinuousInit(adc_channel_t *channel, uint8_t channel_num, adc_unit_t adc_unit, uint32_t sampling_freq_hz) {
//Create new ADC continuous handle
adc_continuous_handle_cfg_t adc_config = {
.max_store_buf_size = adc_handle[adc_unit].buffer_size,
.conv_frame_size = adc_handle[adc_unit].conversion_frame_size,
};
esp_err_t err = adc_continuous_new_handle(&adc_config, &adc_handle[adc_unit].adc_continuous_handle);
if (err != ESP_OK) {
log_e("adc_continuous_new_handle failed with error: %d", err);
return ESP_FAIL;
}
//Configure adc pins
adc_continuous_config_t dig_cfg = {
.sample_freq_hz = sampling_freq_hz,
.conv_mode = ADC_CONV_SINGLE_UNIT_1,
.format = ADC_OUTPUT_TYPE,
};
adc_digi_pattern_config_t adc_pattern[SOC_ADC_PATT_LEN_MAX] = {0};
dig_cfg.pattern_num = channel_num;
for (int i = 0; i < channel_num; i++) {
adc_pattern[i].atten = __adcContinuousAtten;
adc_pattern[i].channel = channel[i];
adc_pattern[i].unit = ADC_UNIT_1;
adc_pattern[i].bit_width = __adcContinuousWidth;
}
dig_cfg.adc_pattern = adc_pattern;
err = adc_continuous_config(adc_handle[adc_unit].adc_continuous_handle, &dig_cfg);
if (err != ESP_OK) {
log_e("adc_continuous_config failed with error: %d", err);
return ESP_FAIL;
}
used_adc_channels = channel_num;
return ESP_OK;
}
bool analogContinuous(const uint8_t pins[], size_t pins_count, uint32_t conversions_per_pin, uint32_t sampling_freq_hz, void (*userFunc)(void)) {
adc_channel_t channel[pins_count];
adc_unit_t adc_unit = ADC_UNIT_1;
esp_err_t err = ESP_OK;
//Convert pins to channels and check if all are ADC1s unit
for (int i = 0; i < pins_count; i++) {
err = adc_continuous_io_to_channel(pins[i], &adc_unit, &channel[i]);
if (err != ESP_OK) {
log_e("Pin %u is not ADC pin!", pins[i]);
return false;
}
if (adc_unit != 0) {
log_e("Only ADC1 pins are supported in continuous mode!");
return false;
}
}
//Check if Oneshot and Continuous handle exists
if (adc_handle[adc_unit].adc_oneshot_handle != NULL) {
log_e("ADC%d is running in oneshot mode. Aborting.", adc_unit + 1);
return false;
}
if (adc_handle[adc_unit].adc_continuous_handle != NULL) {
log_e("ADC%d continuous is already initialized. To reconfigure call analogContinuousDeinit() first.", adc_unit + 1);
return false;
}
//Check sampling frequency
if ((sampling_freq_hz < SOC_ADC_SAMPLE_FREQ_THRES_LOW) || (sampling_freq_hz > SOC_ADC_SAMPLE_FREQ_THRES_HIGH)) {
log_e("Sampling frequency is out of range. Supported sampling frequencies are %d - %d", SOC_ADC_SAMPLE_FREQ_THRES_LOW, SOC_ADC_SAMPLE_FREQ_THRES_HIGH);
return false;
}
//Set periman deinit function and reset all pins to init state.
perimanSetBusDeinit(ESP32_BUS_TYPE_ADC_CONT, adcContinuousDetachBus);
for (int j = 0; j < pins_count; j++) {
if (!perimanClearPinBus(pins[j])) {
return false;
}
}
//Set conversion frame and buffer size (conversion frame must be in multiples of SOC_ADC_DIGI_DATA_BYTES_PER_CONV)
adc_handle[adc_unit].conversion_frame_size = conversions_per_pin * pins_count * SOC_ADC_DIGI_RESULT_BYTES;
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
uint8_t calc_multiple = adc_handle[adc_unit].conversion_frame_size % SOC_ADC_DIGI_DATA_BYTES_PER_CONV;
if (calc_multiple != 0) {
adc_handle[adc_unit].conversion_frame_size = (adc_handle[adc_unit].conversion_frame_size + calc_multiple);
}
#endif
#if CONFIG_IDF_TARGET_ESP32P4
// Align conversion frame size to cache line size (required for DMA on targets with cache)
uint32_t alignment_remainder = adc_handle[adc_unit].conversion_frame_size % CONFIG_CACHE_L1_CACHE_LINE_SIZE;
if (alignment_remainder != 0) {
adc_handle[adc_unit].conversion_frame_size += (CONFIG_CACHE_L1_CACHE_LINE_SIZE - alignment_remainder);
}
#endif
adc_handle[adc_unit].buffer_size = adc_handle[adc_unit].conversion_frame_size * 2;
//Conversion frame size buffer cant be bigger than 4092 bytes
if (adc_handle[adc_unit].conversion_frame_size > 4092) {
log_e("Buffers are too big. Please set lower conversions per pin.");
return false;
}
//Initialize continuous handle and pins
err = __analogContinuousInit(channel, sizeof(channel) / sizeof(adc_channel_t), adc_unit, sampling_freq_hz);
if (err != ESP_OK) {
log_e("Analog initialization failed!");
return false;
}
//Setup callbacks for complete event
adc_continuous_evt_cbs_t cbs = {
.on_conv_done = adcFnWrapper,
//.on_pool_ovf can be used in future
};
adc_handle[adc_unit].adc_interrupt_handle.fn = (voidFuncPtr)userFunc;
err = adc_continuous_register_event_callbacks(adc_handle[adc_unit].adc_continuous_handle, &cbs, &adc_handle[adc_unit].adc_interrupt_handle);
if (err != ESP_OK) {
log_e("adc_continuous_register_event_callbacks failed!");
return false;
}
//Allocate and prepare result structure for adc readings
adc_result = malloc(pins_count * sizeof(adc_continuous_result_t));
for (int k = 0; k < pins_count; k++) {
adc_result[k].pin = pins[k];
adc_result[k].channel = channel[k];
}
//Initialize ADC calibration handle
if (adc_handle[adc_unit].adc_cali_handle == NULL) {
log_d("Creating cali handle for ADC_%d", adc_unit);
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
adc_cali_curve_fitting_config_t cali_config = {
.unit_id = adc_unit,
.atten = __adcContinuousAtten,
.bitwidth = __adcContinuousWidth,
};
err = adc_cali_create_scheme_curve_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle);
#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
adc_cali_line_fitting_config_t cali_config = {
.unit_id = adc_unit,
.bitwidth = __adcContinuousWidth,
.atten = __adcContinuousAtten,
};
err = adc_cali_create_scheme_line_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle);
#else
log_e("ADC Calibration scheme is not supported!");
return false;
#endif
if (err != ESP_OK) {
log_e("adc_cali_create_scheme_x failed!");
return false;
}
}
for (int k = 0; k < pins_count; k++) {
if (!perimanSetPinBus(pins[k], ESP32_BUS_TYPE_ADC_CONT, (void *)(adc_unit + 1), adc_unit, channel[k])) {
log_e("perimanSetPinBus to ADC Continuous failed!");
adcContinuousDetachBus((void *)(adc_unit + 1));
return false;
}
}
return true;
}
bool analogContinuousRead(adc_continuous_result_t **buffer, uint32_t timeout_ms) {
if (adc_handle[ADC_UNIT_1].adc_continuous_handle != NULL) {
uint32_t bytes_read = 0;
uint32_t read_raw[used_adc_channels];
uint32_t read_count[used_adc_channels];
// Allocate DMA buffer with cache line alignment (required for ESP32-P4 and other targets with cache)
size_t buffer_size = adc_handle[ADC_UNIT_1].conversion_frame_size;
#if CONFIG_IDF_TARGET_ESP32P4
uint8_t *adc_read = (uint8_t *)heap_caps_aligned_alloc(CONFIG_CACHE_L1_CACHE_LINE_SIZE, buffer_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
#else
uint8_t *adc_read = (uint8_t *)heap_caps_malloc(buffer_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
#endif
if (adc_read == NULL) {
log_e("Failed to allocate DMA buffer");
*buffer = NULL;
return false;
}
memset(adc_read, 0xcc, buffer_size);
memset(read_raw, 0, sizeof(read_raw));
memset(read_count, 0, sizeof(read_count));
esp_err_t err = adc_continuous_read(adc_handle[ADC_UNIT_1].adc_continuous_handle, adc_read, adc_handle[0].conversion_frame_size, &bytes_read, timeout_ms);
if (err != ESP_OK) {
if (err == ESP_ERR_TIMEOUT) {
log_e("Reading data failed: No data, increase timeout");
} else {
log_e("Reading data failed with error: %X", err);
}
free(adc_read);
adc_read = NULL;
*buffer = NULL;
return false;
}
for (int i = 0; i < bytes_read; i += SOC_ADC_DIGI_RESULT_BYTES) {
adc_digi_output_data_t *p = (adc_digi_output_data_t *)&adc_read[i];
uint32_t chan_num = ADC_GET_CHANNEL(p);
uint32_t data = ADC_GET_DATA(p);
/* Check the channel number validation, the data is invalid if the channel num exceed the maximum channel */
if (chan_num >= SOC_ADC_CHANNEL_NUM(0)) {
log_e("Invalid data [%d_%d]", chan_num, data);
*buffer = NULL;
return false;
}
if (data >= (1 << SOC_ADC_DIGI_MAX_BITWIDTH)) {
data = 0;
log_e("Invalid data");
}
for (int j = 0; j < used_adc_channels; j++) {
if (adc_result[j].channel == chan_num) {
read_raw[j] += data;
read_count[j] += 1;
break;
}
}
}
for (int j = 0; j < used_adc_channels; j++) {
if (read_count[j] != 0) {
adc_result[j].avg_read_raw = read_raw[j] / read_count[j];
adc_cali_raw_to_voltage(adc_handle[ADC_UNIT_1].adc_cali_handle, adc_result[j].avg_read_raw, &adc_result[j].avg_read_mvolts);
} else {
log_w("No data read for pin %d", adc_result[j].pin);
}
}
free(adc_read);
adc_read = NULL;
*buffer = adc_result;
return true;
} else {
log_e("ADC Continuous is not initialized!");
return false;
}
}
bool analogContinuousStart() {
if (adc_handle[ADC_UNIT_1].adc_continuous_handle != NULL) {
if (adc_continuous_start(adc_handle[ADC_UNIT_1].adc_continuous_handle) == ESP_OK) {
return true;
}
} else {
log_e("ADC Continuous is not initialized!");
}
return false;
}
bool analogContinuousStop() {
if (adc_handle[ADC_UNIT_1].adc_continuous_handle != NULL) {
if (adc_continuous_stop(adc_handle[ADC_UNIT_1].adc_continuous_handle) == ESP_OK) {
return true;
}
} else {
log_e("ADC Continuous is not initialized!");
}
return false;
}
bool analogContinuousDeinit() {
if (adc_handle[ADC_UNIT_1].adc_continuous_handle == NULL) {
log_i("ADC Continuous was not initialized");
return true;
}
// Clear all used pins from peripheral manager
// This will trigger adcContinuousDetachBus() callback which cleans up the ADC driver
for (uint8_t channel = 0; channel < SOC_ADC_CHANNEL_NUM(ADC_UNIT_1); channel++) {
int io_pin;
adc_oneshot_channel_to_io(ADC_UNIT_1, channel, &io_pin);
if (perimanGetPinBusType(io_pin) == ESP32_BUS_TYPE_ADC_CONT) {
if (!perimanClearPinBus(io_pin)) {
return false;
}
}
}
// Free the result buffer (callback doesn't do this)
if (adc_result != NULL) {
free(adc_result);
adc_result = NULL;
}
return true;
}
void analogContinuousSetAtten(adc_attenuation_t attenuation) {
__adcContinuousAtten = attenuation;
}
void analogContinuousSetWidth(uint8_t bits) {
if ((bits < SOC_ADC_DIGI_MIN_BITWIDTH) || (bits > SOC_ADC_DIGI_MAX_BITWIDTH)) {
log_e("Selected width cannot be set. Range is from %d to %d", SOC_ADC_DIGI_MIN_BITWIDTH, SOC_ADC_DIGI_MAX_BITWIDTH);
return;
}
__adcContinuousWidth = bits;
}
#endif
+133
View File
@@ -0,0 +1,133 @@
/*
Arduino.h - Main include file for the Arduino SDK
Copyright (c) 2005-2013 Arduino Team. All right 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 "soc/soc_caps.h"
#if SOC_ADC_SUPPORTED
#ifdef __cplusplus
extern "C" {
#endif
#include "esp32-hal.h"
typedef enum {
ADC_0db,
ADC_2_5db,
ADC_6db,
ADC_11db,
ADC_ATTENDB_MAX
} adc_attenuation_t;
/*
* Get ADC value for pin
* */
uint16_t analogRead(uint8_t pin);
/*
* Get MilliVolts value for pin
* */
uint32_t analogReadMilliVolts(uint8_t pin);
/*
* Set the resolution of analogRead return values. Default is 12 bits (range from 0 to 4096).
* If between 9 and 12, it will equal the set hardware resolution, else value will be shifted.
* Range is 1 - 16
*
* Note: compatibility with Arduino SAM
*/
void analogReadResolution(uint8_t bits);
/*
* Set the attenuation for all channels
* Default is 11db
* */
void analogSetAttenuation(adc_attenuation_t attenuation);
/*
* Set the attenuation for particular pin
* Default is 11db
* */
void analogSetPinAttenuation(uint8_t pin, adc_attenuation_t attenuation);
#if CONFIG_IDF_TARGET_ESP32
/*
* Sets the sample bits and read resolution
* Default is 12bit (0 - 4095)
* Range is 9 - 12
* */
void analogSetWidth(uint8_t bits);
#endif
/*
* Analog Continuous mode
* */
typedef struct {
uint8_t pin; /*!<ADC pin */
uint8_t channel; /*!<ADC channel */
int avg_read_raw; /*!<ADC average raw data */
int avg_read_mvolts; /*!<ADC average voltage in mV */
} adc_continuous_result_t;
/*
* Setup ADC continuous peripheral
* */
bool analogContinuous(const uint8_t pins[], size_t pins_count, uint32_t conversions_per_pin, uint32_t sampling_freq_hz, void (*userFunc)(void));
/*
* Read ADC continuous conversion data
* */
bool analogContinuousRead(adc_continuous_result_t **buffer, uint32_t timeout_ms);
/*
* Start ADC continuous conversions
* */
bool analogContinuousStart();
/*
* Stop ADC continuous conversions
* */
bool analogContinuousStop();
/*
* Deinitialize ADC continuous peripheral
* */
bool analogContinuousDeinit();
/*
* Sets the attenuation for continuous mode reading
* Default is 11db
* */
void analogContinuousSetAtten(adc_attenuation_t attenuation);
/*
* Sets the read resolution for continuous mode
* Default is 12bit (0 - 4095)
* Range is 9 - 12
* */
void analogContinuousSetWidth(uint8_t bits);
#ifdef __cplusplus
}
#endif
#endif /* SOC_ADC_SUPPORTED */
+44
View File
@@ -0,0 +1,44 @@
// Copyright 2015-2026 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ESP32_HAL_BT_MEM_H
#define ESP32_HAL_BT_MEM_H
#include "soc/soc_caps.h"
#include "sdkconfig.h"
#if defined(SOC_BT_CLASSIC_SUPPORTED) || defined(SOC_BLE_SUPPORTED)
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
// Flag defined in esp32-hal-bt.c, set by constructors when BT libraries are linked
extern bool _btLibraryInUse;
// Constructor runs before app_main(), setting the flag if any BT library is used.
// Multiple libraries including this header just set the same flag to true.
__attribute__((constructor)) static void _setBtLibraryInUse(void) {
_btLibraryInUse = true;
}
#ifdef __cplusplus
}
#endif
#endif /* SOC_BT_SUPPORTED */
#endif /* ESP32_HAL_BT_MEM_H */
+133
View File
@@ -0,0 +1,133 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "esp32-hal-bt.h"
#if SOC_BT_SUPPORTED
#if (defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)) && __has_include("esp_bt.h")
// Flag set by constructors in esp32-hal-bt-mem.h when BT libraries are linked
bool _btLibraryInUse = false;
// Default behavior: release BTDM memory (~36KB) unless a BT library is used or user overrides.
// BT libraries include esp32-hal-bt-mem.h which sets _btLibraryInUse = true via constructor.
// Users can also provide their own strong btInUse() implementation.
__attribute__((weak)) bool btInUse(void) {
return _btLibraryInUse;
}
#include "esp_bt.h"
#ifdef CONFIG_BTDM_CONTROLLER_MODE_BTDM
#define BT_MODE ESP_BT_MODE_BTDM
#elif defined(CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY)
#define BT_MODE ESP_BT_MODE_CLASSIC_BT
#else
#define BT_MODE ESP_BT_MODE_BLE
#endif
bool btStarted() {
return (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED);
}
bool btStart() {
return btStartMode(BT_MODE);
}
bool btStartMode(bt_mode mode) {
esp_bt_mode_t esp_bt_mode;
esp_bt_controller_config_t cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
#if CONFIG_IDF_TARGET_ESP32
switch (mode) {
case BT_MODE_BLE: esp_bt_mode = ESP_BT_MODE_BLE; break;
case BT_MODE_CLASSIC_BT: esp_bt_mode = ESP_BT_MODE_CLASSIC_BT; break;
case BT_MODE_BTDM: esp_bt_mode = ESP_BT_MODE_BTDM; break;
default: esp_bt_mode = BT_MODE; break;
}
// esp_bt_controller_enable(MODE) This mode must be equal as the mode in “cfg” of esp_bt_controller_init().
cfg.mode = esp_bt_mode;
if (cfg.mode == ESP_BT_MODE_CLASSIC_BT) {
esp_bt_controller_mem_release(ESP_BT_MODE_BLE);
}
#else
// other esp variants dont support BT-classic / DM.
esp_bt_mode = BT_MODE;
#endif
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED) {
return true;
}
esp_err_t ret;
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) {
if ((ret = esp_bt_controller_init(&cfg)) != ESP_OK) {
log_e("initialize controller failed: %s", esp_err_to_name(ret));
return false;
}
while (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) {}
}
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED) {
if ((ret = esp_bt_controller_enable(esp_bt_mode)) != ESP_OK) {
log_e("BT Enable mode=%d failed %s", BT_MODE, esp_err_to_name(ret));
return false;
}
}
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED) {
return true;
}
log_e("BT Start failed");
return false;
}
bool btStop() {
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) {
return true;
}
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED) {
if (esp_bt_controller_disable()) {
log_e("BT Disable failed");
return false;
}
while (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED);
}
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED) {
if (esp_bt_controller_deinit()) {
log_e("BT deint failed");
return false;
}
vTaskDelay(1);
if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_IDLE) {
return false;
}
return true;
}
log_e("BT Stop failed");
return false;
}
#else // !__has_include("esp_bt.h") || !(defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED))
bool btStarted() {
return false;
}
bool btStart() {
return false;
}
bool btStop() {
return false;
}
#endif /* !__has_include("esp_bt.h") || !(defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)) */
#endif /* SOC_BT_SUPPORTED */
+51
View File
@@ -0,0 +1,51 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP32_ESP32_HAL_BT_H_
#define _ESP32_ESP32_HAL_BT_H_
#include "soc/soc_caps.h"
#if SOC_BT_SUPPORTED
#include "esp32-hal.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
BT_MODE_DEFAULT,
BT_MODE_BLE,
BT_MODE_CLASSIC_BT,
BT_MODE_BTDM
} bt_mode;
// Returns true if BT memory should be kept, false to release it (~36KB).
// Default (weak): returns false unless a BT library is linked.
// BT libraries include esp32-hal-bt-mem.h which automatically sets a flag.
// Users may also provide their own strong btInUse() to override.
bool btInUse(void);
bool btStarted();
bool btStart();
bool btStartMode(bt_mode mode);
bool btStop();
#ifdef __cplusplus
}
#endif
#endif /* SOC_BT_SUPPORTED */
#endif /* _ESP32_ESP32_HAL_BT_H_ */
+335
View File
@@ -0,0 +1,335 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "soc/rtc.h"
#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3)
#include "soc/rtc_cntl_reg.h"
#include "soc/syscon_reg.h"
#endif
#include "soc/efuse_reg.h"
#include "esp32-hal.h"
#include "esp32-hal-cpu.h"
#include "hal/timer_ll.h"
#include "esp_private/systimer.h"
#include "esp_system.h"
#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
#include "xtensa_timer.h"
#include "esp32/rom/rtc.h"
static const char *clock_source_names[] = {"XTAL", "PLL", "8.5M", "APLL"};
#elif CONFIG_IDF_TARGET_ESP32S2
#include "xtensa_timer.h"
#include "esp32s2/rom/rtc.h"
static const char *clock_source_names[] = {"XTAL", "PLL", "8.5M", "APLL"};
#elif CONFIG_IDF_TARGET_ESP32S3
#include "xtensa_timer.h"
#include "esp32s3/rom/rtc.h"
static const char *clock_source_names[] = {"XTAL", "PLL", "17.5M"};
#elif CONFIG_IDF_TARGET_ESP32C2
#include "esp32c2/rom/rtc.h"
static const char *clock_source_names[] = {"XTAL", "PLL", "17.5M"};
#elif CONFIG_IDF_TARGET_ESP32C3
#include "esp32c3/rom/rtc.h"
static const char *clock_source_names[] = {"XTAL", "PLL", "17.5M"};
#elif CONFIG_IDF_TARGET_ESP32C6
#include "esp32c6/rom/rtc.h"
static const char *clock_source_names[] = {"XTAL", "PLL", "17.5M"};
#elif CONFIG_IDF_TARGET_ESP32H2
#include "esp32h2/rom/rtc.h"
static const char *clock_source_names[] = {"XTAL", "PLL", "8.5M", "FLASH_PLL"};
#elif CONFIG_IDF_TARGET_ESP32P4
#include "esp32p4/rom/rtc.h"
static const char *clock_source_names[] = {"XTAL", "CPLL", "17.5M"};
#elif CONFIG_IDF_TARGET_ESP32C5
#include "esp32c5/rom/rtc.h"
static const char *clock_source_names[] = {"XTAL", "17.5M", "PLL_F160M", "PLL_F240M"};
#elif CONFIG_IDF_TARGET_ESP32C61
#include "esp32c61/rom/rtc.h"
static const char *clock_source_names[] = {"XTAL", "17.5M", "PLL_F160M"};
#else
#error Target CONFIG_IDF_TARGET is not supported
#endif
#else // ESP32 Before IDF 4.0
#include "rom/rtc.h"
#endif
typedef struct apb_change_cb_s {
struct apb_change_cb_s *prev;
struct apb_change_cb_s *next;
void *arg;
apb_change_cb_t cb;
} apb_change_t;
static apb_change_t *apb_change_callbacks = NULL;
static SemaphoreHandle_t apb_change_lock = NULL;
static void initApbChangeCallback() {
static volatile bool initialized = false;
if (!initialized) {
initialized = true;
apb_change_lock = xSemaphoreCreateMutex();
if (!apb_change_lock) {
initialized = false;
}
}
}
static void triggerApbChangeCallback(apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb) {
initApbChangeCallback();
xSemaphoreTake(apb_change_lock, portMAX_DELAY);
apb_change_t *r = apb_change_callbacks;
if (r != NULL) {
if (ev_type == APB_BEFORE_CHANGE) {
while (r != NULL) {
r->cb(r->arg, ev_type, old_apb, new_apb);
r = r->next;
}
} else { // run backwards through chain
while (r->next != NULL) {
r = r->next; // find first added
}
while (r != NULL) {
r->cb(r->arg, ev_type, old_apb, new_apb);
r = r->prev;
}
}
}
xSemaphoreGive(apb_change_lock);
}
bool addApbChangeCallback(void *arg, apb_change_cb_t cb) {
initApbChangeCallback();
apb_change_t *c = (apb_change_t *)malloc(sizeof(apb_change_t));
if (!c) {
log_e("Callback Object Malloc Failed");
return false;
}
c->next = NULL;
c->prev = NULL;
c->arg = arg;
c->cb = cb;
xSemaphoreTake(apb_change_lock, portMAX_DELAY);
if (apb_change_callbacks == NULL) {
apb_change_callbacks = c;
} else {
apb_change_t *r = apb_change_callbacks;
// look for duplicate callbacks
while ((r != NULL) && !((r->cb == cb) && (r->arg == arg))) {
r = r->next;
}
if (r) {
log_e("duplicate func=%8p arg=%8p", c->cb, c->arg);
free(c);
xSemaphoreGive(apb_change_lock);
return false;
} else {
c->next = apb_change_callbacks;
apb_change_callbacks->prev = c;
apb_change_callbacks = c;
}
}
xSemaphoreGive(apb_change_lock);
return true;
}
bool removeApbChangeCallback(void *arg, apb_change_cb_t cb) {
initApbChangeCallback();
xSemaphoreTake(apb_change_lock, portMAX_DELAY);
apb_change_t *r = apb_change_callbacks;
// look for matching callback
while ((r != NULL) && !((r->cb == cb) && (r->arg == arg))) {
r = r->next;
}
if (r == NULL) {
log_e("not found func=%8p arg=%8p", cb, arg);
xSemaphoreGive(apb_change_lock);
return false;
} else {
// patch links
if (r->prev) {
r->prev->next = r->next;
} else { // this is first link
apb_change_callbacks = r->next;
}
if (r->next) {
r->next->prev = r->prev;
}
free(r);
}
xSemaphoreGive(apb_change_lock);
return true;
}
static uint32_t calculateApb(rtc_cpu_freq_config_t *conf) {
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
if (conf->freq_mhz >= 80) {
return 80 * MHZ;
}
return (conf->source_freq_mhz * MHZ) / conf->div;
#else
return APB_CLK_FREQ;
#endif
}
#if defined(CONFIG_IDF_TARGET_ESP32) && !defined(LACT_MODULE) && !defined(LACT_TICKS_PER_US)
void esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us); //private in IDF
#endif
const char *getClockSourceName(uint8_t source) {
if (source < SOC_CPU_CLK_SRC_INVALID) {
return clock_source_names[source];
}
return "Invalid";
}
const char *getSupportedCpuFrequencyMhz(uint8_t xtal) {
char *supported_frequencies = (char *)calloc(256, sizeof(char));
int pos = 0;
#if TARGET_CPU_FREQ_MAX_400
#if CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP32P4_REV_MIN_FULL < 300
pos += snprintf(supported_frequencies + pos, 256 - pos, "360");
#else
pos += snprintf(supported_frequencies + pos, 256 - pos, "400");
#endif
#elif TARGET_CPU_FREQ_MAX_240
#if CONFIG_IDF_TARGET_ESP32
if (!REG_GET_BIT(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_CPU_FREQ_RATED) || !REG_GET_BIT(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_CPU_FREQ_LOW)) {
pos += snprintf(supported_frequencies + pos, 256 - pos, "160, 80");
} else
#endif
{
pos += snprintf(supported_frequencies + pos, 256 - pos, "240, 160, 80");
}
#elif TARGET_CPU_FREQ_MAX_160
pos += snprintf(supported_frequencies + pos, 256 - pos, "160, 120, 80");
#elif TARGET_CPU_FREQ_MAX_120
pos += snprintf(supported_frequencies + pos, 256 - pos, "120, 80");
#elif TARGET_CPU_FREQ_MAX_96
pos += snprintf(supported_frequencies + pos, 256 - pos, "96, 64, 48");
#else
free(supported_frequencies);
return "Unknown";
#endif
// Append xtal and its dividers only if xtal is nonzero
if (xtal != 0) {
// We'll show as: , <xtal>, <xtal/2>[, <xtal/4>] MHz
pos += snprintf(supported_frequencies + pos, 256 - pos, ", %u, %u", xtal, xtal / 2);
#if CONFIG_IDF_TARGET_ESP32
// Only append xtal/4 if it's > 0 and meaningful for higher-frequency chips (e.g., ESP32 40MHz/4=10)
if (xtal >= RTC_XTAL_FREQ_40M) {
pos += snprintf(supported_frequencies + pos, 256 - pos, ", %u", xtal / 4);
}
#endif
}
pos += snprintf(supported_frequencies + pos, 256 - pos, " MHz");
return supported_frequencies;
}
bool setCpuFrequencyMhz(uint32_t cpu_freq_mhz) {
rtc_cpu_freq_config_t conf, cconf;
uint32_t capb, apb;
[[maybe_unused]]
uint8_t xtal = 0;
// ===== Get XTAL Frequency and validate input =====
#if TARGET_HAS_XTAL_FREQ
xtal = (uint8_t)rtc_clk_xtal_freq_get();
#endif
// ===== Get current configuration and check if change is needed =====
rtc_clk_cpu_freq_get_config(&cconf);
if (cconf.freq_mhz == cpu_freq_mhz) {
return true; // Frequency already set
}
// ===== Get configuration for new frequency =====
if (!rtc_clk_cpu_freq_mhz_to_config(cpu_freq_mhz, &conf)) {
log_e("CPU clock could not be set to %u MHz. Supported frequencies: %s", cpu_freq_mhz, getSupportedCpuFrequencyMhz(xtal));
return false;
}
// ===== Calculate APB frequencies =====
capb = calculateApb(&cconf);
apb = calculateApb(&conf);
// ===== Apply frequency change =====
if (apb_change_callbacks) {
triggerApbChangeCallback(APB_BEFORE_CHANGE, capb, apb);
}
rtc_clk_cpu_freq_set_config_fast(&conf);
// Update APB frequency for targets with dynamic APB
#if TARGET_HAS_DYNAMIC_APB
if (capb != apb) {
// Update REF_TICK (uncomment if REF_TICK is different than 1MHz)
// if (conf.freq_mhz < 80) {
// ESP_REG(APB_CTRL_XTAL_TICK_CONF_REG) = conf.freq_mhz / (REF_CLK_FREQ / MHZ) - 1;
// }
rtc_clk_apb_freq_update(apb);
// ESP32-specific: Update esp_timer divisor
#if CONFIG_IDF_TARGET_ESP32
#if defined(LACT_MODULE) && defined(LACT_TICKS_PER_US)
timer_ll_set_lact_clock_prescale(TIMER_LL_GET_HW(LACT_MODULE), apb / MHZ / LACT_TICKS_PER_US);
#else
esp_timer_impl_update_apb_freq(apb / MHZ);
#endif
#endif
}
#endif
// Update FreeRTOS Tick Divisor for Xtensa targets
#if TARGET_HAS_XTENSA_TICK
uint32_t fcpu = (conf.freq_mhz >= 80) ? (conf.freq_mhz * MHZ) : (apb);
_xt_tick_divisor = fcpu / XT_TICK_PER_SEC;
#endif
if (apb_change_callbacks) {
triggerApbChangeCallback(APB_AFTER_CHANGE, capb, apb);
}
// ===== Debug logging =====
log_d("%s: %u / %u = %u Mhz, APB: %u Hz", getClockSourceName(conf.source), conf.source_freq_mhz, conf.div, conf.freq_mhz, apb);
return true;
}
uint32_t getCpuFrequencyMhz() {
rtc_cpu_freq_config_t conf;
rtc_clk_cpu_freq_get_config(&conf);
return conf.freq_mhz;
}
uint32_t getXtalFrequencyMhz() {
return rtc_clk_xtal_freq_get();
}
uint32_t getApbFrequency() {
rtc_cpu_freq_config_t conf;
rtc_clk_cpu_freq_get_config(&conf);
return calculateApb(&conf);
}
+119
View File
@@ -0,0 +1,119 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP32_HAL_CPU_H_
#define _ESP32_HAL_CPU_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include "sdkconfig.h"
#include "soc/soc_caps.h"
// When adding a new target, update the appropriate group(s) below
// Targets that support XTAL frequency queries via rtc_clk_xtal_freq_get()
#if (!defined(CONFIG_IDF_TARGET_ESP32C5) && !defined(CONFIG_IDF_TARGET_ESP32P4))
#define TARGET_HAS_XTAL_FREQ 1
#else
#define TARGET_HAS_XTAL_FREQ 0
#endif
// Targets that need dynamic APB frequency updates via rtc_clk_apb_freq_update()
#if (defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3))
#define TARGET_HAS_DYNAMIC_APB 1
#else
#define TARGET_HAS_DYNAMIC_APB 0
#endif
// Xtensa architecture targets that need FreeRTOS tick divisor updates
#if (defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2))
#define TARGET_HAS_XTENSA_TICK 1
#else
#define TARGET_HAS_XTENSA_TICK 0
#endif
// Targets with APLL support (uses IDF SOC capability macro)
// Note: ESP32-P4 APLL support is not yet fully implemented in IDF
#if (defined(SOC_CLK_APLL_SUPPORTED) && !defined(CONFIG_IDF_TARGET_ESP32P4))
#define TARGET_HAS_APLL 1
#else
#define TARGET_HAS_APLL 0
#endif
// Targets grouped by maximum CPU frequency support
#if (defined(CONFIG_IDF_TARGET_ESP32P4))
#define TARGET_CPU_FREQ_MAX_400 1
#else
#define TARGET_CPU_FREQ_MAX_400 0
#endif
#if (defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3))
#define TARGET_CPU_FREQ_MAX_240 1
#else
#define TARGET_CPU_FREQ_MAX_240 0
#endif
#if (defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32C61))
#define TARGET_CPU_FREQ_MAX_160 1
#else
#define TARGET_CPU_FREQ_MAX_160 0
#endif
#if (defined(CONFIG_IDF_TARGET_ESP32C2))
#define TARGET_CPU_FREQ_MAX_120 1
#else
#define TARGET_CPU_FREQ_MAX_120 0
#endif
#if (defined(CONFIG_IDF_TARGET_ESP32H2))
#define TARGET_CPU_FREQ_MAX_96 1
#else
#define TARGET_CPU_FREQ_MAX_96 0
#endif
typedef enum {
APB_BEFORE_CHANGE,
APB_AFTER_CHANGE
} apb_change_ev_t;
typedef void (*apb_change_cb_t)(void *arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb);
bool addApbChangeCallback(void *arg, apb_change_cb_t cb);
bool removeApbChangeCallback(void *arg, apb_change_cb_t cb);
//function takes the following frequencies as valid values:
// 240, 160, 80 <<< For all XTAL types
// 40, 20, 10 <<< For 40MHz XTAL
// 26, 13 <<< For 26MHz XTAL
// 24, 12 <<< For 24MHz XTAL
bool setCpuFrequencyMhz(uint32_t cpu_freq_mhz);
const char *getSupportedCpuFrequencyMhz(uint8_t xtal);
const char *getClockSourceName(uint8_t source);
uint32_t getCpuFrequencyMhz(); // In MHz
uint32_t getXtalFrequencyMhz(); // In MHz
uint32_t getApbFrequency(); // In Hz
#ifdef __cplusplus
}
#endif
#endif /* _ESP32_HAL_CPU_H_ */
+76
View File
@@ -0,0 +1,76 @@
/*
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp32-hal-dac.h"
#if SOC_DAC_SUPPORTED
#include "esp32-hal.h"
#include "esp32-hal-periman.h"
#include "soc/dac_channel.h"
#include "driver/dac_oneshot.h"
static bool dacDetachBus(void *bus) {
esp_err_t err = dac_oneshot_del_channel((dac_oneshot_handle_t)bus);
if (err != ESP_OK) {
log_e("dac_oneshot_del_channel failed with error: %d", err);
return false;
}
return true;
}
bool __dacWrite(uint8_t pin, uint8_t value) {
esp_err_t err = ESP_OK;
if (pin != DAC_CHAN0_GPIO_NUM && pin != DAC_CHAN1_GPIO_NUM) {
log_e("pin %u is not a DAC pin", pin);
return false; //not dac pin
}
dac_oneshot_handle_t bus = (dac_oneshot_handle_t)perimanGetPinBus(pin, ESP32_BUS_TYPE_DAC_ONESHOT);
if (bus == NULL) {
perimanSetBusDeinit(ESP32_BUS_TYPE_DAC_ONESHOT, dacDetachBus);
if (!perimanClearPinBus(pin)) {
return false;
}
dac_channel_t channel = (pin == DAC_CHAN0_GPIO_NUM) ? DAC_CHAN_0 : DAC_CHAN_1;
dac_oneshot_config_t config = {.chan_id = channel};
err = dac_oneshot_new_channel(&config, &bus);
if (err != ESP_OK) {
log_e("dac_oneshot_new_channel failed with error: %d", err);
return false;
}
if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_DAC_ONESHOT, (void *)bus, -1, channel)) {
dacDetachBus((void *)bus);
return false;
}
}
err = dac_oneshot_output_voltage(bus, value);
if (err != ESP_OK) {
log_e("dac_oneshot_output_voltage failed with error: %d", err);
return false;
}
return true;
}
bool __dacDisable(uint8_t pin) {
if (pin != DAC_CHAN0_GPIO_NUM && pin != DAC_CHAN1_GPIO_NUM) {
log_e("pin %u is not a DAC pin", pin);
return false; //not dac pin
}
void *bus = perimanGetPinBus(pin, ESP32_BUS_TYPE_DAC_ONESHOT);
if (bus != NULL) {
// will call dacDetachBus
return perimanClearPinBus(pin);
} else {
log_e("pin %u is not attached to DAC", pin);
}
return false;
}
extern bool dacWrite(uint8_t pin, uint8_t value) __attribute__((weak, alias("__dacWrite")));
extern bool dacDisable(uint8_t pin) __attribute__((weak, alias("__dacDisable")));
#endif
+26
View File
@@ -0,0 +1,26 @@
/*
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "soc/soc_caps.h"
#if SOC_DAC_SUPPORTED
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
bool dacWrite(uint8_t pin, uint8_t value);
bool dacDisable(uint8_t pin);
#ifdef __cplusplus
}
#endif
#endif /* SOC_DAC_SUPPORTED */
+284
View File
@@ -0,0 +1,284 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "esp32-hal-gpio.h"
#include "esp32-hal-periman.h"
#include "hal/gpio_hal.h"
#include "soc/soc_caps.h"
// RGB_BUILTIN is defined in pins_arduino.h
// If RGB_BUILTIN is defined, it will be used as a pin number for the RGB LED
// If RGB_BUILTIN has a side effect that prevents using RMT Legacy driver in IDF 5.1
// Define ESP32_ARDUINO_NO_RGB_BUILTIN in build_opt.h or through CLI to disable RGB_BUILTIN
#ifdef ESP32_ARDUINO_NO_RGB_BUILTIN
#ifdef RGB_BUILTIN
#undef RGB_BUILTIN
#endif
#endif
// It fixes lack of pin definition for S3 and for any future SoC
// this function works for ESP32, ESP32-S2 and ESP32-S3 - including the C3, it will return -1 for any pin
#if SOC_TOUCH_SENSOR_NUM > 0
#include "soc/touch_sensor_periph.h"
int8_t digitalPinToTouchChannel(uint8_t pin) {
if (pin < SOC_GPIO_PIN_COUNT) {
for (uint8_t i = 0; i < SOC_TOUCH_SENSOR_NUM; i++) {
if (touch_sensor_channel_io_map[i] == pin) {
return i;
}
}
}
log_e("No touch pad on selected pin(%u)!", pin);
return -1;
}
#else
// No Touch Sensor available
int8_t digitalPinToTouchChannel(uint8_t pin) {
log_e("Touch sensor not available on this chip");
return -1;
}
#endif
#ifdef SOC_ADC_SUPPORTED
#include "soc/adc_periph.h"
int8_t digitalPinToAnalogChannel(uint8_t pin) {
uint8_t channel = 0;
if (pin < SOC_GPIO_PIN_COUNT) {
for (uint8_t i = 0; i < SOC_ADC_PERIPH_NUM; i++) {
for (uint8_t j = 0; j < SOC_ADC_MAX_CHANNEL_NUM; j++) {
if (adc_channel_io_map[i][j] == pin) {
return channel;
}
channel++;
}
}
}
return -1;
}
int8_t analogChannelToDigitalPin(uint8_t channel) {
if (channel >= (SOC_ADC_PERIPH_NUM * SOC_ADC_MAX_CHANNEL_NUM)) {
return -1;
}
uint8_t adc_unit = (channel / SOC_ADC_MAX_CHANNEL_NUM);
uint8_t adc_chan = (channel % SOC_ADC_MAX_CHANNEL_NUM);
return adc_channel_io_map[adc_unit][adc_chan];
}
#else
// No Analog channels available
int8_t analogChannelToDigitalPin(uint8_t channel) {
return -1;
}
#endif
typedef void (*voidFuncPtr)(void);
typedef void (*voidFuncPtrArg)(void *);
typedef struct {
voidFuncPtr fn;
void *arg;
bool functional;
} InterruptHandle_t;
static InterruptHandle_t __pinInterruptHandlers[SOC_GPIO_PIN_COUNT] = {
0,
};
#include "driver/rtc_io.h"
static bool gpioDetachBus(void *bus) {
return true;
}
extern void ARDUINO_ISR_ATTR __pinMode(uint8_t pin, uint8_t mode) {
#ifdef RGB_BUILTIN
if (pin == RGB_BUILTIN) {
__pinMode(RGB_BUILTIN - SOC_GPIO_PIN_COUNT, mode);
return;
}
#endif
if (pin >= SOC_GPIO_PIN_COUNT) {
log_e("Invalid IO %i selected", pin);
return;
}
if (perimanGetPinBus(pin, ESP32_BUS_TYPE_GPIO) == NULL) {
perimanSetBusDeinit(ESP32_BUS_TYPE_GPIO, gpioDetachBus);
if (!perimanClearPinBus(pin)) {
log_e("Deinit of previous bus from IO %i failed", pin);
return;
}
}
gpio_hal_context_t gpiohal;
gpiohal.dev = GPIO_LL_GET_HW(GPIO_PORT_0);
gpio_config_t conf = {
.pin_bit_mask = (1ULL << pin), /*!< GPIO pin: set with bit mask, each bit maps to a GPIO */
.mode = GPIO_MODE_DISABLE, /*!< GPIO mode: set input/output mode */
.pull_up_en = GPIO_PULLUP_DISABLE, /*!< GPIO pull-up */
.pull_down_en = GPIO_PULLDOWN_DISABLE, /*!< GPIO pull-down */
#ifndef CONFIG_IDF_TARGET_ESP32C61
.intr_type = gpiohal.dev->pin[pin].int_type /*!< GPIO interrupt type - previously set */
#else
.intr_type = gpiohal.dev->pinn[pin].pinn_int_type /*!< GPIO interrupt type - previously set */
#endif
};
if (mode < 0x20) { //io
conf.mode = mode & (INPUT | OUTPUT);
if (mode & OPEN_DRAIN) {
conf.mode |= GPIO_MODE_DEF_OD;
}
if (mode & PULLUP) {
conf.pull_up_en = GPIO_PULLUP_ENABLE;
}
if (mode & PULLDOWN) {
conf.pull_down_en = GPIO_PULLDOWN_ENABLE;
}
}
if (gpio_config(&conf) != ESP_OK) {
log_e("IO %i config failed", pin);
return;
}
if (perimanGetPinBus(pin, ESP32_BUS_TYPE_GPIO) == NULL) {
if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_GPIO, (void *)(pin + 1), -1, -1)) {
//gpioDetachBus((void *)(pin+1));
return;
}
}
}
#ifdef RGB_BUILTIN
uint8_t RGB_BUILTIN_storage = 0;
#endif
extern void ARDUINO_ISR_ATTR __digitalWrite(uint8_t pin, uint8_t val) {
#ifdef RGB_BUILTIN
if (pin == RGB_BUILTIN) {
//use RMT to set all channels on/off
RGB_BUILTIN_storage = val;
const uint8_t comm_val = val != 0 ? RGB_BRIGHTNESS : 0;
rgbLedWrite(RGB_BUILTIN, comm_val, comm_val, comm_val);
return;
}
#endif // RGB_BUILTIN
if (perimanGetPinBus(pin, ESP32_BUS_TYPE_GPIO) != NULL) {
gpio_set_level((gpio_num_t)pin, val);
} else {
log_e("IO %i is not set as GPIO. Execute digitalMode(%i, OUTPUT) first.", pin, pin);
}
}
extern int ARDUINO_ISR_ATTR __digitalRead(uint8_t pin) {
#ifdef RGB_BUILTIN
if (pin == RGB_BUILTIN) {
return RGB_BUILTIN_storage;
}
#endif // RGB_BUILTIN
// This work when the pin is set as GPIO and in INPUT mode. For all other pin functions, it may return inconsistent response
if (perimanGetPinBus(pin, ESP32_BUS_TYPE_GPIO) == NULL) {
log_w("IO %i is not set as GPIO. digitalRead() may return an inconsistent value.", pin);
}
return gpio_get_level((gpio_num_t)pin);
}
static void ARDUINO_ISR_ATTR __onPinInterrupt(void *arg) {
InterruptHandle_t *isr = (InterruptHandle_t *)arg;
if (isr->fn) {
if (isr->arg) {
((voidFuncPtrArg)isr->fn)(isr->arg);
} else {
isr->fn();
}
}
}
extern void cleanupFunctional(void *arg);
extern void __attachInterruptFunctionalArg(uint8_t pin, voidFuncPtrArg userFunc, void *arg, int intr_type, bool functional) {
static bool interrupt_initialized = false;
// makes sure that pin -1 (255) will never work -- this follows Arduino standard
if (pin >= SOC_GPIO_PIN_COUNT) {
return;
}
if (!interrupt_initialized) {
esp_err_t err = gpio_install_isr_service((int)ARDUINO_ISR_FLAG);
interrupt_initialized = (err == ESP_OK) || (err == ESP_ERR_INVALID_STATE);
}
if (!interrupt_initialized) {
log_e("IO %i ISR Service Failed To Start", pin);
return;
}
// if new attach without detach remove old info
if (__pinInterruptHandlers[pin].functional && __pinInterruptHandlers[pin].arg) {
cleanupFunctional(__pinInterruptHandlers[pin].arg);
}
__pinInterruptHandlers[pin].fn = (voidFuncPtr)userFunc;
__pinInterruptHandlers[pin].arg = arg;
__pinInterruptHandlers[pin].functional = functional;
gpio_set_intr_type((gpio_num_t)pin, (gpio_int_type_t)(intr_type & 0x7));
if (intr_type & 0x8) {
gpio_wakeup_enable((gpio_num_t)pin, (gpio_int_type_t)(intr_type & 0x7));
}
gpio_isr_handler_add((gpio_num_t)pin, __onPinInterrupt, &__pinInterruptHandlers[pin]);
//FIX interrupts on peripherals outputs (eg. LEDC,...)
//Enable input in GPIO register
gpio_hal_context_t gpiohal;
gpiohal.dev = GPIO_LL_GET_HW(GPIO_PORT_0);
gpio_hal_input_enable(&gpiohal, pin);
}
extern void __attachInterruptArg(uint8_t pin, voidFuncPtrArg userFunc, void *arg, int intr_type) {
__attachInterruptFunctionalArg(pin, userFunc, arg, intr_type, false);
}
extern void __attachInterrupt(uint8_t pin, voidFuncPtr userFunc, int intr_type) {
__attachInterruptFunctionalArg(pin, (voidFuncPtrArg)userFunc, NULL, intr_type, false);
}
extern void __detachInterrupt(uint8_t pin) {
gpio_isr_handler_remove((gpio_num_t)pin); //remove handle and disable isr for pin
gpio_wakeup_disable((gpio_num_t)pin);
if (__pinInterruptHandlers[pin].functional && __pinInterruptHandlers[pin].arg) {
cleanupFunctional(__pinInterruptHandlers[pin].arg);
}
__pinInterruptHandlers[pin].fn = NULL;
__pinInterruptHandlers[pin].arg = NULL;
__pinInterruptHandlers[pin].functional = false;
gpio_set_intr_type((gpio_num_t)pin, GPIO_INTR_DISABLE);
}
extern void enableInterrupt(uint8_t pin) {
gpio_intr_enable((gpio_num_t)pin);
}
extern void disableInterrupt(uint8_t pin) {
gpio_intr_disable((gpio_num_t)pin);
}
extern void pinMode(uint8_t pin, uint8_t mode) __attribute__((weak, alias("__pinMode")));
extern void digitalWrite(uint8_t pin, uint8_t val) __attribute__((weak, alias("__digitalWrite")));
extern int digitalRead(uint8_t pin) __attribute__((weak, alias("__digitalRead")));
extern void attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode) __attribute__((weak, alias("__attachInterrupt")));
extern void attachInterruptArg(uint8_t pin, voidFuncPtrArg handler, void *arg, int mode) __attribute__((weak, alias("__attachInterruptArg")));
extern void detachInterrupt(uint8_t pin) __attribute__((weak, alias("__detachInterrupt")));
+92
View File
@@ -0,0 +1,92 @@
/*
Arduino.h - Main include file for the Arduino SDK
Copyright (c) 2005-2013 Arduino Team. All right 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
*/
#ifndef MAIN_ESP32_HAL_GPIO_H_
#define MAIN_ESP32_HAL_GPIO_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "pins_arduino.h"
#include "esp32-hal.h"
#include "soc/soc_caps.h"
#include "driver/gpio.h"
#if (CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3)
#define NUM_OUPUT_PINS 46
#define PIN_DAC1 17
#define PIN_DAC2 18
#else
#define NUM_OUPUT_PINS 34
#define PIN_DAC1 25
#define PIN_DAC2 26
#endif
#define LOW 0x0
#define HIGH 0x1
//GPIO FUNCTIONS
#define INPUT 0x01
// Changed OUTPUT from 0x02 to behave the same as Arduino pinMode(pin,OUTPUT)
// where you can read the state of pin even when it is set as OUTPUT
#define OUTPUT 0x03
#define PULLUP 0x04
#define INPUT_PULLUP 0x05
#define PULLDOWN 0x08
#define INPUT_PULLDOWN 0x09
#define OPEN_DRAIN 0x10
#define OUTPUT_OPEN_DRAIN 0x13
#define ANALOG 0xC0
//Interrupt Modes
#define DISABLED 0x00
#define RISING 0x01
#define FALLING 0x02
#define CHANGE 0x03
#define ONLOW 0x04
#define ONHIGH 0x05
#define ONLOW_WE 0x0C
#define ONHIGH_WE 0x0D
#define digitalPinIsValid(pin) GPIO_IS_VALID_GPIO(pin)
#define digitalPinCanOutput(pin) GPIO_IS_VALID_OUTPUT_GPIO(pin)
#define digitalPinToRtcPin(pin) ((RTC_GPIO_IS_VALID_GPIO(pin)) ? rtc_io_number_get(pin) : -1)
#define digitalPinToDacChannel(pin) (((pin) == DAC_CHANNEL_1_GPIO_NUM) ? 0 : ((pin) == DAC_CHANNEL_2_GPIO_NUM) ? 1 : -1)
void pinMode(uint8_t pin, uint8_t mode);
void digitalWrite(uint8_t pin, uint8_t val);
int digitalRead(uint8_t pin);
void attachInterrupt(uint8_t pin, void (*)(void), int mode);
void attachInterruptArg(uint8_t pin, void (*)(void *), void *arg, int mode);
void detachInterrupt(uint8_t pin);
void enableInterrupt(uint8_t pin);
void disableInterrupt(uint8_t pin);
int8_t digitalPinToTouchChannel(uint8_t pin);
int8_t digitalPinToAnalogChannel(uint8_t pin);
int8_t analogChannelToDigitalPin(uint8_t channel);
#ifdef __cplusplus
}
#endif
#endif /* MAIN_ESP32_HAL_GPIO_H_ */
+365
View File
@@ -0,0 +1,365 @@
// Copyright 2015-2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "sdkconfig.h"
#if defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) || defined(CONFIG_ESP_WIFI_REMOTE_ENABLED)
#include "esp32-hal-hosted.h"
#include "esp32-hal-log.h"
#include "esp32-hal.h"
#include "pins_arduino.h"
#include "esp_hosted.h"
#include "esp_hosted_transport_config.h"
// extern esp_err_t esp_hosted_init();
// extern esp_err_t esp_hosted_deinit();
static bool hosted_initialized = false;
static bool hosted_ble_active = false;
static bool hosted_wifi_active = false;
static sdio_pin_config_t sdio_pin_config = {
#ifdef BOARD_HAS_SDIO_ESP_HOSTED
.pin_clk = BOARD_SDIO_ESP_HOSTED_CLK,
.pin_cmd = BOARD_SDIO_ESP_HOSTED_CMD,
.pin_d0 = BOARD_SDIO_ESP_HOSTED_D0,
.pin_d1 = BOARD_SDIO_ESP_HOSTED_D1,
.pin_d2 = BOARD_SDIO_ESP_HOSTED_D2,
.pin_d3 = BOARD_SDIO_ESP_HOSTED_D3,
.pin_reset = BOARD_SDIO_ESP_HOSTED_RESET
#else
.pin_clk = CONFIG_ESP_SDIO_PIN_CLK,
.pin_cmd = CONFIG_ESP_SDIO_PIN_CMD,
.pin_d0 = CONFIG_ESP_SDIO_PIN_D0,
.pin_d1 = CONFIG_ESP_SDIO_PIN_D1,
.pin_d2 = CONFIG_ESP_SDIO_PIN_D2,
.pin_d3 = CONFIG_ESP_SDIO_PIN_D3,
.pin_reset = CONFIG_ESP_SDIO_GPIO_RESET_SLAVE
#endif
};
static esp_hosted_coprocessor_fwver_t slave_version_struct = {.major1 = 0, .minor1 = 0, .patch1 = 0};
static esp_hosted_coprocessor_fwver_t host_version_struct = {
.major1 = ESP_HOSTED_VERSION_MAJOR_1, .minor1 = ESP_HOSTED_VERSION_MINOR_1, .patch1 = ESP_HOSTED_VERSION_PATCH_1
};
static bool hostedInit();
static bool hostedDeinit();
void hostedGetHostVersion(uint32_t *major, uint32_t *minor, uint32_t *patch) {
*major = host_version_struct.major1;
*minor = host_version_struct.minor1;
*patch = host_version_struct.patch1;
}
void hostedGetSlaveVersion(uint32_t *major, uint32_t *minor, uint32_t *patch) {
*major = slave_version_struct.major1;
*minor = slave_version_struct.minor1;
*patch = slave_version_struct.patch1;
}
bool hostedHasUpdate() {
if (!hosted_initialized) {
log_e("ESP-Hosted is not initialized");
return false;
}
uint32_t host_version = ESP_HOSTED_VERSION_VAL(host_version_struct.major1, host_version_struct.minor1, host_version_struct.patch1);
uint32_t slave_version = 0;
esp_err_t ret = esp_hosted_get_coprocessor_fwversion(&slave_version_struct);
if (ret != ESP_OK) {
log_e("Could not get slave firmware version: %s", esp_err_to_name(ret));
} else {
slave_version = ESP_HOSTED_VERSION_VAL(slave_version_struct.major1, slave_version_struct.minor1, slave_version_struct.patch1);
}
log_i("Host firmware version: %" PRIu32 ".%" PRIu32 ".%" PRIu32, host_version_struct.major1, host_version_struct.minor1, host_version_struct.patch1);
log_i("Slave firmware version: %" PRIu32 ".%" PRIu32 ".%" PRIu32, slave_version_struct.major1, slave_version_struct.minor1, slave_version_struct.patch1);
// compare major.minor only
// slave_version &= 0xFFFFFF00;
// host_version &= 0xFFFFFF00;
if (host_version == slave_version) {
log_i("Versions Match!");
} else if (host_version > slave_version) {
log_w("Version on Host is NEWER than version on co-processor");
log_w("Update URL: %s", hostedGetUpdateURL());
return true;
} else {
log_w("Version on Host is OLDER than version on co-processor");
}
return false;
}
char *hostedGetUpdateURL() {
// https://espressif.github.io/arduino-esp32/hosted/esp32c6-v1.2.3.bin
static char url[92] = {0};
snprintf(
url, 92, "https://espressif.github.io/arduino-esp32/hosted/%s-v%" PRIu32 ".%" PRIu32 ".%" PRIu32 ".bin", CONFIG_ESP_HOSTED_IDF_SLAVE_TARGET,
host_version_struct.major1, host_version_struct.minor1, host_version_struct.patch1
);
return url;
}
bool hostedBeginUpdate() {
if (!hosted_initialized) {
log_e("ESP-Hosted is not initialized");
return false;
}
esp_err_t err = esp_hosted_slave_ota_begin();
if (err != ESP_OK) {
log_e("Failed to begin Update: %s", esp_err_to_name(err));
}
return err == ESP_OK;
}
bool hostedWriteUpdate(uint8_t *buf, uint32_t len) {
if (!hosted_initialized) {
log_e("ESP-Hosted is not initialized");
return false;
}
esp_err_t err = esp_hosted_slave_ota_write(buf, len);
if (err != ESP_OK) {
log_e("Failed to write Update: %s", esp_err_to_name(err));
}
return err == ESP_OK;
}
bool hostedEndUpdate() {
if (!hosted_initialized) {
log_e("ESP-Hosted is not initialized");
return false;
}
esp_err_t err = esp_hosted_slave_ota_end();
if (err != ESP_OK) {
log_e("Failed to end Update: %s", esp_err_to_name(err));
}
return err == ESP_OK;
}
bool hostedActivateUpdate() {
if (!hosted_initialized) {
log_e("ESP-Hosted is not initialized");
return false;
}
// Activate can fail on older firmwares and that is not critical
uint32_t slave_version = ESP_HOSTED_VERSION_VAL(slave_version_struct.major1, slave_version_struct.minor1, slave_version_struct.patch1);
uint32_t min_version = ESP_HOSTED_VERSION_VAL(2, 6, 0);
if (slave_version < min_version) {
// Silence messages caused by earlier versions
esp_log_level_set("rpc_core", ESP_LOG_NONE);
}
esp_err_t err = esp_hosted_slave_ota_activate();
// Any further communication will result in logged errors
esp_log_level_set("sdmmc_io", ESP_LOG_NONE);
esp_log_level_set("H_SDIO_DRV", ESP_LOG_NONE);
if (err != ESP_OK && slave_version >= min_version) {
log_e("Failed to activate Update: %s", esp_err_to_name(err));
return false;
}
return true;
}
static bool hostedInit() {
if (!hosted_initialized) {
log_i("Initializing ESP-Hosted");
log_d(
"SDIO pins: clk=%d, cmd=%d, d0=%d, d1=%d, d2=%d, d3=%d, rst=%d", sdio_pin_config.pin_clk, sdio_pin_config.pin_cmd, sdio_pin_config.pin_d0,
sdio_pin_config.pin_d1, sdio_pin_config.pin_d2, sdio_pin_config.pin_d3, sdio_pin_config.pin_reset
);
hosted_initialized = true;
struct esp_hosted_sdio_config conf = INIT_DEFAULT_HOST_SDIO_CONFIG();
conf.pin_clk.pin = sdio_pin_config.pin_clk;
conf.pin_cmd.pin = sdio_pin_config.pin_cmd;
conf.pin_d0.pin = sdio_pin_config.pin_d0;
conf.pin_d1.pin = sdio_pin_config.pin_d1;
conf.pin_d2.pin = sdio_pin_config.pin_d2;
conf.pin_d3.pin = sdio_pin_config.pin_d3;
conf.pin_reset.pin = sdio_pin_config.pin_reset;
esp_err_t err = esp_hosted_sdio_set_config(&conf);
if (err != ESP_OK) { //&& err != ESP_ERR_NOT_ALLOWED) { // uncomment when second init is fixed
log_e("esp_hosted_sdio_set_config failed: %s", esp_err_to_name(err));
return false;
}
err = esp_hosted_init();
if (err != ESP_OK) {
log_e("esp_hosted_init failed: %s", esp_err_to_name(err));
hosted_initialized = false;
return false;
}
log_i("ESP-Hosted initialized!");
err = esp_hosted_connect_to_slave();
if (err != ESP_OK) {
log_e("esp_hosted_connect_to_slave failed: %s", esp_err_to_name(err));
hosted_initialized = false;
return false;
}
hostedHasUpdate();
return true;
}
// Attach pins to PeriMan here
// Slave chip model is CONFIG_IDF_SLAVE_TARGET
// sdio_pin_config.pin_clk
// sdio_pin_config.pin_cmd
// sdio_pin_config.pin_d0
// sdio_pin_config.pin_d1
// sdio_pin_config.pin_d2
// sdio_pin_config.pin_d3
// sdio_pin_config.pin_reset
return true;
}
static bool hostedDeinit() {
if (!hosted_initialized) {
log_e("ESP-Hosted is not initialized");
return false;
}
if (esp_hosted_deinit() != ESP_OK) {
log_e("esp_hosted_deinit failed!");
return false;
}
hosted_initialized = false;
return true;
}
bool hostedInitBLE() {
log_i("Initializing ESP-Hosted for BLE");
if (!hostedInit()) {
return false;
}
uint32_t slave_version = ESP_HOSTED_VERSION_VAL(slave_version_struct.major1, slave_version_struct.minor1, slave_version_struct.patch1);
uint32_t min_version = ESP_HOSTED_VERSION_VAL(2, 6, 0);
if (slave_version >= min_version) {
esp_err_t err = esp_hosted_bt_controller_init();
if (err != ESP_OK) {
log_e("esp_hosted_bt_controller_init failed: %s", esp_err_to_name(err));
return false;
}
err = esp_hosted_bt_controller_enable();
if (err != ESP_OK) {
log_e("esp_hosted_bt_controller_enable failed: %s", esp_err_to_name(err));
return false;
}
}
hosted_ble_active = true;
return true;
}
bool hostedInitWiFi() {
log_i("Initializing ESP-Hosted for WiFi");
hosted_wifi_active = true;
return hostedInit();
}
bool hostedDeinitBLE() {
log_i("Deinitializing ESP-Hosted for BLE");
uint32_t slave_version = ESP_HOSTED_VERSION_VAL(slave_version_struct.major1, slave_version_struct.minor1, slave_version_struct.patch1);
uint32_t min_version = ESP_HOSTED_VERSION_VAL(2, 6, 0);
if (slave_version >= min_version) {
esp_err_t err = esp_hosted_bt_controller_disable();
if (err != ESP_OK) {
log_e("esp_hosted_bt_controller_disable failed: %s", esp_err_to_name(err));
return false;
}
err = esp_hosted_bt_controller_deinit(false);
if (err != ESP_OK) {
log_e("esp_hosted_bt_controller_deinit failed: %s", esp_err_to_name(err));
return false;
}
}
hosted_ble_active = false;
if (!hosted_wifi_active) {
return hostedDeinit();
} else {
log_i("ESP-Hosted is still being used by Wi-Fi. Skipping deinit.");
return true;
}
}
bool hostedDeinitWiFi() {
log_i("Deinitializing ESP-Hosted for WiFi");
hosted_wifi_active = false;
if (!hosted_ble_active) {
return hostedDeinit();
} else {
log_i("ESP-Hosted is still being used by BLE. Skipping deinit.");
return true;
}
}
bool hostedSetPins(int8_t clk, int8_t cmd, int8_t d0, int8_t d1, int8_t d2, int8_t d3, int8_t rst) {
if (clk < 0 || cmd < 0 || d0 < 0 || d1 < 0 || d2 < 0 || d3 < 0 || rst < 0) {
log_e("All SDIO pins must be defined");
return false;
}
if (hosted_initialized) {
int8_t current_clk, current_cmd, current_d0, current_d1, current_d2, current_d3, current_rst;
hostedGetPins(&current_clk, &current_cmd, &current_d0, &current_d1, &current_d2, &current_d3, &current_rst);
log_e("SDIO pins must be set before ESP-Hosted is initialized");
log_e(
"Current pins used: clk=%d, cmd=%d, d0=%d, d1=%d, d2=%d, d3=%d, rst=%d", current_clk, current_cmd, current_d0, current_d1, current_d2, current_d3,
current_rst
);
return false;
}
sdio_pin_config.pin_clk = clk;
sdio_pin_config.pin_cmd = cmd;
sdio_pin_config.pin_d0 = d0;
sdio_pin_config.pin_d1 = d1;
sdio_pin_config.pin_d2 = d2;
sdio_pin_config.pin_d3 = d3;
sdio_pin_config.pin_reset = rst;
return true;
}
void hostedGetPins(int8_t *clk, int8_t *cmd, int8_t *d0, int8_t *d1, int8_t *d2, int8_t *d3, int8_t *rst) {
*clk = sdio_pin_config.pin_clk;
*cmd = sdio_pin_config.pin_cmd;
*d0 = sdio_pin_config.pin_d0;
*d1 = sdio_pin_config.pin_d1;
*d2 = sdio_pin_config.pin_d2;
*d3 = sdio_pin_config.pin_d3;
*rst = sdio_pin_config.pin_reset;
}
bool hostedIsBLEActive() {
return hosted_ble_active;
}
bool hostedIsWiFiActive() {
return hosted_wifi_active;
}
bool hostedIsInitialized() {
return hosted_initialized;
}
#endif /* defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) || defined(CONFIG_ESP_WIFI_REMOTE_ENABLED) */
+61
View File
@@ -0,0 +1,61 @@
// Copyright 2015-2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef MAIN_ESP32_HAL_HOSTED_H_
#define MAIN_ESP32_HAL_HOSTED_H_
#include "sdkconfig.h"
#if defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) || defined(CONFIG_ESP_WIFI_REMOTE_ENABLED)
#include "stdint.h"
#include "stdbool.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
uint8_t pin_clk;
uint8_t pin_cmd;
uint8_t pin_d0;
uint8_t pin_d1;
uint8_t pin_d2;
uint8_t pin_d3;
uint8_t pin_reset;
} sdio_pin_config_t;
bool hostedInitBLE();
bool hostedInitWiFi();
bool hostedDeinitBLE();
bool hostedDeinitWiFi();
bool hostedIsInitialized();
bool hostedIsBLEActive();
bool hostedIsWiFiActive();
bool hostedSetPins(int8_t clk, int8_t cmd, int8_t d0, int8_t d1, int8_t d2, int8_t d3, int8_t rst);
void hostedGetPins(int8_t *clk, int8_t *cmd, int8_t *d0, int8_t *d1, int8_t *d2, int8_t *d3, int8_t *rst);
void hostedGetHostVersion(uint32_t *major, uint32_t *minor, uint32_t *patch);
void hostedGetSlaveVersion(uint32_t *major, uint32_t *minor, uint32_t *patch);
bool hostedHasUpdate();
char *hostedGetUpdateURL();
bool hostedBeginUpdate();
bool hostedWriteUpdate(uint8_t *buf, uint32_t len);
bool hostedEndUpdate();
bool hostedActivateUpdate();
#ifdef __cplusplus
}
#endif
#endif /* defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) || defined(CONFIG_ESP_WIFI_REMOTE_ENABLED) */
#endif /* MAIN_ESP32_HAL_HOSTED_H_ */
+455
View File
@@ -0,0 +1,455 @@
// Copyright 2015-2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "esp32-hal-i2c.h"
#if SOC_I2C_SUPPORTED
#include "esp_idf_version.h"
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)
#include "esp32-hal.h"
#if !CONFIG_DISABLE_HAL_LOCKS
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#endif
#include "esp_attr.h"
#include "esp_system.h"
#include "soc/soc_caps.h"
#include "driver/i2c_master.h"
#include "esp32-hal-periman.h"
typedef volatile struct {
bool initialized;
uint32_t frequency;
#if !CONFIG_DISABLE_HAL_LOCKS
SemaphoreHandle_t lock;
#endif
int8_t scl;
int8_t sda;
i2c_master_bus_handle_t bus_handle;
i2c_master_dev_handle_t dev_handles[128];
} i2c_bus_t;
static i2c_bus_t bus[SOC_I2C_NUM];
static bool i2cDetachBus(void *bus_i2c_num) {
uint8_t i2c_num = (int)bus_i2c_num - 1;
if (!bus[i2c_num].initialized) {
return true;
}
esp_err_t err = i2cDeinit(i2c_num);
if (err != ESP_OK) {
log_e("i2cDeinit failed with error: %d", err);
return false;
}
return true;
}
void *i2cBusHandle(uint8_t i2c_num) {
if (i2c_num >= SOC_I2C_NUM) {
return NULL;
}
return bus[i2c_num].bus_handle;
}
bool i2cIsInit(uint8_t i2c_num) {
if (i2c_num >= SOC_I2C_NUM) {
return false;
}
return bus[i2c_num].initialized;
}
esp_err_t i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t frequency) {
esp_err_t ret = ESP_OK;
if (i2c_num >= SOC_I2C_NUM) {
return ESP_ERR_INVALID_ARG;
}
#if !CONFIG_DISABLE_HAL_LOCKS
if (bus[i2c_num].lock == NULL) {
bus[i2c_num].lock = xSemaphoreCreateMutex();
if (bus[i2c_num].lock == NULL) {
log_e("xSemaphoreCreateMutex failed");
return ESP_ERR_NO_MEM;
}
}
//acquire lock
if (xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE) {
log_e("could not acquire lock");
return ESP_FAIL;
}
#endif
if (bus[i2c_num].initialized) {
log_e("bus is already initialized");
ret = ESP_FAIL;
goto init_fail;
}
if (!frequency) {
frequency = 100000UL;
} else if (frequency > 1000000UL) {
frequency = 1000000UL;
}
perimanSetBusDeinit(ESP32_BUS_TYPE_I2C_MASTER_SDA, i2cDetachBus);
perimanSetBusDeinit(ESP32_BUS_TYPE_I2C_MASTER_SCL, i2cDetachBus);
if (!perimanClearPinBus(sda) || !perimanClearPinBus(scl)) {
ret = ESP_FAIL;
goto init_fail;
}
log_i("Initializing I2C Master: num=%u sda=%d scl=%d freq=%lu", i2c_num, sda, scl, frequency);
i2c_master_bus_handle_t bus_handle = NULL;
i2c_master_bus_config_t bus_config;
memset(&bus_config, 0, sizeof(i2c_master_bus_config_t));
bus_config.i2c_port = (i2c_port_num_t)i2c_num;
bus_config.sda_io_num = (gpio_num_t)sda;
bus_config.scl_io_num = (gpio_num_t)scl;
#if SOC_LP_I2C_SUPPORTED
if (i2c_num >= SOC_HP_I2C_NUM) {
bus_config.lp_source_clk = LP_I2C_SCLK_DEFAULT;
} else
#endif
{
bus_config.clk_source = I2C_CLK_SRC_DEFAULT;
}
bus_config.glitch_ignore_cnt = 7;
bus_config.intr_priority = 0; // auto
bus_config.trans_queue_depth = 0; // only valid in asynchronous transaction, which Arduino does not use
bus_config.flags.enable_internal_pullup = 1;
#if SOC_I2C_SUPPORT_SLEEP_RETENTION
bus_config.flags.allow_pd = 1; // backup/restore the I2C registers before/after entering/exist sleep mode
#endif
ret = i2c_new_master_bus(&bus_config, &bus_handle);
if (ret != ESP_OK) {
log_e("i2c_new_master_bus failed: [%d] %s", ret, esp_err_to_name(ret));
} else {
bus[i2c_num].initialized = true;
bus[i2c_num].frequency = frequency;
bus[i2c_num].scl = scl;
bus[i2c_num].sda = sda;
bus[i2c_num].bus_handle = bus_handle;
for (uint8_t i = 0; i < 128; i++) {
bus[i2c_num].dev_handles[i] = NULL;
}
if (!perimanSetPinBus(sda, ESP32_BUS_TYPE_I2C_MASTER_SDA, (void *)(i2c_num + 1), i2c_num, -1)
|| !perimanSetPinBus(scl, ESP32_BUS_TYPE_I2C_MASTER_SCL, (void *)(i2c_num + 1), i2c_num, -1)) {
#if !CONFIG_DISABLE_HAL_LOCKS
//release lock so that i2cDetachBus can execute i2cDeinit
xSemaphoreGive(bus[i2c_num].lock);
#endif
i2cDetachBus((void *)(i2c_num + 1));
return ESP_FAIL;
}
}
// Silence messages coming from the IDF driver
esp_log_level_set("i2c.master", ESP_LOG_NONE);
init_fail:
#if !CONFIG_DISABLE_HAL_LOCKS
//release lock
xSemaphoreGive(bus[i2c_num].lock);
#endif
return ret;
}
esp_err_t i2cDeinit(uint8_t i2c_num) {
esp_err_t err = ESP_FAIL;
if (i2c_num >= SOC_I2C_NUM) {
return ESP_ERR_INVALID_ARG;
}
#if !CONFIG_DISABLE_HAL_LOCKS
//acquire lock
if (bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE) {
log_e("could not acquire lock");
return err;
}
#endif
if (!bus[i2c_num].initialized) {
log_e("bus is not initialized");
} else {
// remove devices from the bus
for (uint8_t i = 0; i < 128; i++) {
if (bus[i2c_num].dev_handles[i] != NULL) {
err = i2c_master_bus_rm_device(bus[i2c_num].dev_handles[i]);
bus[i2c_num].dev_handles[i] = NULL;
if (err != ESP_OK) {
log_e("i2c_master_bus_rm_device failed: [%d] %s", err, esp_err_to_name(err));
}
}
}
err = i2c_del_master_bus(bus[i2c_num].bus_handle);
if (err != ESP_OK) {
log_e("i2c_del_master_bus failed: [%d] %s", err, esp_err_to_name(err));
} else {
bus[i2c_num].initialized = false;
perimanClearPinBus(bus[i2c_num].scl);
perimanClearPinBus(bus[i2c_num].sda);
bus[i2c_num].scl = -1;
bus[i2c_num].sda = -1;
bus[i2c_num].bus_handle = NULL;
}
}
#if !CONFIG_DISABLE_HAL_LOCKS
//release lock
xSemaphoreGive(bus[i2c_num].lock);
#endif
return err;
}
static esp_err_t i2cAddDeviceIfNeeded(uint8_t i2c_num, uint16_t address) {
esp_err_t ret = ESP_OK;
if (bus[i2c_num].dev_handles[address] == NULL) {
i2c_master_dev_handle_t dev_handle = NULL;
i2c_device_config_t dev_config;
memset(&dev_config, 0, sizeof(i2c_device_config_t));
dev_config.dev_addr_length = I2C_ADDR_BIT_LEN_7; // Arduino supports only 7bit addresses
dev_config.device_address = address;
dev_config.scl_speed_hz = bus[i2c_num].frequency;
dev_config.scl_wait_us = 0;
dev_config.flags.disable_ack_check = 0;
ret = i2c_master_bus_add_device(bus[i2c_num].bus_handle, &dev_config, &dev_handle);
if (ret != ESP_OK) {
log_e("i2c_master_bus_add_device failed: [%d] %s", ret, esp_err_to_name(ret));
} else {
bus[i2c_num].dev_handles[address] = dev_handle;
log_v("added device: bus=%u addr=0x%x handle=0x%08x", i2c_num, address, dev_handle);
}
}
return ret;
}
esp_err_t i2cWrite(uint8_t i2c_num, uint16_t address, const uint8_t *buff, size_t size, uint32_t timeOutMillis) {
esp_err_t ret = ESP_FAIL;
// i2c_cmd_handle_t cmd = NULL;
if (i2c_num >= SOC_I2C_NUM) {
return ESP_ERR_INVALID_ARG;
}
if (address >= 128) {
log_e("Only 7bit I2C addresses are supported");
return ESP_ERR_INVALID_ARG;
}
#if !CONFIG_DISABLE_HAL_LOCKS
//acquire lock
if (bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE) {
log_e("could not acquire lock");
return ret;
}
#endif
if (!bus[i2c_num].initialized) {
log_e("bus is not initialized");
goto end;
}
if (size == 0) {
// Probe device
ret = i2c_master_probe(bus[i2c_num].bus_handle, address, timeOutMillis);
if (ret != ESP_OK) {
log_v("i2c_master_probe failed: [%d] %s", ret, esp_err_to_name(ret));
}
} else {
// writing data to device
ret = i2cAddDeviceIfNeeded(i2c_num, address);
if (ret != ESP_OK) {
goto end;
}
log_v("i2c_master_transmit: bus=%u addr=0x%x handle=0x%08x size=%u", i2c_num, address, bus[i2c_num].dev_handles[address], size);
ret = i2c_master_transmit(bus[i2c_num].dev_handles[address], buff, size, timeOutMillis);
if (ret != ESP_OK) {
log_e("i2c_master_transmit failed: [%d] %s", ret, esp_err_to_name(ret));
goto end;
}
// wait for transactions to finish (is it needed with sync transactions?)
// ret = i2c_master_bus_wait_all_done(bus[i2c_num].bus_handle, timeOutMillis);
// if (ret != ESP_OK) {
// log_e("i2c_master_bus_wait_all_done failed: [%d] %s", ret, esp_err_to_name(ret));
// goto end;
// }
}
end:
#if !CONFIG_DISABLE_HAL_LOCKS
//release lock
xSemaphoreGive(bus[i2c_num].lock);
#endif
return ret;
}
esp_err_t i2cRead(uint8_t i2c_num, uint16_t address, uint8_t *buff, size_t size, uint32_t timeOutMillis, size_t *readCount) {
esp_err_t ret = ESP_FAIL;
*readCount = 0;
if (i2c_num >= SOC_I2C_NUM) {
return ESP_ERR_INVALID_ARG;
}
#if !CONFIG_DISABLE_HAL_LOCKS
//acquire lock
if (bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE) {
log_e("could not acquire lock");
return ret;
}
#endif
if (!bus[i2c_num].initialized) {
log_e("bus is not initialized");
goto end;
}
ret = i2cAddDeviceIfNeeded(i2c_num, address);
if (ret != ESP_OK) {
goto end;
}
log_v("i2c_master_receive: bus=%u addr=0x%x handle=0x%08x size=%u", i2c_num, address, bus[i2c_num].dev_handles[address], size);
ret = i2c_master_receive(bus[i2c_num].dev_handles[address], buff, size, timeOutMillis);
if (ret != ESP_OK) {
log_e("i2c_master_receive failed: [%d] %s", ret, esp_err_to_name(ret));
goto end;
}
// wait for transactions to finish (is it needed with sync transactions?)
// ret = i2c_master_bus_wait_all_done(bus[i2c_num].bus_handle, timeOutMillis);
// if (ret != ESP_OK) {
// log_e("i2c_master_bus_wait_all_done failed: [%d] %s", ret, esp_err_to_name(ret));
// goto end;
// }
*readCount = size;
end:
#if !CONFIG_DISABLE_HAL_LOCKS
//release lock
xSemaphoreGive(bus[i2c_num].lock);
#endif
return ret;
}
esp_err_t i2cWriteReadNonStop(
uint8_t i2c_num, uint16_t address, const uint8_t *wbuff, size_t wsize, uint8_t *rbuff, size_t rsize, uint32_t timeOutMillis, size_t *readCount
) {
esp_err_t ret = ESP_FAIL;
*readCount = 0;
if (i2c_num >= SOC_I2C_NUM) {
return ESP_ERR_INVALID_ARG;
}
#if !CONFIG_DISABLE_HAL_LOCKS
//acquire lock
if (bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE) {
log_e("could not acquire lock");
return ret;
}
#endif
if (!bus[i2c_num].initialized) {
log_e("bus is not initialized");
goto end;
}
ret = i2cAddDeviceIfNeeded(i2c_num, address);
if (ret != ESP_OK) {
goto end;
}
log_v("i2c_master_transmit_receive: bus=%u addr=0x%x handle=0x%08x write=%u read=%u", i2c_num, address, bus[i2c_num].dev_handles[address], wsize, rsize);
ret = i2c_master_transmit_receive(bus[i2c_num].dev_handles[address], wbuff, wsize, rbuff, rsize, timeOutMillis);
if (ret != ESP_OK) {
log_e("i2c_master_transmit_receive failed: [%d] %s", ret, esp_err_to_name(ret));
goto end;
}
// wait for transactions to finish (is it needed with sync transactions?)
// ret = i2c_master_bus_wait_all_done(bus[i2c_num].bus_handle, timeOutMillis);
// if (ret != ESP_OK) {
// log_e("i2c_master_bus_wait_all_done failed: [%d] %s", ret, esp_err_to_name(ret));
// goto end;
// }
*readCount = rsize;
end:
#if !CONFIG_DISABLE_HAL_LOCKS
//release lock
xSemaphoreGive(bus[i2c_num].lock);
#endif
return ret;
}
esp_err_t i2cSetClock(uint8_t i2c_num, uint32_t frequency) {
esp_err_t ret = ESP_FAIL;
if (i2c_num >= SOC_I2C_NUM) {
return ESP_ERR_INVALID_ARG;
}
#if !CONFIG_DISABLE_HAL_LOCKS
//acquire lock
if (bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE) {
log_e("could not acquire lock");
return ret;
}
#endif
if (!bus[i2c_num].initialized) {
log_e("bus is not initialized");
goto end;
}
if (bus[i2c_num].frequency == frequency) {
ret = ESP_OK;
goto end;
}
if (!frequency) {
frequency = 100000UL;
} else if (frequency > 1000000UL) {
frequency = 1000000UL;
}
bus[i2c_num].frequency = frequency;
// loop through devices, remove them and then re-add them with the new frequency
for (uint8_t i = 0; i < 128; i++) {
if (bus[i2c_num].dev_handles[i] != NULL) {
ret = i2c_master_bus_rm_device(bus[i2c_num].dev_handles[i]);
if (ret != ESP_OK) {
log_e("i2c_master_bus_rm_device failed: [%d] %s", ret, esp_err_to_name(ret));
goto end;
} else {
bus[i2c_num].dev_handles[i] = NULL;
ret = i2cAddDeviceIfNeeded(i2c_num, i);
if (ret != ESP_OK) {
goto end;
}
}
}
}
end:
#if !CONFIG_DISABLE_HAL_LOCKS
//release lock
xSemaphoreGive(bus[i2c_num].lock);
#endif
return ret;
}
esp_err_t i2cGetClock(uint8_t i2c_num, uint32_t *frequency) {
if (i2c_num >= SOC_I2C_NUM) {
return ESP_ERR_INVALID_ARG;
}
if (!bus[i2c_num].initialized) {
log_e("bus is not initialized");
return ESP_FAIL;
}
*frequency = bus[i2c_num].frequency;
return ESP_OK;
}
#endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) */
#endif /* SOC_I2C_SUPPORTED */
+925
View File
@@ -0,0 +1,925 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "soc/soc_caps.h"
#if SOC_I2C_SUPPORT_SLAVE
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <inttypes.h>
#include <string.h>
#include <math.h>
#include "sdkconfig.h"
#include "esp_attr.h"
#include "rom/gpio.h"
#include "soc/gpio_sig_map.h"
#include "hal/gpio_types.h"
#include "driver/gpio.h"
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/ringbuf.h"
#include "esp_intr_alloc.h"
#include "soc/i2c_reg.h"
#include "soc/i2c_struct.h"
#include "soc/periph_defs.h"
#include "hal/i2c_ll.h"
#include "hal/i2c_types.h"
#if !defined(CONFIG_IDF_TARGET_ESP32C5) && !defined(CONFIG_IDF_TARGET_ESP32C61)
#include "hal/clk_gate_ll.h"
#endif
#include "esp32-hal-log.h"
#include "esp32-hal-i2c-slave.h"
#include "esp32-hal-periman.h"
#include "esp_private/periph_ctrl.h"
#if SOC_PERIPH_CLK_CTRL_SHARED
#define I2C_CLOCK_SRC_ATOMIC() PERIPH_RCC_ATOMIC()
#else
#define I2C_CLOCK_SRC_ATOMIC()
#endif
#if !SOC_RCC_IS_INDEPENDENT
#define I2C_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
#else
#define I2C_RCC_ATOMIC()
#endif
#define I2C_SLAVE_USE_RX_QUEUE 0 // 1: Queue, 0: RingBuffer
#ifdef CONFIG_IDF_TARGET_ESP32P4
#define I2C_SCL_IDX(p) ((p == 0) ? I2C0_SCL_PAD_OUT_IDX : ((p == 1) ? I2C1_SCL_PAD_OUT_IDX : 0))
#define I2C_SDA_IDX(p) ((p == 0) ? I2C0_SDA_PAD_OUT_IDX : ((p == 1) ? I2C1_SDA_PAD_OUT_IDX : 0))
#else
#if SOC_HP_I2C_NUM > 1
#define I2C_SCL_IDX(p) ((p == 0) ? I2CEXT0_SCL_OUT_IDX : ((p == 1) ? I2CEXT1_SCL_OUT_IDX : 0))
#define I2C_SDA_IDX(p) ((p == 0) ? I2CEXT0_SDA_OUT_IDX : ((p == 1) ? I2CEXT1_SDA_OUT_IDX : 0))
#else
#define I2C_SCL_IDX(p) I2CEXT0_SCL_OUT_IDX
#define I2C_SDA_IDX(p) I2CEXT0_SDA_OUT_IDX
#endif
#endif // ifdef CONFIG_IDF_TARGET_ESP32P4
#if CONFIG_IDF_TARGET_ESP32
#define I2C_TXFIFO_WM_INT_ENA I2C_TXFIFO_EMPTY_INT_ENA
#define I2C_RXFIFO_WM_INT_ENA I2C_RXFIFO_FULL_INT_ENA
#endif
enum {
I2C_SLAVE_EVT_RX,
I2C_SLAVE_EVT_TX
};
typedef struct i2c_slave_struct_t {
i2c_dev_t *dev;
uint8_t num;
int8_t sda;
int8_t scl;
i2c_slave_request_cb_t request_callback;
i2c_slave_receive_cb_t receive_callback;
void *arg;
intr_handle_t intr_handle;
TaskHandle_t task_handle;
QueueHandle_t event_queue;
#if I2C_SLAVE_USE_RX_QUEUE
QueueHandle_t rx_queue;
#else
RingbufHandle_t rx_ring_buf;
#endif
QueueHandle_t tx_queue;
uint32_t rx_data_count;
#if !CONFIG_DISABLE_HAL_LOCKS
SemaphoreHandle_t lock;
#endif
} i2c_slave_struct_t;
typedef union {
struct {
uint32_t event : 2;
uint32_t stop : 1;
uint32_t param : 29;
};
uint32_t val;
} i2c_slave_queue_event_t;
static i2c_slave_struct_t _i2c_bus_array[SOC_HP_I2C_NUM] = {
{&I2C0, 0, -1, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0
#if !CONFIG_DISABLE_HAL_LOCKS
,
NULL
#endif
},
#if SOC_HP_I2C_NUM > 1
{&I2C1, 1, -1, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0
#if !CONFIG_DISABLE_HAL_LOCKS
,
NULL
#endif
}
#endif
};
#if CONFIG_DISABLE_HAL_LOCKS
#define I2C_SLAVE_MUTEX_LOCK()
#define I2C_SLAVE_MUTEX_UNLOCK()
#else
#define I2C_SLAVE_MUTEX_LOCK() \
if (i2c->lock) { \
xSemaphoreTake(i2c->lock, portMAX_DELAY); \
}
#define I2C_SLAVE_MUTEX_UNLOCK() \
if (i2c->lock) { \
xSemaphoreGive(i2c->lock); \
}
#endif
//-------------------------------------- HAL_LL (Missing Functions) ------------------------------------------------
typedef enum {
I2C_STRETCH_CAUSE_MASTER_READ,
I2C_STRETCH_CAUSE_TX_FIFO_EMPTY,
I2C_STRETCH_CAUSE_RX_FIFO_FULL,
I2C_STRETCH_CAUSE_MAX
} i2c_stretch_cause_t;
static inline i2c_stretch_cause_t i2c_ll_stretch_cause(i2c_dev_t *hw) {
#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
return hw->sr.stretch_cause;
#elif CONFIG_IDF_TARGET_ESP32S2
return hw->status_reg.stretch_cause;
#else
return I2C_STRETCH_CAUSE_MAX;
#endif
}
static inline void i2c_ll_set_stretch(i2c_dev_t *hw, uint16_t time) {
#ifndef CONFIG_IDF_TARGET_ESP32
typeof(hw->scl_stretch_conf) scl_stretch_conf;
scl_stretch_conf.val = 0;
scl_stretch_conf.slave_scl_stretch_en = (time > 0);
scl_stretch_conf.stretch_protect_num = time;
scl_stretch_conf.slave_scl_stretch_clr = 1;
hw->scl_stretch_conf.val = scl_stretch_conf.val;
if (time > 0) {
//enable interrupt
hw->int_ena.val |= I2C_SLAVE_STRETCH_INT_ENA;
} else {
//disable interrupt
hw->int_ena.val &= (~I2C_SLAVE_STRETCH_INT_ENA);
}
#endif
}
static inline void i2c_ll_stretch_clr(i2c_dev_t *hw) {
#ifndef CONFIG_IDF_TARGET_ESP32
hw->scl_stretch_conf.slave_scl_stretch_clr = 1;
#endif
}
static inline bool i2c_ll_slave_addressed(i2c_dev_t *hw) {
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
return hw->status_reg.slave_addressed;
#else
return hw->sr.slave_addressed;
#endif
}
static inline bool i2c_ll_slave_rw(i2c_dev_t *hw) //not exposed by hal_ll
{
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
return hw->status_reg.slave_rw;
#else
return hw->sr.slave_rw;
#endif
}
//-------------------------------------- PRIVATE (Function Prototypes) ------------------------------------------------
static void i2c_slave_free_resources(i2c_slave_struct_t *i2c);
static void i2c_slave_delay_us(uint64_t us);
static void i2c_slave_gpio_mode(int8_t pin, gpio_mode_t mode);
static bool i2c_slave_check_line_state(int8_t sda, int8_t scl);
static bool i2c_slave_attach_gpio(i2c_slave_struct_t *i2c, int8_t sda, int8_t scl);
static bool i2c_slave_detach_gpio(i2c_slave_struct_t *i2c);
static bool i2c_slave_set_frequency(i2c_slave_struct_t *i2c, uint32_t clk_speed);
static bool i2c_slave_send_event(i2c_slave_struct_t *i2c, i2c_slave_queue_event_t *event);
static bool i2c_slave_handle_tx_fifo_empty(i2c_slave_struct_t *i2c);
static bool i2c_slave_handle_rx_fifo_full(i2c_slave_struct_t *i2c, uint32_t len);
static size_t i2c_slave_read_rx(i2c_slave_struct_t *i2c, uint8_t *data, size_t len);
static void i2c_slave_isr_handler(void *arg);
static void i2c_slave_task(void *pv_args);
static bool i2cSlaveDetachBus(void *bus_i2c_num);
//=====================================================================================================================
//-------------------------------------- Public Functions -------------------------------------------------------------
//=====================================================================================================================
esp_err_t i2cSlaveAttachCallbacks(uint8_t num, i2c_slave_request_cb_t request_callback, i2c_slave_receive_cb_t receive_callback, void *arg) {
if (num >= SOC_HP_I2C_NUM) {
log_e("Invalid port num: %u", num);
return ESP_ERR_INVALID_ARG;
}
i2c_slave_struct_t *i2c = &_i2c_bus_array[num];
I2C_SLAVE_MUTEX_LOCK();
i2c->request_callback = request_callback;
i2c->receive_callback = receive_callback;
i2c->arg = arg;
I2C_SLAVE_MUTEX_UNLOCK();
return ESP_OK;
}
esp_err_t i2cSlaveInit(uint8_t num, int sda, int scl, uint16_t slaveID, uint32_t frequency, size_t rx_len, size_t tx_len) {
if (num >= SOC_HP_I2C_NUM) {
log_e("Invalid port num: %u", num);
return ESP_ERR_INVALID_ARG;
}
if (sda < 0 || scl < 0) {
log_e("invalid pins sda=%d, scl=%d", sda, scl);
return ESP_ERR_INVALID_ARG;
}
if (!frequency) {
frequency = 100000;
} else if (frequency > 1000000) {
frequency = 1000000;
}
perimanSetBusDeinit(ESP32_BUS_TYPE_I2C_SLAVE_SDA, i2cSlaveDetachBus);
perimanSetBusDeinit(ESP32_BUS_TYPE_I2C_SLAVE_SCL, i2cSlaveDetachBus);
if (!perimanClearPinBus(sda) || !perimanClearPinBus(scl)) {
return false;
}
log_i("Initializing I2C Slave: sda=%d scl=%d freq=%d, addr=0x%x", sda, scl, frequency, slaveID);
i2c_slave_struct_t *i2c = &_i2c_bus_array[num];
esp_err_t ret = ESP_OK;
#if !CONFIG_DISABLE_HAL_LOCKS
if (!i2c->lock) {
i2c->lock = xSemaphoreCreateMutex();
if (i2c->lock == NULL) {
log_e("RX queue create failed");
return ESP_ERR_NO_MEM;
}
}
#endif
I2C_SLAVE_MUTEX_LOCK();
i2c_slave_free_resources(i2c);
#if I2C_SLAVE_USE_RX_QUEUE
i2c->rx_queue = xQueueCreate(rx_len, sizeof(uint8_t));
if (i2c->rx_queue == NULL) {
log_e("RX queue create failed");
ret = ESP_ERR_NO_MEM;
goto fail;
}
#else
i2c->rx_ring_buf = xRingbufferCreate(rx_len, RINGBUF_TYPE_BYTEBUF);
if (i2c->rx_ring_buf == NULL) {
log_e("RX RingBuf create failed");
ret = ESP_ERR_NO_MEM;
goto fail;
}
#endif
i2c->tx_queue = xQueueCreate(tx_len, sizeof(uint8_t));
if (i2c->tx_queue == NULL) {
log_e("TX queue create failed");
ret = ESP_ERR_NO_MEM;
goto fail;
}
i2c->event_queue = xQueueCreate(16, sizeof(i2c_slave_queue_event_t));
if (i2c->event_queue == NULL) {
log_e("Event queue create failed");
ret = ESP_ERR_NO_MEM;
goto fail;
}
xTaskCreate(i2c_slave_task, "i2c_slave_task", 4096, i2c, 20, &i2c->task_handle);
if (i2c->task_handle == NULL) {
log_e("Event thread create failed");
ret = ESP_ERR_NO_MEM;
goto fail;
}
if (frequency == 0) {
frequency = 100000L;
}
frequency = (frequency * 5) / 4;
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 \
|| CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
if (i2c->num == 0) {
periph_ll_enable_clk_clear_rst(PERIPH_I2C0_MODULE);
#if SOC_HP_I2C_NUM > 1
} else {
periph_ll_enable_clk_clear_rst(PERIPH_I2C1_MODULE);
#endif
}
#endif // !defined(CONFIG_IDF_TARGET_ESP32P4)
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)) || (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2) && ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0)) \
|| (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 3) && ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 0))
i2c_ll_set_mode(i2c->dev, I2C_BUS_MODE_SLAVE);
i2c_ll_enable_pins_open_drain(i2c->dev, true);
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2)
i2c_ll_enable_fifo_mode(i2c->dev, true);
#else
i2c_ll_slave_set_fifo_mode(i2c->dev, true);
#endif
#else
i2c_ll_slave_init(i2c->dev);
i2c_ll_slave_set_fifo_mode(i2c->dev, true);
#endif
i2c_ll_set_slave_addr(i2c->dev, slaveID, false);
i2c_ll_set_tout(i2c->dev, I2C_LL_MAX_TIMEOUT);
i2c_slave_set_frequency(i2c, frequency);
if (!i2c_slave_check_line_state(sda, scl)) {
log_e("bad pin state");
ret = ESP_FAIL;
goto fail;
}
i2c_slave_attach_gpio(i2c, sda, scl);
if (i2c_ll_is_bus_busy(i2c->dev)) {
log_w("Bus busy, reinit");
ret = ESP_FAIL;
goto fail;
}
i2c_ll_disable_intr_mask(i2c->dev, I2C_LL_INTR_MASK);
i2c_ll_clear_intr_mask(i2c->dev, I2C_LL_INTR_MASK);
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2)
i2c_ll_enable_fifo_mode(i2c->dev, true);
#else
i2c_ll_slave_set_fifo_mode(i2c->dev, true);
#endif
if (!i2c->intr_handle) {
uint32_t flags = ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED;
if (i2c->num == 0) {
#if !defined(CONFIG_IDF_TARGET_ESP32P4)
ret = esp_intr_alloc(ETS_I2C_EXT0_INTR_SOURCE, flags, &i2c_slave_isr_handler, i2c, &i2c->intr_handle);
#else
ret = esp_intr_alloc(ETS_I2C0_INTR_SOURCE, flags, &i2c_slave_isr_handler, i2c, &i2c->intr_handle);
#endif
#if SOC_HP_I2C_NUM > 1
} else {
#if !defined(CONFIG_IDF_TARGET_ESP32P4)
ret = esp_intr_alloc(ETS_I2C_EXT1_INTR_SOURCE, flags, &i2c_slave_isr_handler, i2c, &i2c->intr_handle);
#else
ret = esp_intr_alloc(ETS_I2C1_INTR_SOURCE, flags, &i2c_slave_isr_handler, i2c, &i2c->intr_handle);
#endif
#endif
}
if (ret != ESP_OK) {
log_e("install interrupt handler Failed=%d", ret);
goto fail;
}
}
i2c_ll_txfifo_rst(i2c->dev);
i2c_ll_rxfifo_rst(i2c->dev);
i2c_ll_slave_enable_rx_it(i2c->dev);
i2c_ll_set_stretch(i2c->dev, 0x3FF);
i2c_ll_update(i2c->dev);
if (!perimanSetPinBus(sda, ESP32_BUS_TYPE_I2C_SLAVE_SDA, (void *)(i2c->num + 1), i2c->num, -1)
|| !perimanSetPinBus(scl, ESP32_BUS_TYPE_I2C_SLAVE_SCL, (void *)(i2c->num + 1), i2c->num, -1)) {
i2cSlaveDetachBus((void *)(i2c->num + 1));
ret = ESP_FAIL;
}
I2C_SLAVE_MUTEX_UNLOCK();
return ret;
fail:
i2c_slave_free_resources(i2c);
I2C_SLAVE_MUTEX_UNLOCK();
return ret;
}
esp_err_t i2cSlaveDeinit(uint8_t num) {
if (num >= SOC_HP_I2C_NUM) {
log_e("Invalid port num: %u", num);
return ESP_ERR_INVALID_ARG;
}
i2c_slave_struct_t *i2c = &_i2c_bus_array[num];
#if !CONFIG_DISABLE_HAL_LOCKS
if (!i2c->lock) {
log_e("Lock is not initialized! Did you call i2c_slave_init()?");
return ESP_ERR_NO_MEM;
}
#endif
I2C_SLAVE_MUTEX_LOCK();
int scl = i2c->scl;
int sda = i2c->sda;
i2c_slave_free_resources(i2c);
perimanClearPinBus(scl);
perimanClearPinBus(sda);
I2C_SLAVE_MUTEX_UNLOCK();
return ESP_OK;
}
size_t i2cSlaveWrite(uint8_t num, const uint8_t *buf, uint32_t len, uint32_t timeout_ms) {
if (num >= SOC_HP_I2C_NUM) {
log_e("Invalid port num: %u", num);
return 0;
}
uint32_t to_queue = 0, to_fifo = 0;
i2c_slave_struct_t *i2c = &_i2c_bus_array[num];
#if !CONFIG_DISABLE_HAL_LOCKS
if (!i2c->lock) {
log_e("Lock is not initialized! Did you call i2c_slave_init()?");
return ESP_ERR_NO_MEM;
}
#endif
if (!i2c->tx_queue) {
return 0;
}
I2C_SLAVE_MUTEX_LOCK();
#if CONFIG_IDF_TARGET_ESP32
i2c_ll_slave_disable_tx_it(i2c->dev);
uint32_t txfifo_len = 0;
i2c_ll_get_txfifo_len(i2c->dev, &txfifo_len);
if (txfifo_len < SOC_I2C_FIFO_LEN) {
i2c_ll_txfifo_rst(i2c->dev);
}
#endif
i2c_ll_get_txfifo_len(i2c->dev, &to_fifo);
if (to_fifo) {
if (len < to_fifo) {
to_fifo = len;
}
i2c_ll_write_txfifo(i2c->dev, (uint8_t *)buf, to_fifo);
buf += to_fifo;
len -= to_fifo;
//reset tx_queue
xQueueReset(i2c->tx_queue);
//write the rest of the bytes to the queue
if (len) {
to_queue = uxQueueSpacesAvailable(i2c->tx_queue);
if (len < to_queue) {
to_queue = len;
}
for (size_t i = 0; i < to_queue; i++) {
if (xQueueSend(i2c->tx_queue, &buf[i], timeout_ms / portTICK_PERIOD_MS) != pdTRUE) {
xQueueReset(i2c->tx_queue);
to_queue = 0;
break;
}
}
//no need to enable TX_EMPTY if tx_queue is empty
if (to_queue) {
i2c_ll_slave_enable_tx_it(i2c->dev);
}
}
}
I2C_SLAVE_MUTEX_UNLOCK();
return to_queue + to_fifo;
}
//=====================================================================================================================
//-------------------------------------- Private Functions ------------------------------------------------------------
//=====================================================================================================================
static void i2c_slave_free_resources(i2c_slave_struct_t *i2c) {
i2c_slave_detach_gpio(i2c);
i2c_ll_set_slave_addr(i2c->dev, 0, false);
i2c_ll_disable_intr_mask(i2c->dev, I2C_LL_INTR_MASK);
i2c_ll_clear_intr_mask(i2c->dev, I2C_LL_INTR_MASK);
if (i2c->intr_handle) {
esp_intr_free(i2c->intr_handle);
i2c->intr_handle = NULL;
}
if (i2c->task_handle) {
vTaskDelete(i2c->task_handle);
i2c->task_handle = NULL;
}
#if I2C_SLAVE_USE_RX_QUEUE
if (i2c->rx_queue) {
vQueueDelete(i2c->rx_queue);
i2c->rx_queue = NULL;
}
#else
if (i2c->rx_ring_buf) {
vRingbufferDelete(i2c->rx_ring_buf);
i2c->rx_ring_buf = NULL;
}
#endif
if (i2c->tx_queue) {
vQueueDelete(i2c->tx_queue);
i2c->tx_queue = NULL;
}
if (i2c->event_queue) {
vQueueDelete(i2c->event_queue);
i2c->event_queue = NULL;
}
i2c->rx_data_count = 0;
}
static bool i2c_slave_set_frequency(i2c_slave_struct_t *i2c, uint32_t clk_speed) {
if (i2c == NULL) {
log_e("no control buffer");
return false;
}
if (clk_speed > 1100000UL) {
clk_speed = 1100000UL;
}
// Adjust Fifo thresholds based on frequency
uint32_t a = (clk_speed / 50000L) + 2;
log_d("Fifo thresholds: rx_fifo_full = %d, tx_fifo_empty = %d", SOC_I2C_FIFO_LEN - a, a);
i2c_hal_clk_config_t clk_cal;
#if SOC_I2C_SUPPORT_APB
i2c_ll_master_cal_bus_clk(APB_CLK_FREQ, clk_speed, &clk_cal);
I2C_CLOCK_SRC_ATOMIC() {
i2c_ll_set_source_clk(i2c->dev, SOC_MOD_CLK_APB); /*!< I2C source clock from APB, 80M*/
}
#elif SOC_I2C_SUPPORT_XTAL
#ifndef XTAL_CLK_FREQ
#define XTAL_CLK_FREQ APB_CLK_FREQ
#endif
i2c_ll_master_cal_bus_clk(XTAL_CLK_FREQ, clk_speed, &clk_cal);
I2C_CLOCK_SRC_ATOMIC() {
i2c_ll_set_source_clk(i2c->dev, SOC_MOD_CLK_XTAL); /*!< I2C source clock from XTAL, 40M */
}
#endif
i2c_ll_set_txfifo_empty_thr(i2c->dev, a);
i2c_ll_set_rxfifo_full_thr(i2c->dev, SOC_I2C_FIFO_LEN - a);
i2c_ll_master_set_bus_timing(i2c->dev, &clk_cal);
i2c_ll_master_set_filter(i2c->dev, 3);
return true;
}
static void i2c_slave_delay_us(uint64_t us) {
uint64_t m = esp_timer_get_time();
if (us) {
uint64_t e = (m + us);
if (m > e) { //overflow
while ((uint64_t)esp_timer_get_time() > e);
}
while ((uint64_t)esp_timer_get_time() < e);
}
}
static void i2c_slave_gpio_mode(int8_t pin, gpio_mode_t mode) {
gpio_config_t conf = {
.pin_bit_mask = 1LL << pin, .mode = mode, .pull_up_en = GPIO_PULLUP_ENABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE, .intr_type = GPIO_INTR_DISABLE
};
gpio_config(&conf);
}
static bool i2c_slave_check_line_state(int8_t sda, int8_t scl) {
if (sda < 0 || scl < 0) {
return false; //return false since there is nothing to do
}
// if the bus is not 'clear' try the cycling SCL until SDA goes High or 9 cycles
gpio_set_level(sda, 1);
gpio_set_level(scl, 1);
i2c_slave_gpio_mode(sda, GPIO_MODE_INPUT | GPIO_MODE_DEF_OD);
i2c_slave_gpio_mode(scl, GPIO_MODE_INPUT | GPIO_MODE_DEF_OD);
gpio_set_level(scl, 1);
if (!gpio_get_level(sda) || !gpio_get_level(scl)) { // bus in busy state
log_w("invalid state sda(%d)=%d, scl(%d)=%d", sda, gpio_get_level(sda), scl, gpio_get_level(scl));
for (uint8_t a = 0; a < 9; a++) {
i2c_slave_delay_us(5);
if (gpio_get_level(sda) && gpio_get_level(scl)) { // bus recovered
log_w("Recovered after %d Cycles", a);
gpio_set_level(sda, 0); // start
i2c_slave_delay_us(5);
for (uint8_t b = 0; b < 9; b++) {
gpio_set_level(scl, 1);
i2c_slave_delay_us(5);
gpio_set_level(scl, 0);
i2c_slave_delay_us(5);
}
gpio_set_level(scl, 1);
i2c_slave_delay_us(5);
gpio_set_level(sda, 1); // stop
break;
}
gpio_set_level(scl, 0);
i2c_slave_delay_us(5);
gpio_set_level(scl, 1);
}
}
if (!gpio_get_level(sda) || !gpio_get_level(scl)) { // bus in busy state
log_e("Bus Invalid State, Can't init sda=%d, scl=%d", gpio_get_level(sda), gpio_get_level(scl));
return false; // bus is busy
}
return true;
}
static bool i2c_slave_attach_gpio(i2c_slave_struct_t *i2c, int8_t sda, int8_t scl) {
if (i2c == NULL) {
log_e("no control block");
return false;
}
if ((sda < 0) || (scl < 0)) {
log_e("bad pins sda=%d, scl=%d", sda, scl);
return false;
}
i2c->scl = scl;
gpio_set_level(scl, 1);
i2c_slave_gpio_mode(scl, GPIO_MODE_INPUT_OUTPUT_OD);
gpio_matrix_out(scl, I2C_SCL_IDX(i2c->num), false, false);
gpio_matrix_in(scl, I2C_SCL_IDX(i2c->num), false);
i2c->sda = sda;
gpio_set_level(sda, 1);
i2c_slave_gpio_mode(sda, GPIO_MODE_INPUT_OUTPUT_OD);
gpio_matrix_out(sda, I2C_SDA_IDX(i2c->num), false, false);
gpio_matrix_in(sda, I2C_SDA_IDX(i2c->num), false);
return true;
}
static bool i2c_slave_detach_gpio(i2c_slave_struct_t *i2c) {
if (i2c == NULL) {
log_e("no control Block");
return false;
}
if (i2c->scl >= 0) {
gpio_matrix_out(i2c->scl, 0x100, false, false);
gpio_matrix_in(0x30, I2C_SCL_IDX(i2c->num), false);
i2c_slave_gpio_mode(i2c->scl, GPIO_MODE_INPUT);
i2c->scl = -1; // un attached
}
if (i2c->sda >= 0) {
gpio_matrix_out(i2c->sda, 0x100, false, false);
gpio_matrix_in(0x30, I2C_SDA_IDX(i2c->num), false);
i2c_slave_gpio_mode(i2c->sda, GPIO_MODE_INPUT);
i2c->sda = -1; // un attached
}
return true;
}
static bool i2c_slave_send_event(i2c_slave_struct_t *i2c, i2c_slave_queue_event_t *event) {
bool pxHigherPriorityTaskWoken = false;
if (i2c->event_queue) {
if (xQueueSendFromISR(i2c->event_queue, event, (BaseType_t *const)&pxHigherPriorityTaskWoken) != pdTRUE) {
//log_e("event_queue_full");
}
}
return pxHigherPriorityTaskWoken;
}
static bool i2c_slave_handle_tx_fifo_empty(i2c_slave_struct_t *i2c) {
bool pxHigherPriorityTaskWoken = false;
uint32_t d = 0, moveCnt = 0;
i2c_ll_get_txfifo_len(i2c->dev, &moveCnt);
while (moveCnt > 0) { // read tx queue until Fifo is full or queue is empty
if (xQueueReceiveFromISR(i2c->tx_queue, &d, (BaseType_t *const)&pxHigherPriorityTaskWoken) == pdTRUE) {
i2c_ll_write_txfifo(i2c->dev, (uint8_t *)&d, 1);
moveCnt--;
} else {
i2c_ll_slave_disable_tx_it(i2c->dev);
break;
}
}
return pxHigherPriorityTaskWoken;
}
static bool i2c_slave_handle_rx_fifo_full(i2c_slave_struct_t *i2c, uint32_t len) {
#if I2C_SLAVE_USE_RX_QUEUE
uint32_t d = 0;
#else
uint8_t data[SOC_I2C_FIFO_LEN];
#endif
bool pxHigherPriorityTaskWoken = false;
#if I2C_SLAVE_USE_RX_QUEUE
while (len > 0) {
i2c_ll_read_rxfifo(i2c->dev, (uint8_t *)&d, 1);
if (xQueueSendFromISR(i2c->rx_queue, &d, (BaseType_t *const)&pxHigherPriorityTaskWoken) != pdTRUE) {
log_e("rx_queue_full");
} else {
i2c->rx_data_count++;
}
if (--len == 0) {
len = i2c_ll_get_rxfifo_cnt(i2c->dev);
}
#else
if (len) {
i2c_ll_read_rxfifo(i2c->dev, data, len);
if (xRingbufferSendFromISR(i2c->rx_ring_buf, (void *)data, len, (BaseType_t *const)&pxHigherPriorityTaskWoken) != pdTRUE) {
log_e("rx_ring_buf_full");
} else {
i2c->rx_data_count += len;
}
#endif
}
return pxHigherPriorityTaskWoken;
}
static void i2c_slave_isr_handler(void *arg) {
bool pxHigherPriorityTaskWoken = false;
i2c_slave_struct_t *i2c = (i2c_slave_struct_t *)arg; // recover data
uint32_t activeInt = 0;
i2c_ll_get_intr_mask(i2c->dev, &activeInt);
i2c_ll_clear_intr_mask(i2c->dev, activeInt);
uint32_t rx_fifo_len = 0;
i2c_ll_get_rxfifo_cnt(i2c->dev, &rx_fifo_len);
bool slave_rw = i2c_ll_slave_rw(i2c->dev);
if (activeInt & I2C_RXFIFO_WM_INT_ENA) { // RX FiFo Full
pxHigherPriorityTaskWoken |= i2c_slave_handle_rx_fifo_full(i2c, rx_fifo_len);
i2c_ll_slave_enable_rx_it(i2c->dev); //is this necessary?
}
if (activeInt & I2C_TRANS_COMPLETE_INT_ENA) { // STOP
if (rx_fifo_len) { //READ RX FIFO
pxHigherPriorityTaskWoken |= i2c_slave_handle_rx_fifo_full(i2c, rx_fifo_len);
}
if (i2c->rx_data_count) { //WRITE or RepeatedStart
//SEND RX Event
i2c_slave_queue_event_t event;
event.event = I2C_SLAVE_EVT_RX;
event.stop = !slave_rw;
event.param = i2c->rx_data_count;
pxHigherPriorityTaskWoken |= i2c_slave_send_event(i2c, &event);
//Zero RX count
i2c->rx_data_count = 0;
}
if (slave_rw) { // READ
#if CONFIG_IDF_TARGET_ESP32
if (i2c->dev->status_reg.scl_main_state_last == 6) {
//SEND TX Event
i2c_slave_queue_event_t event;
event.event = I2C_SLAVE_EVT_TX;
pxHigherPriorityTaskWoken |= i2c_slave_send_event(i2c, &event);
}
#else
//reset TX data
i2c_ll_txfifo_rst(i2c->dev);
uint8_t d;
while (xQueueReceiveFromISR(i2c->tx_queue, &d, (BaseType_t *const)&pxHigherPriorityTaskWoken) == pdTRUE); //flush partial write
#endif
}
}
#ifndef CONFIG_IDF_TARGET_ESP32
if (activeInt & I2C_SLAVE_STRETCH_INT_ENA) { // STRETCH
i2c_stretch_cause_t cause = i2c_ll_stretch_cause(i2c->dev);
if (cause == I2C_STRETCH_CAUSE_MASTER_READ) {
//on C3 RX data disappears with repeated start, so we need to get it here
if (rx_fifo_len) {
pxHigherPriorityTaskWoken |= i2c_slave_handle_rx_fifo_full(i2c, rx_fifo_len);
}
//SEND TX Event
i2c_slave_queue_event_t event;
event.event = I2C_SLAVE_EVT_TX;
pxHigherPriorityTaskWoken |= i2c_slave_send_event(i2c, &event);
//will clear after execution
} else if (cause == I2C_STRETCH_CAUSE_TX_FIFO_EMPTY) {
pxHigherPriorityTaskWoken |= i2c_slave_handle_tx_fifo_empty(i2c);
i2c_ll_stretch_clr(i2c->dev);
} else if (cause == I2C_STRETCH_CAUSE_RX_FIFO_FULL) {
pxHigherPriorityTaskWoken |= i2c_slave_handle_rx_fifo_full(i2c, rx_fifo_len);
i2c_ll_stretch_clr(i2c->dev);
}
}
#endif
if (activeInt & I2C_TXFIFO_WM_INT_ENA) { // TX FiFo Empty
pxHigherPriorityTaskWoken |= i2c_slave_handle_tx_fifo_empty(i2c);
}
if (pxHigherPriorityTaskWoken) {
portYIELD_FROM_ISR();
}
}
static size_t i2c_slave_read_rx(i2c_slave_struct_t *i2c, uint8_t *data, size_t len) {
if (!len) {
return 0;
}
#if I2C_SLAVE_USE_RX_QUEUE
uint8_t d = 0;
BaseType_t res = pdTRUE;
for (size_t i = 0; i < len; i++) {
if (data) {
res = xQueueReceive(i2c->rx_queue, &data[i], 0);
} else {
res = xQueueReceive(i2c->rx_queue, &d, 0);
}
if (res != pdTRUE) {
log_e("Read Queue(%u) Failed", i);
len = i;
break;
}
}
return (data) ? len : 0;
#else
size_t dlen = 0, to_read = len, so_far = 0, available = 0;
uint8_t *rx_data = NULL;
vRingbufferGetInfo(i2c->rx_ring_buf, NULL, NULL, NULL, NULL, &available);
if (available < to_read) {
log_e("Less available than requested. %u < %u", available, len);
to_read = available;
}
while (to_read) {
dlen = 0;
rx_data = (uint8_t *)xRingbufferReceiveUpTo(i2c->rx_ring_buf, &dlen, 0, to_read);
if (!rx_data) {
log_e("Receive %u Failed", to_read);
return so_far;
}
if (data) {
memcpy(data + so_far, rx_data, dlen);
}
vRingbufferReturnItem(i2c->rx_ring_buf, rx_data);
so_far += dlen;
to_read -= dlen;
}
return (data) ? so_far : 0;
#endif
}
static void i2c_slave_task(void *pv_args) {
i2c_slave_struct_t *i2c = (i2c_slave_struct_t *)pv_args;
i2c_slave_queue_event_t event;
size_t len = 0;
bool stop = false;
uint8_t *data = NULL;
for (;;) {
if (xQueueReceive(i2c->event_queue, &event, portMAX_DELAY) == pdTRUE) {
// Write
if (event.event == I2C_SLAVE_EVT_RX) {
len = event.param;
stop = event.stop;
data = (len > 0) ? (uint8_t *)malloc(len) : NULL;
if (len && data == NULL) {
log_e("Malloc (%u) Failed", len);
}
len = i2c_slave_read_rx(i2c, data, len);
if (i2c->receive_callback) {
i2c->receive_callback(i2c->num, data, len, stop, i2c->arg);
}
free(data);
// Read
} else if (event.event == I2C_SLAVE_EVT_TX) {
if (i2c->request_callback) {
i2c->request_callback(i2c->num, i2c->arg);
}
i2c_ll_stretch_clr(i2c->dev);
}
}
}
vTaskDelete(NULL);
}
static bool i2cSlaveDetachBus(void *bus_i2c_num) {
uint8_t num = (int)bus_i2c_num - 1;
i2c_slave_struct_t *i2c = &_i2c_bus_array[num];
if (i2c->scl == -1 && i2c->sda == -1) {
return true;
}
esp_err_t err = i2cSlaveDeinit(num);
if (err != ESP_OK) {
log_e("i2cSlaveDeinit failed with error: %d", err);
return false;
}
return true;
}
#endif /* SOC_I2C_SUPPORT_SLAVE */
+40
View File
@@ -0,0 +1,40 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "soc/soc_caps.h"
#if SOC_I2C_SUPPORT_SLAVE
#ifdef __cplusplus
extern "C" {
#endif
#include "stdint.h"
#include "stddef.h"
#include "esp_err.h"
typedef void (*i2c_slave_request_cb_t)(uint8_t num, void *arg);
typedef void (*i2c_slave_receive_cb_t)(uint8_t num, uint8_t *data, size_t len, bool stop, void *arg);
esp_err_t i2cSlaveAttachCallbacks(uint8_t num, i2c_slave_request_cb_t request_callback, i2c_slave_receive_cb_t receive_callback, void *arg);
esp_err_t i2cSlaveInit(uint8_t num, int sda, int scl, uint16_t slaveID, uint32_t frequency, size_t rx_len, size_t tx_len);
esp_err_t i2cSlaveDeinit(uint8_t num);
size_t i2cSlaveWrite(uint8_t num, const uint8_t *buf, uint32_t len, uint32_t timeout_ms);
#ifdef __cplusplus
}
#endif
#endif /* SOC_I2C_SUPPORT_SLAVE */
+435
View File
@@ -0,0 +1,435 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "esp32-hal-i2c.h"
#if SOC_I2C_SUPPORTED
#include "esp_idf_version.h"
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 0)
#include "esp32-hal.h"
#if !CONFIG_DISABLE_HAL_LOCKS
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#endif
#include "esp_attr.h"
#include "esp_system.h"
#include "soc/soc_caps.h"
#include "soc/i2c_periph.h"
#include "hal/i2c_hal.h"
#include "hal/i2c_ll.h"
#include "driver/i2c.h"
#include "esp32-hal-periman.h"
#include "esp_private/periph_ctrl.h"
#if SOC_PERIPH_CLK_CTRL_SHARED
#define I2C_CLOCK_SRC_ATOMIC() PERIPH_RCC_ATOMIC()
#else
#define I2C_CLOCK_SRC_ATOMIC()
#endif
#if !SOC_RCC_IS_INDEPENDENT
#define I2C_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
#else
#define I2C_RCC_ATOMIC()
#endif
#if SOC_I2C_SUPPORT_APB || SOC_I2C_SUPPORT_XTAL
#include "esp_private/esp_clk.h"
#endif
#if SOC_I2C_SUPPORT_RTC
#include "clk_ctrl_os.h"
#endif
typedef volatile struct {
bool initialized;
uint32_t frequency;
#if !CONFIG_DISABLE_HAL_LOCKS
SemaphoreHandle_t lock;
#endif
int8_t scl;
int8_t sda;
} i2c_bus_t;
static i2c_bus_t bus[SOC_I2C_NUM];
static bool i2cDetachBus(void *bus_i2c_num) {
uint8_t i2c_num = (int)bus_i2c_num - 1;
if (!bus[i2c_num].initialized) {
return true;
}
esp_err_t err = i2cDeinit(i2c_num);
if (err != ESP_OK) {
log_e("i2cDeinit failed with error: %d", err);
return false;
}
return true;
}
bool i2cIsInit(uint8_t i2c_num) {
if (i2c_num >= SOC_I2C_NUM) {
return false;
}
return bus[i2c_num].initialized;
}
esp_err_t i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t frequency) {
esp_err_t ret = ESP_OK;
if (i2c_num >= SOC_I2C_NUM) {
return ESP_ERR_INVALID_ARG;
}
#if !CONFIG_DISABLE_HAL_LOCKS
if (bus[i2c_num].lock == NULL) {
bus[i2c_num].lock = xSemaphoreCreateMutex();
if (bus[i2c_num].lock == NULL) {
log_e("xSemaphoreCreateMutex failed");
return ESP_ERR_NO_MEM;
}
}
//acquire lock
if (xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE) {
log_e("could not acquire lock");
return ESP_FAIL;
}
#endif
if (bus[i2c_num].initialized) {
log_e("bus is already initialized");
ret = ESP_FAIL;
goto init_fail;
}
if (!frequency) {
frequency = 100000UL;
} else if (frequency > 1000000UL) {
frequency = 1000000UL;
}
perimanSetBusDeinit(ESP32_BUS_TYPE_I2C_MASTER_SDA, i2cDetachBus);
perimanSetBusDeinit(ESP32_BUS_TYPE_I2C_MASTER_SCL, i2cDetachBus);
if (!perimanClearPinBus(sda) || !perimanClearPinBus(scl)) {
ret = ESP_FAIL;
goto init_fail;
}
log_i("Initializing I2C Master: sda=%d scl=%d freq=%d", sda, scl, frequency);
i2c_config_t conf = {};
conf.mode = I2C_MODE_MASTER;
conf.scl_io_num = (gpio_num_t)scl;
conf.sda_io_num = (gpio_num_t)sda;
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
conf.master.clk_speed = frequency;
conf.clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL; //Any one clock source that is available for the specified frequency may be chosen
ret = i2c_param_config((i2c_port_t)i2c_num, &conf);
if (ret != ESP_OK) {
log_e("i2c_param_config failed");
} else {
ret = i2c_driver_install((i2c_port_t)i2c_num, conf.mode, 0, 0, 0);
if (ret != ESP_OK) {
log_e("i2c_driver_install failed");
} else {
bus[i2c_num].initialized = true;
bus[i2c_num].frequency = frequency;
bus[i2c_num].scl = scl;
bus[i2c_num].sda = sda;
//Clock Stretching Timeout: 20b:esp32, 5b:esp32-c3, 24b:esp32-s2
i2c_set_timeout((i2c_port_t)i2c_num, I2C_LL_MAX_TIMEOUT);
if (!perimanSetPinBus(sda, ESP32_BUS_TYPE_I2C_MASTER_SDA, (void *)(i2c_num + 1), i2c_num, -1)
|| !perimanSetPinBus(scl, ESP32_BUS_TYPE_I2C_MASTER_SCL, (void *)(i2c_num + 1), i2c_num, -1)) {
#if !CONFIG_DISABLE_HAL_LOCKS
//release lock so that i2cDetachBus can execute i2cDeinit
xSemaphoreGive(bus[i2c_num].lock);
#endif
i2cDetachBus((void *)(i2c_num + 1));
return ESP_FAIL;
}
}
}
init_fail:
#if !CONFIG_DISABLE_HAL_LOCKS
//release lock
xSemaphoreGive(bus[i2c_num].lock);
#endif
return ret;
}
esp_err_t i2cDeinit(uint8_t i2c_num) {
esp_err_t err = ESP_FAIL;
if (i2c_num >= SOC_I2C_NUM) {
return ESP_ERR_INVALID_ARG;
}
#if !CONFIG_DISABLE_HAL_LOCKS
//acquire lock
if (bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE) {
log_e("could not acquire lock");
return err;
}
#endif
if (!bus[i2c_num].initialized) {
log_e("bus is not initialized");
} else {
err = i2c_driver_delete((i2c_port_t)i2c_num);
if (err == ESP_OK) {
bus[i2c_num].initialized = false;
perimanClearPinBus(bus[i2c_num].scl);
perimanClearPinBus(bus[i2c_num].sda);
bus[i2c_num].scl = -1;
bus[i2c_num].sda = -1;
}
}
#if !CONFIG_DISABLE_HAL_LOCKS
//release lock
xSemaphoreGive(bus[i2c_num].lock);
#endif
return err;
}
esp_err_t i2cWrite(uint8_t i2c_num, uint16_t address, const uint8_t *buff, size_t size, uint32_t timeOutMillis) {
esp_err_t ret = ESP_FAIL;
i2c_cmd_handle_t cmd = NULL;
if (i2c_num >= SOC_I2C_NUM) {
return ESP_ERR_INVALID_ARG;
}
#if !CONFIG_DISABLE_HAL_LOCKS
//acquire lock
if (bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE) {
log_e("could not acquire lock");
return ret;
}
#endif
if (!bus[i2c_num].initialized) {
log_e("bus is not initialized");
goto end;
}
//short implementation does not support zero size writes (example when scanning) PR in IDF?
//ret = i2c_master_write_to_device((i2c_port_t)i2c_num, address, buff, size, timeOutMillis / portTICK_PERIOD_MS);
ret = ESP_OK;
uint8_t cmd_buff[I2C_LINK_RECOMMENDED_SIZE(1)] = {0};
cmd = i2c_cmd_link_create_static(cmd_buff, I2C_LINK_RECOMMENDED_SIZE(1));
ret = i2c_master_start(cmd);
if (ret != ESP_OK) {
goto end;
}
ret = i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, true);
if (ret != ESP_OK) {
goto end;
}
if (size) {
ret = i2c_master_write(cmd, buff, size, true);
if (ret != ESP_OK) {
goto end;
}
}
ret = i2c_master_stop(cmd);
if (ret != ESP_OK) {
goto end;
}
ret = i2c_master_cmd_begin((i2c_port_t)i2c_num, cmd, timeOutMillis / portTICK_PERIOD_MS);
end:
if (cmd != NULL) {
i2c_cmd_link_delete_static(cmd);
}
#if !CONFIG_DISABLE_HAL_LOCKS
//release lock
xSemaphoreGive(bus[i2c_num].lock);
#endif
return ret;
}
esp_err_t i2cRead(uint8_t i2c_num, uint16_t address, uint8_t *buff, size_t size, uint32_t timeOutMillis, size_t *readCount) {
esp_err_t ret = ESP_FAIL;
if (i2c_num >= SOC_I2C_NUM) {
return ESP_ERR_INVALID_ARG;
}
#if !CONFIG_DISABLE_HAL_LOCKS
//acquire lock
if (bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE) {
log_e("could not acquire lock");
return ret;
}
#endif
if (!bus[i2c_num].initialized) {
log_e("bus is not initialized");
} else {
ret = i2c_master_read_from_device((i2c_port_t)i2c_num, address, buff, size, timeOutMillis / portTICK_PERIOD_MS);
if (ret == ESP_OK) {
*readCount = size;
} else {
*readCount = 0;
}
}
#if !CONFIG_DISABLE_HAL_LOCKS
//release lock
xSemaphoreGive(bus[i2c_num].lock);
#endif
return ret;
}
esp_err_t i2cWriteReadNonStop(
uint8_t i2c_num, uint16_t address, const uint8_t *wbuff, size_t wsize, uint8_t *rbuff, size_t rsize, uint32_t timeOutMillis, size_t *readCount
) {
esp_err_t ret = ESP_FAIL;
if (i2c_num >= SOC_I2C_NUM) {
return ESP_ERR_INVALID_ARG;
}
#if !CONFIG_DISABLE_HAL_LOCKS
//acquire lock
if (bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE) {
log_e("could not acquire lock");
return ret;
}
#endif
if (!bus[i2c_num].initialized) {
log_e("bus is not initialized");
} else {
ret = i2c_master_write_read_device((i2c_port_t)i2c_num, address, wbuff, wsize, rbuff, rsize, timeOutMillis / portTICK_PERIOD_MS);
if (ret == ESP_OK) {
*readCount = rsize;
} else {
*readCount = 0;
}
}
#if !CONFIG_DISABLE_HAL_LOCKS
//release lock
xSemaphoreGive(bus[i2c_num].lock);
#endif
return ret;
}
esp_err_t i2cSetClock(uint8_t i2c_num, uint32_t frequency) {
esp_err_t ret = ESP_FAIL;
if (i2c_num >= SOC_I2C_NUM) {
return ESP_ERR_INVALID_ARG;
}
#if !CONFIG_DISABLE_HAL_LOCKS
//acquire lock
if (bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE) {
log_e("could not acquire lock");
return ret;
}
#endif
if (!bus[i2c_num].initialized) {
log_e("bus is not initialized");
goto end;
}
if (bus[i2c_num].frequency == frequency) {
ret = ESP_OK;
goto end;
}
if (!frequency) {
frequency = 100000UL;
} else if (frequency > 1000000UL) {
frequency = 1000000UL;
}
typedef struct {
soc_module_clk_t clk; /*!< I2C source clock */
uint32_t clk_freq; /*!< I2C source clock frequency */
} i2c_clk_alloc_t;
typedef enum {
I2C_SCLK_DEFAULT = 0, /*!< I2C source clock not selected*/
#if SOC_I2C_SUPPORT_APB
I2C_SCLK_APB, /*!< I2C source clock from APB, 80M*/
#endif
#if SOC_I2C_SUPPORT_XTAL
I2C_SCLK_XTAL, /*!< I2C source clock from XTAL, 40M */
#endif
#if SOC_I2C_SUPPORT_RTC
I2C_SCLK_RTC, /*!< I2C source clock from 8M RTC, 8M */
#endif
#if SOC_I2C_SUPPORT_REF_TICK
I2C_SCLK_REF_TICK, /*!< I2C source clock from REF_TICK, 1M */
#endif
I2C_SCLK_MAX,
} i2c_sclk_t;
// i2c clock characteristic, The order is the same as i2c_sclk_t.
i2c_clk_alloc_t i2c_clk_alloc[I2C_SCLK_MAX] = {
{0, 0},
#if SOC_I2C_SUPPORT_APB
{SOC_MOD_CLK_APB, esp_clk_apb_freq()}, /*!< I2C APB clock characteristic*/
#endif
#if SOC_I2C_SUPPORT_XTAL
{SOC_MOD_CLK_XTAL, esp_clk_xtal_freq()}, /*!< I2C XTAL characteristic*/
#endif
#if SOC_I2C_SUPPORT_RTC
{SOC_MOD_CLK_RC_FAST, periph_rtc_dig_clk8m_get_freq()}, /*!< I2C 20M RTC characteristic*/
#endif
#if SOC_I2C_SUPPORT_REF_TICK
{SOC_MOD_CLK_REF_TICK, REF_CLK_FREQ}, /*!< I2C REF_TICK characteristic*/
#endif
};
i2c_sclk_t src_clk = I2C_SCLK_DEFAULT;
ret = ESP_OK;
for (i2c_sclk_t clk = I2C_SCLK_DEFAULT + 1; clk < I2C_SCLK_MAX; clk++) {
#if CONFIG_IDF_TARGET_ESP32S3
if (clk == I2C_SCLK_RTC) { // RTC clock for s3 is inaccessible now.
continue;
}
#endif
if (frequency <= i2c_clk_alloc[clk].clk_freq) {
src_clk = clk;
break;
}
}
if (src_clk == I2C_SCLK_DEFAULT || src_clk == I2C_SCLK_MAX) {
log_e("clock source could not be selected");
ret = ESP_FAIL;
} else {
i2c_hal_context_t hal;
hal.dev = I2C_LL_GET_HW(i2c_num);
#if SOC_I2C_SUPPORT_RTC
if (src_clk == I2C_SCLK_RTC) {
periph_rtc_dig_clk8m_enable();
}
#endif
I2C_CLOCK_SRC_ATOMIC() {
i2c_hal_set_bus_timing(&(hal), frequency, i2c_clk_alloc[src_clk].clk, i2c_clk_alloc[src_clk].clk_freq);
}
bus[i2c_num].frequency = frequency;
//Clock Stretching Timeout: 20b:esp32, 5b:esp32-c3, 24b:esp32-s2
i2c_set_timeout((i2c_port_t)i2c_num, I2C_LL_MAX_TIMEOUT);
}
end:
#if !CONFIG_DISABLE_HAL_LOCKS
//release lock
xSemaphoreGive(bus[i2c_num].lock);
#endif
return ret;
}
esp_err_t i2cGetClock(uint8_t i2c_num, uint32_t *frequency) {
if (i2c_num >= SOC_I2C_NUM) {
return ESP_ERR_INVALID_ARG;
}
if (!bus[i2c_num].initialized) {
log_e("bus is not initialized");
return ESP_FAIL;
}
*frequency = bus[i2c_num].frequency;
return ESP_OK;
}
#endif /* ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 0) */
#endif /* SOC_I2C_SUPPORTED */
+52
View File
@@ -0,0 +1,52 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// modified Nov 2017 by Chuck Todd <StickBreaker> to support Interrupt Driven I/O
// modified Nov 2021 by Hristo Gochkov <Me-No-Dev> to support ESP-IDF API
#ifndef _ESP32_HAL_I2C_H_
#define _ESP32_HAL_I2C_H_
#include "soc/soc_caps.h"
#if SOC_I2C_SUPPORTED
#include "esp_idf_version.h"
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
#include <esp_err.h>
esp_err_t i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t clk_speed);
esp_err_t i2cDeinit(uint8_t i2c_num);
esp_err_t i2cSetClock(uint8_t i2c_num, uint32_t frequency);
esp_err_t i2cGetClock(uint8_t i2c_num, uint32_t *frequency);
esp_err_t i2cWrite(uint8_t i2c_num, uint16_t address, const uint8_t *buff, size_t size, uint32_t timeOutMillis);
esp_err_t i2cRead(uint8_t i2c_num, uint16_t address, uint8_t *buff, size_t size, uint32_t timeOutMillis, size_t *readCount);
esp_err_t i2cWriteReadNonStop(
uint8_t i2c_num, uint16_t address, const uint8_t *wbuff, size_t wsize, uint8_t *rbuff, size_t rsize, uint32_t timeOutMillis, size_t *readCount
);
bool i2cIsInit(uint8_t i2c_num);
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)
void *i2cBusHandle(uint8_t i2c_num);
#endif
#ifdef __cplusplus
}
#endif
#endif /* SOC_I2C_SUPPORTED */
#endif /* _ESP32_HAL_I2C_H_ */
+808
View File
@@ -0,0 +1,808 @@
// Copyright 2015-2023 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "soc/soc_caps.h"
#if SOC_LEDC_SUPPORTED
#include "esp32-hal.h"
#include "esp32-hal-ledc.h"
#include "driver/ledc.h"
#include "esp32-hal-periman.h"
#include "soc/gpio_sig_map.h"
#include "esp_rom_gpio.h"
#include "hal/ledc_ll.h"
#if SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED
#include <math.h>
#endif
#ifdef SOC_LEDC_SUPPORT_HS_MODE
#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM << 1)
#else
#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM)
#endif
//Use XTAL clock if possible to avoid timer frequency error when setting APB clock < 80 Mhz
//Need to be fixed in ESP-IDF
#ifdef SOC_LEDC_SUPPORT_XTAL_CLOCK
#define LEDC_DEFAULT_CLK LEDC_USE_XTAL_CLK
#else
#define LEDC_DEFAULT_CLK LEDC_AUTO_CLK
#endif
#define LEDC_MAX_BIT_WIDTH SOC_LEDC_TIMER_BIT_WIDTH
typedef struct {
int used_channels : LEDC_CHANNELS; // Used channels as a bits
} ledc_periph_t;
ledc_periph_t ledc_handle = {0};
// Helper function to find a timer with matching frequency and resolution
static bool find_matching_timer(uint8_t speed_mode, uint32_t freq, uint8_t resolution, uint8_t *timer_num) {
log_d("Searching for timer with freq=%u, resolution=%u", freq, resolution);
// Check all channels to find one with matching frequency and resolution
for (uint8_t i = 0; i < SOC_GPIO_PIN_COUNT; i++) {
if (!perimanPinIsValid(i)) {
continue;
}
peripheral_bus_type_t type = perimanGetPinBusType(i);
if (type == ESP32_BUS_TYPE_LEDC) {
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(i, ESP32_BUS_TYPE_LEDC);
if (bus != NULL && (bus->channel / SOC_LEDC_CHANNEL_NUM) == speed_mode && bus->freq_hz == freq && bus->channel_resolution == resolution) {
log_d("Found matching timer %u for freq=%u, resolution=%u", bus->timer_num, freq, resolution);
*timer_num = bus->timer_num;
return true;
}
}
}
log_d("No matching timer found for freq=%u, resolution=%u", freq, resolution);
return false;
}
// Helper function to find an unused timer
static bool find_free_timer(uint8_t speed_mode, uint8_t *timer_num) {
// Check which timers are in use
uint8_t used_timers = 0;
for (uint8_t i = 0; i < SOC_GPIO_PIN_COUNT; i++) {
if (!perimanPinIsValid(i)) {
continue;
}
peripheral_bus_type_t type = perimanGetPinBusType(i);
if (type == ESP32_BUS_TYPE_LEDC) {
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(i, ESP32_BUS_TYPE_LEDC);
if (bus != NULL && (bus->channel / SOC_LEDC_CHANNEL_NUM) == speed_mode) {
log_d("Timer %u is in use by channel %u", bus->timer_num, bus->channel);
used_timers |= (1 << bus->timer_num);
}
}
}
#ifndef SOC_LEDC_TIMER_NUM
#define SOC_LEDC_TIMER_NUM 4
#endif
// Find first unused timer
for (uint8_t i = 0; i < SOC_LEDC_TIMER_NUM; i++) {
if (!(used_timers & (1 << i))) {
log_d("Found free timer %u", i);
*timer_num = i;
return true;
}
}
log_e("No free timers available");
return false;
}
// Helper function to remove a channel from a timer and clear timer if no channels are using it
static void remove_channel_from_timer(uint8_t speed_mode, uint8_t timer_num, uint8_t channel) {
log_d("Removing channel %u from timer %u in speed_mode %u", channel, timer_num, speed_mode);
// Check if any other channels are using this timer
bool timer_in_use = false;
for (uint8_t i = 0; i < SOC_GPIO_PIN_COUNT; i++) {
if (!perimanPinIsValid(i)) {
continue;
}
peripheral_bus_type_t type = perimanGetPinBusType(i);
if (type == ESP32_BUS_TYPE_LEDC) {
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(i, ESP32_BUS_TYPE_LEDC);
if (bus != NULL && (bus->channel / SOC_LEDC_CHANNEL_NUM) == speed_mode && bus->timer_num == timer_num && bus->channel != channel) {
log_d("Timer %u is still in use by channel %u", timer_num, bus->channel);
timer_in_use = true;
break;
}
}
}
if (!timer_in_use) {
log_d("No other channels using timer %u, deconfiguring timer", timer_num);
// Stop the timer
ledc_timer_pause(speed_mode, timer_num);
// Deconfigure the timer
ledc_timer_config_t ledc_timer;
memset((void *)&ledc_timer, 0, sizeof(ledc_timer_config_t));
ledc_timer.speed_mode = speed_mode;
ledc_timer.timer_num = timer_num;
ledc_timer.deconfigure = true;
ledc_timer_config(&ledc_timer);
}
}
static bool fade_initialized = false;
static ledc_clk_cfg_t clock_source = LEDC_DEFAULT_CLK;
ledc_clk_cfg_t ledcGetClockSource(void) {
return clock_source;
}
bool ledcSetClockSource(ledc_clk_cfg_t source) {
if (ledc_handle.used_channels) {
log_e("Cannot change LEDC clock source! LEDC channels in use.");
return false;
}
clock_source = source;
return true;
}
static bool ledcDetachBus(void *bus) {
ledc_channel_handle_t *handle = (ledc_channel_handle_t *)bus;
bool channel_found = false;
// Check if more pins are attached to the same ledc channel
for (uint8_t i = 0; i < SOC_GPIO_PIN_COUNT; i++) {
if (!perimanPinIsValid(i) || i == handle->pin) {
continue; //invalid pin or same pin
}
peripheral_bus_type_t type = perimanGetPinBusType(i);
if (type == ESP32_BUS_TYPE_LEDC) {
ledc_channel_handle_t *bus_check = (ledc_channel_handle_t *)perimanGetPinBus(i, ESP32_BUS_TYPE_LEDC);
if (bus_check->channel == handle->channel) {
channel_found = true;
break;
}
}
}
pinMatrixOutDetach(handle->pin, false, false);
if (!channel_found) {
uint8_t group = (handle->channel / SOC_LEDC_CHANNEL_NUM);
remove_channel_from_timer(group, handle->timer_num, handle->channel % SOC_LEDC_CHANNEL_NUM);
ledc_handle.used_channels &= ~(1UL << handle->channel);
}
free(handle);
if (ledc_handle.used_channels == 0) {
ledc_fade_func_uninstall();
fade_initialized = false;
}
return true;
}
bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t channel) {
if (channel >= LEDC_CHANNELS) {
log_e("Channel %u is not available (maximum %u)!", channel, LEDC_CHANNELS);
return false;
}
if (freq == 0) {
log_e("LEDC pin %u - frequency can't be zero.", pin);
return false;
}
if (resolution == 0 || resolution > LEDC_MAX_BIT_WIDTH) {
log_e("LEDC pin %u - resolution is zero or it is too big (maximum %u)", pin, LEDC_MAX_BIT_WIDTH);
return false;
}
perimanSetBusDeinit(ESP32_BUS_TYPE_LEDC, ledcDetachBus);
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
if (bus != NULL) {
log_e("Pin %u is already attached to LEDC (channel %u, resolution %u)", pin, bus->channel, bus->channel_resolution);
return false;
}
if (!perimanClearPinBus(pin)) {
log_e("Pin %u is already attached to another bus and failed to detach", pin);
return false;
}
uint8_t group = (channel / SOC_LEDC_CHANNEL_NUM);
uint8_t timer = 0;
bool channel_used = ledc_handle.used_channels & (1UL << channel);
if (channel_used) {
log_i("Channel %u is already set up, given frequency and resolution will be ignored", channel);
if (ledc_set_pin(pin, group, channel % SOC_LEDC_CHANNEL_NUM) != ESP_OK) {
log_e("Attaching pin to already used channel failed!");
return false;
}
} else {
// Find a timer with matching frequency and resolution, or a free timer
if (!find_matching_timer(group, freq, resolution, &timer)) {
if (!find_free_timer(group, &timer)) {
log_w("No free timers available for speed mode %u", group);
return false;
}
// Configure the timer if we're using a new one
ledc_timer_config_t ledc_timer;
memset((void *)&ledc_timer, 0, sizeof(ledc_timer_config_t));
ledc_timer.speed_mode = group;
ledc_timer.timer_num = timer;
ledc_timer.duty_resolution = resolution;
ledc_timer.freq_hz = freq;
ledc_timer.clk_cfg = clock_source;
if (ledc_timer_config(&ledc_timer) != ESP_OK) {
log_e("ledc setup failed!");
return false;
}
}
uint32_t duty = ledc_get_duty(group, (channel % SOC_LEDC_CHANNEL_NUM));
ledc_channel_config_t ledc_channel;
memset((void *)&ledc_channel, 0, sizeof(ledc_channel_config_t));
ledc_channel.speed_mode = group;
ledc_channel.channel = (channel % SOC_LEDC_CHANNEL_NUM);
ledc_channel.timer_sel = timer;
ledc_channel.intr_type = LEDC_INTR_DISABLE;
ledc_channel.gpio_num = pin;
ledc_channel.duty = duty;
ledc_channel.hpoint = 0;
ledc_channel_config(&ledc_channel);
}
ledc_channel_handle_t *handle = (ledc_channel_handle_t *)malloc(sizeof(ledc_channel_handle_t));
handle->pin = pin;
handle->channel = channel;
handle->timer_num = timer;
handle->freq_hz = freq;
#ifndef SOC_LEDC_SUPPORT_FADE_STOP
handle->lock = NULL;
#endif
//get resolution of selected channel when used
if (channel_used) {
uint32_t channel_resolution = 0;
ledc_ll_get_duty_resolution(LEDC_LL_GET_HW(), group, timer, &channel_resolution);
log_i("Channel %u frequency: %u, resolution: %u", channel, ledc_get_freq(group, timer), channel_resolution);
handle->channel_resolution = (uint8_t)channel_resolution;
} else {
handle->channel_resolution = resolution;
ledc_handle.used_channels |= 1UL << channel;
}
if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_LEDC, (void *)handle, channel, timer)) {
ledcDetachBus((void *)handle);
return false;
}
log_i("LEDC attached to pin %u (channel %u, resolution %u)", pin, channel, resolution);
return true;
}
bool ledcAttach(uint8_t pin, uint32_t freq, uint8_t resolution) {
int free_channel = ~ledc_handle.used_channels & (ledc_handle.used_channels + 1);
if (free_channel == 0) {
log_e("No more LEDC channels available! (maximum is %u channels)", LEDC_CHANNELS);
return false;
}
uint8_t channel = __builtin_ctz(free_channel); // Convert the free_channel bit to channel number
// Try the first available channel
if (ledcAttachChannel(pin, freq, resolution, channel)) {
return true;
}
#ifdef SOC_LEDC_SUPPORT_HS_MODE
// If first attempt failed and HS mode is supported, try to find a free channel in group 1
if ((channel / SOC_LEDC_CHANNEL_NUM) == 0) { // First attempt was in group 0
log_d("LEDC: Group 0 channel %u failed, trying to find a free channel in group 1", channel);
// Find free channels specifically in group 1
uint32_t group1_mask = ((1UL << SOC_LEDC_CHANNEL_NUM) - 1) << SOC_LEDC_CHANNEL_NUM;
int group1_free_channel = (~ledc_handle.used_channels) & group1_mask;
if (group1_free_channel != 0) {
uint8_t group1_channel = __builtin_ctz(group1_free_channel);
if (ledcAttachChannel(pin, freq, resolution, group1_channel)) {
return true;
}
}
}
#endif
log_e(
"No free timers available for freq=%u, resolution=%u. To attach a new channel, use the same frequency and resolution as an already attached channel to "
"share its timer.",
freq, resolution
);
return false;
}
bool ledcWrite(uint8_t pin, uint32_t duty) {
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
if (bus != NULL) {
uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM), channel = (bus->channel % SOC_LEDC_CHANNEL_NUM);
//Fixing if all bits in resolution is set = LEDC FULL ON
uint32_t max_duty = (1 << bus->channel_resolution) - 1;
if ((duty == max_duty) && (max_duty != 1)) {
duty = max_duty + 1;
}
if (ledc_set_duty(group, channel, duty) != ESP_OK) {
log_e("ledc_set_duty failed");
return false;
}
if (ledc_update_duty(group, channel) != ESP_OK) {
log_e("ledc_update_duty failed");
return false;
}
return true;
}
return false;
}
bool ledcWriteChannel(uint8_t channel, uint32_t duty) {
//check if channel is valid and used
if (channel >= LEDC_CHANNELS || !(ledc_handle.used_channels & (1UL << channel))) {
log_e("Channel %u is not available (maximum %u) or not used!", channel, LEDC_CHANNELS);
return false;
}
uint8_t group = (channel / SOC_LEDC_CHANNEL_NUM);
ledc_timer_t timer;
// Get the actual timer being used by this channel
ledc_ll_get_channel_timer(LEDC_LL_GET_HW(), group, (channel % SOC_LEDC_CHANNEL_NUM), &timer);
//Fixing if all bits in resolution is set = LEDC FULL ON
uint32_t resolution = 0;
ledc_ll_get_duty_resolution(LEDC_LL_GET_HW(), group, timer, &resolution);
uint32_t max_duty = (1 << resolution) - 1;
if ((duty == max_duty) && (max_duty != 1)) {
duty = max_duty + 1;
}
if (ledc_set_duty(group, channel, duty) != ESP_OK) {
log_e("ledc_set_duty failed");
return false;
}
if (ledc_update_duty(group, channel) != ESP_OK) {
log_e("ledc_update_duty failed");
return false;
}
return true;
}
uint32_t ledcRead(uint8_t pin) {
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
if (bus != NULL) {
uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM), channel = (bus->channel % SOC_LEDC_CHANNEL_NUM);
return ledc_get_duty(group, channel);
}
return 0;
}
uint32_t ledcReadFreq(uint8_t pin) {
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
if (bus != NULL) {
if (!ledcRead(pin)) {
return 0;
}
uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM);
return ledc_get_freq(group, bus->timer_num);
}
return 0;
}
uint32_t ledcWriteTone(uint8_t pin, uint32_t freq) {
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
if (bus != NULL) {
if (!freq) {
ledcWrite(pin, 0);
return 0;
}
uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM);
ledc_timer_config_t ledc_timer;
memset((void *)&ledc_timer, 0, sizeof(ledc_timer_config_t));
ledc_timer.speed_mode = group;
ledc_timer.timer_num = bus->timer_num;
ledc_timer.duty_resolution = 10;
ledc_timer.freq_hz = freq;
ledc_timer.clk_cfg = clock_source;
if (ledc_timer_config(&ledc_timer) != ESP_OK) {
log_e("ledcWriteTone configuration failed!");
return 0;
}
bus->channel_resolution = 10;
uint32_t res_freq = ledc_get_freq(group, bus->timer_num);
ledcWrite(pin, 0x1FF);
return res_freq;
}
return 0;
}
uint32_t ledcWriteNote(uint8_t pin, note_t note, uint8_t octave) {
const uint16_t noteFrequencyBase[12] = {// C C# D Eb E F F# G G# A Bb B
4186, 4435, 4699, 4978, 5274, 5588, 5920, 6272, 6645, 7040, 7459, 7902
};
if (octave > 8 || note >= NOTE_MAX) {
return 0;
}
uint32_t noteFreq = (uint32_t)noteFrequencyBase[note] / (uint32_t)(1 << (8 - octave));
return ledcWriteTone(pin, noteFreq);
}
bool ledcDetach(uint8_t pin) {
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
if (bus != NULL) {
// will call ledcDetachBus
return perimanClearPinBus(pin);
} else {
log_e("pin %u is not attached to LEDC", pin);
}
return false;
}
uint32_t ledcChangeFrequency(uint8_t pin, uint32_t freq, uint8_t resolution) {
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
if (bus != NULL) {
if (freq == 0) {
log_e("LEDC pin %u - frequency can't be zero.", pin);
return 0;
}
if (resolution == 0 || resolution > LEDC_MAX_BIT_WIDTH) {
log_e("LEDC pin %u - resolution is zero or it is too big (maximum %u)", pin, LEDC_MAX_BIT_WIDTH);
return 0;
}
uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM);
ledc_timer_config_t ledc_timer;
memset((void *)&ledc_timer, 0, sizeof(ledc_timer_config_t));
ledc_timer.speed_mode = group;
ledc_timer.timer_num = bus->timer_num;
ledc_timer.duty_resolution = resolution;
ledc_timer.freq_hz = freq;
ledc_timer.clk_cfg = clock_source;
if (ledc_timer_config(&ledc_timer) != ESP_OK) {
log_e("ledcChangeFrequency failed!");
return 0;
}
bus->channel_resolution = resolution;
return ledc_get_freq(group, bus->timer_num);
}
return 0;
}
bool ledcOutputInvert(uint8_t pin, bool out_invert) {
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
if (bus != NULL) {
gpio_set_level(pin, out_invert);
#ifdef CONFIG_IDF_TARGET_ESP32P4
esp_rom_gpio_connect_out_signal(pin, LEDC_LS_SIG_OUT_PAD_OUT0_IDX + ((bus->channel) % SOC_LEDC_CHANNEL_NUM), out_invert, 0);
#else
#ifdef SOC_LEDC_SUPPORT_HS_MODE
esp_rom_gpio_connect_out_signal(
pin, ((bus->channel / SOC_LEDC_CHANNEL_NUM == 0) ? LEDC_HS_SIG_OUT0_IDX : LEDC_LS_SIG_OUT0_IDX) + ((bus->channel) % SOC_LEDC_CHANNEL_NUM), out_invert, 0
);
#else
esp_rom_gpio_connect_out_signal(pin, LEDC_LS_SIG_OUT0_IDX + ((bus->channel) % SOC_LEDC_CHANNEL_NUM), out_invert, 0);
#endif
#endif // ifdef CONFIG_IDF_TARGET_ESP32P4
return true;
}
return false;
}
static IRAM_ATTR bool ledcFnWrapper(const ledc_cb_param_t *param, void *user_arg) {
if (param->event == LEDC_FADE_END_EVT) {
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)user_arg;
#ifndef SOC_LEDC_SUPPORT_FADE_STOP
portBASE_TYPE xTaskWoken = 0;
xSemaphoreGiveFromISR(bus->lock, &xTaskWoken);
#endif
if (bus->fn) {
if (bus->arg) {
((voidFuncPtrArg)bus->fn)(bus->arg);
} else {
bus->fn();
}
}
}
return true;
}
static bool ledcFadeConfig(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void *), void *arg) {
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
if (bus != NULL) {
#ifndef SOC_LEDC_SUPPORT_FADE_STOP
#if !CONFIG_DISABLE_HAL_LOCKS
if (bus->lock == NULL) {
bus->lock = xSemaphoreCreateBinary();
if (bus->lock == NULL) {
log_e("xSemaphoreCreateBinary failed");
return false;
}
xSemaphoreGive(bus->lock);
}
//acquire lock
if (xSemaphoreTake(bus->lock, 0) != pdTRUE) {
log_e("LEDC Fade is still running on pin %u! SoC does not support stopping fade.", pin);
return false;
}
#endif
#endif
uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM), channel = (bus->channel % SOC_LEDC_CHANNEL_NUM);
// Initialize fade service.
if (!fade_initialized) {
ledc_fade_func_install(0);
fade_initialized = true;
}
bus->fn = (voidFuncPtr)userFunc;
bus->arg = arg;
ledc_cbs_t callbacks = {.fade_cb = ledcFnWrapper};
ledc_cb_register(group, channel, &callbacks, (void *)bus);
//Fixing if all bits in resolution is set = LEDC FULL ON
uint32_t max_duty = (1 << bus->channel_resolution) - 1;
if ((target_duty == max_duty) && (max_duty != 1)) {
target_duty = max_duty + 1;
} else if ((start_duty == max_duty) && (max_duty != 1)) {
start_duty = max_duty + 1;
}
#if SOC_LEDC_SUPPORT_FADE_STOP
ledc_fade_stop(group, channel);
#endif
if (ledc_set_duty_and_update(group, channel, start_duty, 0) != ESP_OK) {
log_e("ledc_set_duty_and_update failed");
return false;
}
// Wait for LEDCs next PWM cycle to update duty (~ 1-2 ms)
while (ledc_get_duty(group, channel) != start_duty);
if (ledc_set_fade_time_and_start(group, channel, target_duty, max_fade_time_ms, LEDC_FADE_NO_WAIT) != ESP_OK) {
log_e("ledc_set_fade_time_and_start failed");
return false;
}
} else {
log_e("Pin %u is not attached to LEDC. Call ledcAttach first!", pin);
return false;
}
return true;
}
bool ledcFade(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms) {
return ledcFadeConfig(pin, start_duty, target_duty, max_fade_time_ms, NULL, NULL);
}
bool ledcFadeWithInterrupt(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, voidFuncPtr userFunc) {
return ledcFadeConfig(pin, start_duty, target_duty, max_fade_time_ms, (voidFuncPtrArg)userFunc, NULL);
}
bool ledcFadeWithInterruptArg(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void *), void *arg) {
return ledcFadeConfig(pin, start_duty, target_duty, max_fade_time_ms, userFunc, arg);
}
#ifdef SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED
// Default gamma factor for gamma correction (common value for LEDs)
static float ledcGammaFactor = 2.8;
// Gamma correction LUT support
static const float *ledcGammaLUT = NULL;
static uint16_t ledcGammaLUTSize = 0;
// Global variable to store current resolution for gamma callback
static uint8_t ledcGammaResolution = 13;
bool ledcSetGammaTable(const float *gamma_table, uint16_t size) {
if (gamma_table == NULL || size == 0) {
log_e("Invalid gamma table or size");
return false;
}
ledcGammaLUT = gamma_table;
ledcGammaLUTSize = size;
log_i("Custom gamma LUT set with %u entries", size);
return true;
}
void ledcClearGammaTable(void) {
ledcGammaLUT = NULL;
ledcGammaLUTSize = 0;
log_i("Gamma LUT cleared, using mathematical calculation");
}
void ledcSetGammaFactor(float factor) {
ledcGammaFactor = factor;
}
// Gamma correction calculator function
static uint32_t ledcGammaCorrection(uint32_t duty) {
if (duty == 0) {
return 0;
}
uint32_t max_duty = (1U << ledcGammaResolution) - 1;
if (duty >= (1U << ledcGammaResolution)) {
return max_duty;
}
// Use LUT if provided, otherwise use mathematical calculation
if (ledcGammaLUT != NULL && ledcGammaLUTSize > 0) {
// LUT-based gamma correction
uint32_t lut_index = (duty * (ledcGammaLUTSize - 1)) / max_duty;
if (lut_index >= ledcGammaLUTSize) {
lut_index = ledcGammaLUTSize - 1;
}
float corrected_normalized = ledcGammaLUT[lut_index];
return (uint32_t)(corrected_normalized * max_duty);
} else {
// Mathematical gamma correction
double normalized = (double)duty / (1U << ledcGammaResolution);
double corrected = pow(normalized, ledcGammaFactor);
return (uint32_t)(corrected * (1U << ledcGammaResolution));
}
}
static bool ledcFadeGammaConfig(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void *), void *arg) {
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
if (bus != NULL) {
#ifndef SOC_LEDC_SUPPORT_FADE_STOP
#if !CONFIG_DISABLE_HAL_LOCKS
if (bus->lock == NULL) {
bus->lock = xSemaphoreCreateBinary();
if (bus->lock == NULL) {
log_e("xSemaphoreCreateBinary failed");
return false;
}
xSemaphoreGive(bus->lock);
}
//acquire lock
if (xSemaphoreTake(bus->lock, 0) != pdTRUE) {
log_e("LEDC Fade is still running on pin %u! SoC does not support stopping fade.", pin);
return false;
}
#endif
#endif
uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM), channel = (bus->channel % SOC_LEDC_CHANNEL_NUM);
// Initialize fade service.
if (!fade_initialized) {
ledc_fade_func_install(0);
fade_initialized = true;
}
bus->fn = (voidFuncPtr)userFunc;
bus->arg = arg;
ledc_cbs_t callbacks = {.fade_cb = ledcFnWrapper};
ledc_cb_register(group, channel, &callbacks, (void *)bus);
// Prepare gamma curve fade parameters
ledc_fade_param_config_t fade_params[SOC_LEDC_GAMMA_CURVE_FADE_RANGE_MAX];
uint32_t actual_fade_ranges = 0;
// Use a moderate number of linear segments for smooth gamma curve
const uint32_t linear_fade_segments = 12;
// Set the global resolution for gamma correction
ledcGammaResolution = bus->channel_resolution;
// Fill multi-fade parameter list using ESP-IDF API
esp_err_t err = ledc_fill_multi_fade_param_list(
group, channel, start_duty, target_duty, linear_fade_segments, max_fade_time_ms, ledcGammaCorrection, SOC_LEDC_GAMMA_CURVE_FADE_RANGE_MAX, fade_params,
&actual_fade_ranges
);
if (err != ESP_OK) {
log_e("ledc_fill_multi_fade_param_list failed: %s", esp_err_to_name(err));
return false;
}
// Apply the gamma-corrected start duty
uint32_t gamma_start_duty = ledcGammaCorrection(start_duty);
// Set multi-fade parameters
err = ledc_set_multi_fade(group, channel, gamma_start_duty, fade_params, actual_fade_ranges);
if (err != ESP_OK) {
log_e("ledc_set_multi_fade failed: %s", esp_err_to_name(err));
return false;
}
// Start the gamma curve fade
err = ledc_fade_start(group, channel, LEDC_FADE_NO_WAIT);
if (err != ESP_OK) {
log_e("ledc_fade_start failed: %s", esp_err_to_name(err));
return false;
}
log_d("Gamma curve fade started on pin %u: %u -> %u over %dms", pin, start_duty, target_duty, max_fade_time_ms);
} else {
log_e("Pin %u is not attached to LEDC. Call ledcAttach first!", pin);
return false;
}
return true;
}
bool ledcFadeGamma(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms) {
return ledcFadeGammaConfig(pin, start_duty, target_duty, max_fade_time_ms, NULL, NULL);
}
bool ledcFadeGammaWithInterrupt(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, voidFuncPtr userFunc) {
return ledcFadeGammaConfig(pin, start_duty, target_duty, max_fade_time_ms, (voidFuncPtrArg)userFunc, NULL);
}
bool ledcFadeGammaWithInterruptArg(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void *), void *arg) {
return ledcFadeGammaConfig(pin, start_duty, target_duty, max_fade_time_ms, userFunc, arg);
}
#endif /* SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED */
static uint8_t analog_resolution = 8;
static int analog_frequency = 1000;
void analogWrite(uint8_t pin, int value) {
// Use ledc hardware for internal pins
if (pin < SOC_GPIO_PIN_COUNT) {
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
if (bus == NULL && perimanClearPinBus(pin)) {
if (ledcAttach(pin, analog_frequency, analog_resolution) == 0) {
log_e("analogWrite setup failed (freq = %u, resolution = %u). Try setting different resolution or frequency");
return;
}
}
ledcWrite(pin, value);
}
}
void analogWriteFrequency(uint8_t pin, uint32_t freq) {
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
if (bus != NULL) { // if pin is attached to LEDC change frequency, otherwise update the global frequency
if (ledcChangeFrequency(pin, freq, analog_resolution) == 0) {
log_e("analogWrite frequency cant be set due to selected resolution! Try to adjust resolution first");
return;
}
}
analog_frequency = freq;
}
void analogWriteResolution(uint8_t pin, uint8_t resolution) {
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
if (bus != NULL) { // if pin is attached to LEDC change resolution, otherwise update the global resolution
if (ledcChangeFrequency(pin, analog_frequency, resolution) == 0) {
log_e("analogWrite resolution cant be set due to selected frequency! Try to adjust frequency first");
return;
}
}
analog_resolution = resolution;
}
#endif /* SOC_LEDC_SUPPORTED */
+319
View File
@@ -0,0 +1,319 @@
// Copyright 2015-2023 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP32_HAL_LEDC_H_
#define _ESP32_HAL_LEDC_H_
#include "soc/soc_caps.h"
#if SOC_LEDC_SUPPORTED
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "hal/ledc_types.h"
typedef enum {
NOTE_C,
NOTE_Cs,
NOTE_D,
NOTE_Eb,
NOTE_E,
NOTE_F,
NOTE_Fs,
NOTE_G,
NOTE_Gs,
NOTE_A,
NOTE_Bb,
NOTE_B,
NOTE_MAX
} note_t;
typedef void (*voidFuncPtr)(void);
typedef void (*voidFuncPtrArg)(void *);
typedef struct {
uint8_t pin; // Pin assigned to channel
uint8_t channel; // Channel number
uint8_t channel_resolution; // Resolution of channel
uint8_t timer_num; // Timer number used by this channel
uint32_t freq_hz; // Frequency configured for this channel
voidFuncPtr fn;
void *arg;
#ifndef SOC_LEDC_SUPPORT_FADE_STOP
SemaphoreHandle_t lock; //xSemaphoreCreateBinary
#endif
} ledc_channel_handle_t;
/**
* @brief Get the LEDC clock source.
*
* @return LEDC clock source.
*/
ledc_clk_cfg_t ledcGetClockSource(void);
/**
* @brief Set the LEDC clock source.
*
* @param source LEDC clock source to set.
*
* @return true if LEDC clock source was successfully set, false otherwise.
*/
bool ledcSetClockSource(ledc_clk_cfg_t source);
/**
* @brief Attach a pin to the LEDC driver, with a given frequency and resolution.
* Channel is automatically assigned.
*
* @param pin GPIO pin
* @param freq frequency of PWM signal
* @param resolution resolution for LEDC pin
*
* @return true if configuration is successful and pin was successfully attached, false otherwise.
*/
bool ledcAttach(uint8_t pin, uint32_t freq, uint8_t resolution);
/**
* @brief Attach a pin to the LEDC driver, with a given frequency, resolution and channel.
*
* @param pin GPIO pin
* @param freq frequency of PWM signal
* @param resolution resolution for LEDC pin
* @param channel LEDC channel to attach to
*
* @return true if configuration is successful and pin was successfully attached, false otherwise.
*/
bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t channel);
/**
* @brief Set the duty cycle of a given pin.
*
* @param pin GPIO pin
* @param duty duty cycle to set
*
* @return true if duty cycle was successfully set, false otherwise.
*/
bool ledcWrite(uint8_t pin, uint32_t duty);
/**
* @brief Set the duty cycle of a given channel.
*
* @param channel LEDC channel
* @param duty duty cycle to set
*
* @return true if duty cycle was successfully set, false otherwise.
*/
bool ledcWriteChannel(uint8_t channel, uint32_t duty);
/**
* @brief Sets the duty to 50 % PWM tone on selected frequency.
*
* @param pin GPIO pin
* @param freq select frequency of pwm signal. If frequency is 0, duty will be set to 0.
*
* @return frequency if tone was successfully set.
* If ``0`` is returned, error occurs and LEDC pin was not configured.
*/
uint32_t ledcWriteTone(uint8_t pin, uint32_t freq);
/**
* @brief Sets the LEDC pin to specific note.
*
* @param pin GPIO pin
* @param note select note to be set (NOTE_C, NOTE_Cs, NOTE_D, NOTE_Eb, NOTE_E, NOTE_F, NOTE_Fs, NOTE_G, NOTE_Gs, NOTE_A, NOTE_Bb, NOTE_B).
* @param octave select octave for note.
*
* @return frequency if note was successfully set.
* If ``0`` is returned, error occurs and LEDC pin was not configured.
*/
uint32_t ledcWriteNote(uint8_t pin, note_t note, uint8_t octave);
/**
* @brief Read the duty cycle of a given LEDC pin.
*
* @param pin GPIO pin
*
* @return duty cycle of selected LEDC pin.
*/
uint32_t ledcRead(uint8_t pin);
/**
* @brief Read the frequency of a given LEDC pin.
*
* @param pin GPIO pin
*
* @return frequency of selected LEDC pin.
*/
uint32_t ledcReadFreq(uint8_t pin);
/**
* @brief Detach a pin from the LEDC driver.
*
* @param pin GPIO pin
*
* @return true if pin was successfully detached, false otherwise.
*/
bool ledcDetach(uint8_t pin);
/**
* @brief Change the frequency and resolution of a given LEDC pin.
*
* @param pin GPIO pin
* @param freq frequency of PWM signal
* @param resolution resolution for LEDC pin
*
* @return frequency configured for the LEDC channel.
* If ``0`` is returned, error occurs and LEDC pin was not configured.
*/
uint32_t ledcChangeFrequency(uint8_t pin, uint32_t freq, uint8_t resolution);
/**
* @brief Sets inverting of the output signal for a given LEDC pin.
*
* @param pin GPIO pin
* @param out_invert select, if output should be inverted (true = inverting output).
*
* @return true if output inverting was successfully set, false otherwise.
*/
bool ledcOutputInvert(uint8_t pin, bool out_invert);
//Fade functions
/**
* @brief Setup and start a fade on a given LEDC pin.
*
* @param pin GPIO pin
* @param start_duty initial duty cycle of the fade
* @param target_duty target duty cycle of the fade
* @param max_fade_time_ms maximum fade time in milliseconds
*
* @return true if fade was successfully set and started, false otherwise.
*/
bool ledcFade(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms);
/**
* @brief Setup and start a fade on a given LEDC pin with a callback function.
*
* @param pin GPIO pin
* @param start_duty initial duty cycle of the fade
* @param target_duty target duty cycle of the fade
* @param max_fade_time_ms maximum fade time in milliseconds
* @param userFunc callback function to be called after fade is finished
*
* @return true if fade was successfully set and started, false otherwise.
*/
bool ledcFadeWithInterrupt(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void));
/**
* @brief Setup and start a fade on a given LEDC pin with a callback function and argument.
*
* @param pin GPIO pin
* @param start_duty initial duty cycle of the fade
* @param target_duty target duty cycle of the fade
* @param max_fade_time_ms maximum fade time in milliseconds
* @param userFunc callback function to be called after fade is finished
* @param arg argument to be passed to the callback function
*
* @return true if fade was successfully set and started, false otherwise.
*/
bool ledcFadeWithInterruptArg(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void *), void *arg);
//Gamma Curve Fade functions - only available on supported chips
#ifdef SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED
/**
* @brief Set a custom gamma correction lookup table for gamma curve fading.
* The LUT should contain normalized values (0.0 to 1.0) representing
* the gamma-corrected brightness curve.
*
* @param gamma_table Pointer to array of float values (0.0 to 1.0)
* @param size Number of entries in the lookup table
*
* @return true if gamma table was successfully set, false otherwise.
*
* @note The LUT array must remain valid for as long as gamma fading is used.
* Larger tables provide smoother transitions but use more memory.
*/
bool ledcSetGammaTable(const float *gamma_table, uint16_t size);
/**
* @brief Clear the current gamma correction lookup table.
* After calling this, gamma correction will use mathematical
* calculation with the default gamma factor (2.8).
*/
void ledcClearGammaTable(void);
/**
* @brief Set the gamma factor for gamma correction.
*
* @param factor Gamma factor to use for gamma correction.
*/
void ledcSetGammaFactor(float factor);
/**
* @brief Setup and start a gamma curve fade on a given LEDC pin.
* Gamma correction makes LED brightness changes appear more gradual to human eyes.
*
* @param pin GPIO pin
* @param start_duty initial duty cycle of the fade
* @param target_duty target duty cycle of the fade
* @param max_fade_time_ms maximum fade time in milliseconds
*
* @return true if gamma fade was successfully set and started, false otherwise.
*
* @note This function is only available on ESP32 variants that support gamma curve fading.
*/
bool ledcFadeGamma(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms);
/**
* @brief Setup and start a gamma curve fade on a given LEDC pin with a callback function.
*
* @param pin GPIO pin
* @param start_duty initial duty cycle of the fade
* @param target_duty target duty cycle of the fade
* @param max_fade_time_ms maximum fade time in milliseconds
* @param userFunc callback function to be called after fade is finished
*
* @return true if gamma fade was successfully set and started, false otherwise.
*
* @note This function is only available on ESP32 variants that support gamma curve fading.
*/
bool ledcFadeGammaWithInterrupt(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void));
/**
* @brief Setup and start a gamma curve fade on a given LEDC pin with a callback function and argument.
*
* @param pin GPIO pin
* @param start_duty initial duty cycle of the fade
* @param target_duty target duty cycle of the fade
* @param max_fade_time_ms maximum fade time in milliseconds
* @param userFunc callback function to be called after fade is finished
* @param arg argument to be passed to the callback function
*
* @return true if gamma fade was successfully set and started, false otherwise.
*
* @note This function is only available on ESP32 variants that support gamma curve fading.
*/
bool ledcFadeGammaWithInterruptArg(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void *), void *arg);
#endif // SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED
#ifdef __cplusplus
}
#endif
#endif /* SOC_LEDC_SUPPORTED */
#endif /* _ESP32_HAL_LEDC_H_ */
+42
View File
@@ -0,0 +1,42 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
/*
* This file provides wrapper implementations for esp_log_write and esp_log_writev
* when CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP is not enabled.
*
* When CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP is enabled, the esp_diagnostics component
* provides these wrappers. However, when it's disabled, WiFi libraries still expect
* these wrapper functions to exist, causing linker errors.
*
* This implementation provides simple pass-through wrappers that call the real
* ESP-IDF logging functions, ensuring compatibility without requiring esp_diagnostics.
*/
#ifndef CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP
#include <stdarg.h>
#include "esp_log.h"
// Declare the real functions that will be wrapped by the linker
void __real_esp_log_write(esp_log_level_t level, const char *tag, const char *format, ...);
void __real_esp_log_writev(esp_log_level_t level, const char *tag, const char *format, va_list args);
// Wrapper implementations that simply call through to the real functions
void __wrap_esp_log_write(esp_log_level_t level, const char *tag, const char *format, ...) {
va_list args;
va_start(args, format);
__real_esp_log_writev(level, tag, format, args);
va_end(args);
}
void __wrap_esp_log_writev(esp_log_level_t level, const char *tag, const char *format, va_list args) {
__real_esp_log_writev(level, tag, format, args);
}
#endif // !CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP
+349
View File
@@ -0,0 +1,349 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __ARDUHAL_LOG_H__
#define __ARDUHAL_LOG_H__
#ifdef __cplusplus
extern "C" {
#endif
#include "sdkconfig.h"
#include "esp_timer.h"
#include "rom/ets_sys.h"
#define ARDUHAL_LOG_LEVEL_NONE (0)
#define ARDUHAL_LOG_LEVEL_ERROR (1)
#define ARDUHAL_LOG_LEVEL_WARN (2)
#define ARDUHAL_LOG_LEVEL_INFO (3)
#define ARDUHAL_LOG_LEVEL_DEBUG (4)
#define ARDUHAL_LOG_LEVEL_VERBOSE (5)
#ifndef CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL
#define CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL ARDUHAL_LOG_LEVEL_NONE
#endif
#ifndef CORE_DEBUG_LEVEL
#define ARDUHAL_LOG_LEVEL CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL
#else
#define ARDUHAL_LOG_LEVEL CORE_DEBUG_LEVEL
#ifdef USE_ESP_IDF_LOG
#ifndef LOG_LOCAL_LEVEL
#define LOG_LOCAL_LEVEL CORE_DEBUG_LEVEL
#endif
#endif
#endif
#ifndef CONFIG_ARDUHAL_LOG_COLORS
#define CONFIG_ARDUHAL_LOG_COLORS 0
#endif
#if CONFIG_ARDUHAL_LOG_COLORS
#define ARDUHAL_LOG_COLOR_BLACK "30"
#define ARDUHAL_LOG_COLOR_RED "31" //ERROR
#define ARDUHAL_LOG_COLOR_GREEN "32" //INFO
#define ARDUHAL_LOG_COLOR_YELLOW "33" //WARNING
#define ARDUHAL_LOG_COLOR_BLUE "34"
#define ARDUHAL_LOG_COLOR_MAGENTA "35"
#define ARDUHAL_LOG_COLOR_CYAN "36" //DEBUG
#define ARDUHAL_LOG_COLOR_GRAY "37" //VERBOSE
#define ARDUHAL_LOG_COLOR_WHITE "38"
#define ARDUHAL_LOG_COLOR(COLOR) "\033[0;" COLOR "m"
#define ARDUHAL_LOG_BOLD(COLOR) "\033[1;" COLOR "m"
#define ARDUHAL_LOG_RESET_COLOR "\033[0m"
#define ARDUHAL_LOG_COLOR_E ARDUHAL_LOG_COLOR(ARDUHAL_LOG_COLOR_RED)
#define ARDUHAL_LOG_COLOR_W ARDUHAL_LOG_COLOR(ARDUHAL_LOG_COLOR_YELLOW)
#define ARDUHAL_LOG_COLOR_I ARDUHAL_LOG_COLOR(ARDUHAL_LOG_COLOR_GREEN)
#define ARDUHAL_LOG_COLOR_D ARDUHAL_LOG_COLOR(ARDUHAL_LOG_COLOR_CYAN)
#define ARDUHAL_LOG_COLOR_V ARDUHAL_LOG_COLOR(ARDUHAL_LOG_COLOR_GRAY)
#define ARDUHAL_LOG_COLOR_PRINT(letter) log_printf(ARDUHAL_LOG_COLOR_##letter)
#define ARDUHAL_LOG_COLOR_PRINT_END log_printf(ARDUHAL_LOG_RESET_COLOR)
#else
#define ARDUHAL_LOG_COLOR_E
#define ARDUHAL_LOG_COLOR_W
#define ARDUHAL_LOG_COLOR_I
#define ARDUHAL_LOG_COLOR_D
#define ARDUHAL_LOG_COLOR_V
#define ARDUHAL_LOG_RESET_COLOR
#define ARDUHAL_LOG_COLOR_PRINT(letter)
#define ARDUHAL_LOG_COLOR_PRINT_END
#endif
#ifdef USE_ESP_IDF_LOG
#ifndef ARDUHAL_ESP_LOG_TAG
#define ARDUHAL_ESP_LOG_TAG "ARDUINO"
#endif
#endif
const char *pathToFileName(const char *path);
int log_printf(const char *fmt, ...);
void log_print_buf(const uint8_t *b, size_t len);
#define ARDUHAL_SHORT_LOG_FORMAT(letter, format) ARDUHAL_LOG_COLOR_##letter format ARDUHAL_LOG_RESET_COLOR "\r\n"
#define ARDUHAL_LOG_FORMAT(letter, format) \
ARDUHAL_LOG_COLOR_##letter "[%6u][" #letter "][%s:%u] %s(): " format ARDUHAL_LOG_RESET_COLOR "\r\n", (unsigned long)(esp_timer_get_time() / 1000ULL), \
pathToFileName(__FILE__), __LINE__, __FUNCTION__
//esp_rom_printf(DRAM_STR("ST:%d\n"), frame_pos);
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE
#ifndef USE_ESP_IDF_LOG
#define log_v(format, ...) log_printf(ARDUHAL_LOG_FORMAT(V, format), ##__VA_ARGS__)
#define isr_log_v(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(V, format), ##__VA_ARGS__)
#define log_buf_v(b, l) \
do { \
ARDUHAL_LOG_COLOR_PRINT(V); \
log_print_buf(b, l); \
ARDUHAL_LOG_COLOR_PRINT_END; \
} while (0)
#else
#define log_v(format, ...) \
do { \
ESP_LOG_LEVEL_LOCAL(ESP_LOG_VERBOSE, ARDUHAL_ESP_LOG_TAG, format, ##__VA_ARGS__); \
} while (0)
#define isr_log_v(format, ...) \
do { \
ets_printf(LOG_FORMAT(V, format), esp_log_timestamp(), ARDUHAL_ESP_LOG_TAG, ##__VA_ARGS__); \
} while (0)
#define log_buf_v(b, l) \
do { \
ESP_LOG_BUFFER_HEXDUMP(ARDUHAL_ESP_LOG_TAG, b, l, ESP_LOG_VERBOSE); \
} while (0)
#endif
#else
#define log_v(format, ...) \
do { \
} while (0)
#define isr_log_v(format, ...) \
do { \
} while (0)
#define log_buf_v(b, l) \
do { \
} while (0)
#endif
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG
#ifndef USE_ESP_IDF_LOG
#define log_d(format, ...) log_printf(ARDUHAL_LOG_FORMAT(D, format), ##__VA_ARGS__)
#define isr_log_d(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(D, format), ##__VA_ARGS__)
#define log_buf_d(b, l) \
do { \
ARDUHAL_LOG_COLOR_PRINT(D); \
log_print_buf(b, l); \
ARDUHAL_LOG_COLOR_PRINT_END; \
} while (0)
#else
#define log_d(format, ...) \
do { \
ESP_LOG_LEVEL_LOCAL(ESP_LOG_DEBUG, ARDUHAL_ESP_LOG_TAG, format, ##__VA_ARGS__); \
} while (0)
#define isr_log_d(format, ...) \
do { \
ets_printf(LOG_FORMAT(D, format), esp_log_timestamp(), ARDUHAL_ESP_LOG_TAG, ##__VA_ARGS__); \
} while (0)
#define log_buf_d(b, l) \
do { \
ESP_LOG_BUFFER_HEXDUMP(ARDUHAL_ESP_LOG_TAG, b, l, ESP_LOG_DEBUG); \
} while (0)
#endif
#else
#define log_d(format, ...) \
do { \
} while (0)
#define isr_log_d(format, ...) \
do { \
} while (0)
#define log_buf_d(b, l) \
do { \
} while (0)
#endif
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO
#ifndef USE_ESP_IDF_LOG
#define log_i(format, ...) log_printf(ARDUHAL_LOG_FORMAT(I, format), ##__VA_ARGS__)
#define isr_log_i(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(I, format), ##__VA_ARGS__)
#define log_buf_i(b, l) \
do { \
ARDUHAL_LOG_COLOR_PRINT(I); \
log_print_buf(b, l); \
ARDUHAL_LOG_COLOR_PRINT_END; \
} while (0)
#else
#define log_i(format, ...) \
do { \
ESP_LOG_LEVEL_LOCAL(ESP_LOG_INFO, ARDUHAL_ESP_LOG_TAG, format, ##__VA_ARGS__); \
} while (0)
#define isr_log_i(format, ...) \
do { \
ets_printf(LOG_FORMAT(I, format), esp_log_timestamp(), ARDUHAL_ESP_LOG_TAG, ##__VA_ARGS__); \
} while (0)
#define log_buf_i(b, l) \
do { \
ESP_LOG_BUFFER_HEXDUMP(ARDUHAL_ESP_LOG_TAG, b, l, ESP_LOG_INFO); \
} while (0)
#endif
#else
#define log_i(format, ...) \
do { \
} while (0)
#define isr_log_i(format, ...) \
do { \
} while (0)
#define log_buf_i(b, l) \
do { \
} while (0)
#endif
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_WARN
#ifndef USE_ESP_IDF_LOG
#define log_w(format, ...) log_printf(ARDUHAL_LOG_FORMAT(W, format), ##__VA_ARGS__)
#define isr_log_w(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(W, format), ##__VA_ARGS__)
#define log_buf_w(b, l) \
do { \
ARDUHAL_LOG_COLOR_PRINT(W); \
log_print_buf(b, l); \
ARDUHAL_LOG_COLOR_PRINT_END; \
} while (0)
#else
#define log_w(format, ...) \
do { \
ESP_LOG_LEVEL_LOCAL(ESP_LOG_WARN, ARDUHAL_ESP_LOG_TAG, format, ##__VA_ARGS__); \
} while (0)
#define isr_log_w(format, ...) \
do { \
ets_printf(LOG_FORMAT(W, format), esp_log_timestamp(), ARDUHAL_ESP_LOG_TAG, ##__VA_ARGS__); \
} while (0)
#define log_buf_w(b, l) \
do { \
ESP_LOG_BUFFER_HEXDUMP(ARDUHAL_ESP_LOG_TAG, b, l, ESP_LOG_WARN); \
} while (0)
#endif
#else
#define log_w(format, ...) \
do { \
} while (0)
#define isr_log_w(format, ...) \
do { \
} while (0)
#define log_buf_w(b, l) \
do { \
} while (0)
#endif
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR
#ifndef USE_ESP_IDF_LOG
#define log_e(format, ...) log_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__)
#define isr_log_e(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__)
#define log_buf_e(b, l) \
do { \
ARDUHAL_LOG_COLOR_PRINT(E); \
log_print_buf(b, l); \
ARDUHAL_LOG_COLOR_PRINT_END; \
} while (0)
#else
#define log_e(format, ...) \
do { \
ESP_LOG_LEVEL_LOCAL(ESP_LOG_ERROR, ARDUHAL_ESP_LOG_TAG, format, ##__VA_ARGS__); \
} while (0)
#define isr_log_e(format, ...) \
do { \
ets_printf(LOG_FORMAT(E, format), esp_log_timestamp(), ARDUHAL_ESP_LOG_TAG, ##__VA_ARGS__); \
} while (0)
#define log_buf_e(b, l) \
do { \
ESP_LOG_BUFFER_HEXDUMP(ARDUHAL_ESP_LOG_TAG, b, l, ESP_LOG_ERROR); \
} while (0)
#endif
#else
#define log_e(format, ...) \
do { \
} while (0)
#define isr_log_e(format, ...) \
do { \
} while (0)
#define log_buf_e(b, l) \
do { \
} while (0)
#endif
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_NONE
#ifndef USE_ESP_IDF_LOG
#define log_n(format, ...) log_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__)
#define isr_log_n(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__)
#define log_buf_n(b, l) \
do { \
ARDUHAL_LOG_COLOR_PRINT(E); \
log_print_buf(b, l); \
ARDUHAL_LOG_COLOR_PRINT_END; \
} while (0)
#else
#define log_n(format, ...) \
do { \
ESP_LOG_LEVEL_LOCAL(ESP_LOG_ERROR, ARDUHAL_ESP_LOG_TAG, format, ##__VA_ARGS__); \
} while (0)
#define isr_log_n(format, ...) \
do { \
ets_printf(LOG_FORMAT(E, format), esp_log_timestamp(), ARDUHAL_ESP_LOG_TAG, ##__VA_ARGS__); \
} while (0)
#define log_buf_n(b, l) \
do { \
ESP_LOG_BUFFER_HEXDUMP(ARDUHAL_ESP_LOG_TAG, b, l, ESP_LOG_ERROR); \
} while (0)
#endif
#else
#define log_n(format, ...) \
do { \
} while (0)
#define isr_log_n(format, ...) \
do { \
} while (0)
#define log_buf_n(b, l) \
do { \
} while (0)
#endif
#include "esp_log.h"
#ifndef USE_ESP_IDF_LOG
#ifdef CONFIG_ARDUHAL_ESP_LOG
#undef ESP_LOGE
#undef ESP_LOGW
#undef ESP_LOGI
#undef ESP_LOGD
#undef ESP_LOGV
#undef ESP_EARLY_LOGE
#undef ESP_EARLY_LOGW
#undef ESP_EARLY_LOGI
#undef ESP_EARLY_LOGD
#undef ESP_EARLY_LOGV
#define ESP_LOGE(tag, format, ...) log_e("[%s] " format, tag, ##__VA_ARGS__)
#define ESP_LOGW(tag, format, ...) log_w("[%s] " format, tag, ##__VA_ARGS__)
#define ESP_LOGI(tag, format, ...) log_i("[%s] " format, tag, ##__VA_ARGS__)
#define ESP_LOGD(tag, format, ...) log_d("[%s] " format, tag, ##__VA_ARGS__)
#define ESP_LOGV(tag, format, ...) log_v("[%s] " format, tag, ##__VA_ARGS__)
#define ESP_EARLY_LOGE(tag, format, ...) isr_log_e("[%s] " format, tag, ##__VA_ARGS__)
#define ESP_EARLY_LOGW(tag, format, ...) isr_log_w("[%s] " format, tag, ##__VA_ARGS__)
#define ESP_EARLY_LOGI(tag, format, ...) isr_log_i("[%s] " format, tag, ##__VA_ARGS__)
#define ESP_EARLY_LOGD(tag, format, ...) isr_log_d("[%s] " format, tag, ##__VA_ARGS__)
#define ESP_EARLY_LOGV(tag, format, ...) isr_log_v("[%s] " format, tag, ##__VA_ARGS__)
#endif
#endif
#ifdef __cplusplus
}
#endif
#endif /* __ESP_LOGGING_H__ */
+69
View File
@@ -0,0 +1,69 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "esp32-hal-matrix.h"
#include "esp_attr.h"
#include "esp_system.h"
#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+
#include "soc/gpio_pins.h"
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
#include "esp32/rom/gpio.h"
#elif CONFIG_IDF_TARGET_ESP32S2
#include "esp32s2/rom/gpio.h"
#elif CONFIG_IDF_TARGET_ESP32S3
#include "esp32s3/rom/gpio.h"
#elif CONFIG_IDF_TARGET_ESP32C2
#include "esp32c2/rom/gpio.h"
#elif CONFIG_IDF_TARGET_ESP32C3
#include "esp32c3/rom/gpio.h"
#elif CONFIG_IDF_TARGET_ESP32C6
#include "esp32c6/rom/gpio.h"
#elif CONFIG_IDF_TARGET_ESP32H2
#include "esp32h2/rom/gpio.h"
#elif CONFIG_IDF_TARGET_ESP32P4
#include "esp32p4/rom/gpio.h"
#elif CONFIG_IDF_TARGET_ESP32C5
#include "esp32c5/rom/gpio.h"
#elif CONFIG_IDF_TARGET_ESP32C61
#include "esp32c61/rom/gpio.h"
#else
#error Target CONFIG_IDF_TARGET is not supported
#endif
#else // ESP32 Before IDF 4.0
#include "rom/gpio.h"
#define GPIO_MATRIX_CONST_ZERO_INPUT GPIO_FUNC_IN_LOW
#define GPIO_MATRIX_CONST_ONE_INPUT GPIO_FUNC_IN_HIGH
#endif
void ARDUINO_ISR_ATTR pinMatrixOutAttach(uint8_t pin, uint8_t function, bool invertOut, bool invertEnable) {
gpio_matrix_out(pin, function, invertOut, invertEnable);
}
void ARDUINO_ISR_ATTR pinMatrixOutDetach(uint8_t pin, bool invertOut, bool invertEnable) {
gpio_matrix_out(pin, SIG_GPIO_OUT_IDX, invertOut, invertEnable);
}
void ARDUINO_ISR_ATTR pinMatrixInAttach(uint8_t pin, uint8_t signal, bool inverted) {
gpio_matrix_in(pin, signal, inverted);
}
void ARDUINO_ISR_ATTR pinMatrixInDetach(uint8_t signal, bool high, bool inverted) {
gpio_matrix_in(high ? GPIO_MATRIX_CONST_ONE_INPUT : GPIO_MATRIX_CONST_ZERO_INPUT, signal, inverted);
}
/*
void ARDUINO_ISR_ATTR intrMatrixAttach(uint32_t source, uint32_t inum){
intr_matrix_set(PRO_CPU_NUM, source, inum);
}
*/
+34
View File
@@ -0,0 +1,34 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP32_HAL_MATRIX_H_
#define _ESP32_HAL_MATRIX_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "esp32-hal.h"
#include "soc/gpio_sig_map.h"
void pinMatrixOutAttach(uint8_t pin, uint8_t function, bool invertOut, bool invertEnable);
void pinMatrixOutDetach(uint8_t pin, bool invertOut, bool invertEnable);
void pinMatrixInAttach(uint8_t pin, uint8_t signal, bool inverted);
void pinMatrixInDetach(uint8_t signal, bool high, bool inverted);
#ifdef __cplusplus
}
#endif
#endif /* COMPONENTS_ARDUHAL_INCLUDE_ESP32_HAL_MATRIX_H_ */
+457
View File
@@ -0,0 +1,457 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_attr.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "esp_partition.h"
#include "esp_log.h"
#include "esp_timer.h"
#ifdef CONFIG_APP_ROLLBACK_ENABLE
#include "esp_ota_ops.h"
#endif //CONFIG_APP_ROLLBACK_ENABLE
#include "esp_private/startup_internal.h"
#if (defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)) && SOC_BT_SUPPORTED && __has_include("esp_bt.h")
#include "esp_bt.h"
#endif
#include <sys/time.h>
#include "soc/rtc.h"
#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)
#include "soc/rtc_cntl_reg.h"
#include "soc/syscon_reg.h"
#endif
#include "esp_task_wdt.h"
#include "esp32-hal.h"
#include "esp_system.h"
#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
#include "esp32/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32S2
#include "esp32s2/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32S3
#include "esp32s3/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32C2
#include "esp32c2/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32C3
#include "esp32c3/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32C6
#include "esp32c6/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32H2
#include "esp32h2/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32P4
#include "esp32p4/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32C5
#include "esp32c5/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32C61
#include "esp32c61/rom/rtc.h"
#else
#error Target CONFIG_IDF_TARGET is not supported
#endif
#if SOC_TEMP_SENSOR_SUPPORTED
#include "driver/temperature_sensor.h"
#endif
#else // ESP32 Before IDF 4.0
#include "rom/rtc.h"
#endif
//Undocumented!!! Get chip temperature in Fahrenheit
//Source: https://github.com/pcbreflux/espressif/blob/master/esp32/arduino/sketchbook/ESP32_int_temp_sensor/ESP32_int_temp_sensor.ino
#ifdef CONFIG_IDF_TARGET_ESP32
uint8_t temprature_sens_read();
float temperatureRead() {
return (temprature_sens_read() - 32) / 1.8;
}
#elif SOC_TEMP_SENSOR_SUPPORTED
static temperature_sensor_handle_t temp_sensor = NULL;
static bool temperatureReadInit() {
static volatile bool initialized = false;
if (!initialized) {
initialized = true;
//Install temperature sensor, expected temp ranger range: 10~50 ℃
temperature_sensor_config_t temp_sensor_config = TEMPERATURE_SENSOR_CONFIG_DEFAULT(10, 50);
if (temperature_sensor_install(&temp_sensor_config, &temp_sensor) != ESP_OK) {
initialized = false;
temp_sensor = NULL;
log_e("temperature_sensor_install failed");
} else if (temperature_sensor_enable(temp_sensor) != ESP_OK) {
temperature_sensor_uninstall(temp_sensor);
initialized = false;
temp_sensor = NULL;
log_e("temperature_sensor_enable failed");
}
}
return initialized;
}
float temperatureRead() {
float result = NAN;
if (temperatureReadInit()) {
if (temperature_sensor_get_celsius(temp_sensor, &result) != ESP_OK) {
log_e("temperature_sensor_get_celsius failed");
}
}
return result;
}
#endif
void __yield() {
vPortYield();
}
void yield() __attribute__((weak, alias("__yield")));
#if CONFIG_AUTOSTART_ARDUINO
extern TaskHandle_t loopTaskHandle;
extern bool loopTaskWDTEnabled;
void enableLoopWDT() {
if (loopTaskHandle != NULL) {
if (esp_task_wdt_add(loopTaskHandle) != ESP_OK) {
log_e("Failed to add loop task to WDT");
} else {
loopTaskWDTEnabled = true;
}
}
}
void disableLoopWDT() {
if (loopTaskHandle != NULL && loopTaskWDTEnabled) {
loopTaskWDTEnabled = false;
if (esp_task_wdt_delete(loopTaskHandle) != ESP_OK) {
log_e("Failed to remove loop task from WDT");
}
}
}
void feedLoopWDT() {
esp_err_t err = esp_task_wdt_reset();
if (err != ESP_OK) {
log_e("Failed to feed WDT! Error: %d", err);
}
}
#endif
void enableCore0WDT() {
TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCore(0);
if (idle_0 == NULL || esp_task_wdt_add(idle_0) != ESP_OK) {
log_e("Failed to add Core 0 IDLE task to WDT");
}
}
bool disableCore0WDT() {
TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCore(0);
if (idle_0 == NULL || esp_task_wdt_status(idle_0) || esp_task_wdt_delete(idle_0) != ESP_OK) {
log_e("Failed to remove Core 0 IDLE task from WDT");
return false;
}
return true;
}
#ifndef CONFIG_FREERTOS_UNICORE
void enableCore1WDT() {
TaskHandle_t idle_1 = xTaskGetIdleTaskHandleForCore(1);
if (idle_1 == NULL || esp_task_wdt_add(idle_1) != ESP_OK) {
log_e("Failed to add Core 1 IDLE task to WDT");
}
}
bool disableCore1WDT() {
TaskHandle_t idle_1 = xTaskGetIdleTaskHandleForCore(1);
if (idle_1 == NULL || esp_task_wdt_status(idle_1) || esp_task_wdt_delete(idle_1) != ESP_OK) {
log_e("Failed to remove Core 1 IDLE task from WDT");
return false;
}
return true;
}
#endif
BaseType_t xTaskCreateUniversal(
TaskFunction_t pxTaskCode, const char *const pcName, const uint32_t usStackDepth, void *const pvParameters, UBaseType_t uxPriority,
TaskHandle_t *const pxCreatedTask, const BaseType_t xCoreID
) {
#ifndef CONFIG_FREERTOS_UNICORE
if (xCoreID >= 0 && xCoreID < 2) {
return xTaskCreatePinnedToCore(pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask, xCoreID);
} else {
#endif
return xTaskCreate(pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask);
#ifndef CONFIG_FREERTOS_UNICORE
}
#endif
}
unsigned long ARDUINO_ISR_ATTR micros() {
return (unsigned long)(esp_timer_get_time());
}
unsigned long ARDUINO_ISR_ATTR millis() {
return (unsigned long)(esp_timer_get_time() / 1000ULL);
}
void delay(uint32_t ms) {
vTaskDelay(ms / portTICK_PERIOD_MS);
}
void ARDUINO_ISR_ATTR delayMicroseconds(uint32_t us) {
uint64_t m = (uint64_t)esp_timer_get_time();
if (us) {
uint64_t e = (m + us);
if (m > e) { //overflow
while ((uint64_t)esp_timer_get_time() > e) {
NOP();
}
}
while ((uint64_t)esp_timer_get_time() < e) {
NOP();
}
}
}
void initVariant() __attribute__((weak));
void initVariant() {}
void init() __attribute__((weak));
void init() {}
#ifdef CONFIG_APP_ROLLBACK_ENABLE
/**
* @brief Verify the OTA image after boot
*
* This weak hook is invoked when a newly-updated application image is in
* the `ESP_OTA_IMG_PENDING_VERIFY` state. The default implementation
* returns `true`, indicating the image is considered valid. Applications
* may override this function (provide a non-weak implementation) to
* perform custom checks such as running a self-test, validating
* application-specific state, or performing connectivity / sensor checks
* before confirming the update.
*
* Usage:
* - Return `true` to mark the OTA image as valid; the system will call
* `esp_ota_mark_app_valid_cancel_rollback()` to cancel rollback.
* - Return `false` to indicate verification failure; the system will
* call `esp_ota_mark_app_invalid_rollback_and_reboot()` to rollback.
*
* @note Keep this function short and non-blocking if possible; blocking
* for long periods may delay boot.
*
* @return true if verification succeeds and the image should be kept
* @return false if verification fails and the image should be rolled back
*/
bool verifyOta() __attribute__((weak));
bool verifyOta() {
return true;
}
/**
* @brief Optionally defer OTA verification to later
*
* This weak hook allows an application to postpone the default OTA
* verification performed at startup. If this function returns `true`, the
* initialization code will skip immediate verification and the application
* can choose to verify the update at a later time (for example, after a
* lengthy hardware initialization or user interaction). The default weak
* implementation returns `false`, meaning verification will run during
* `initArduino()` when an image is pending verification.
*
* Usage:
* - Return `true` to delay verification and handle it manually later.
* - Return `false` to allow the built-in verification flow to run at
* startup.
*
* @return true to defer verification to a later time
* @return false to perform verification immediately during init
*/
bool verifyRollbackLater() __attribute__((weak));
bool verifyRollbackLater() {
return false;
}
#endif
#if (defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)) && SOC_BT_SUPPORTED && __has_include("esp_bt.h")
// declared here, defined in esp32-hal-bt.c (weak so users can override)
extern bool btInUse(void);
#endif
#if CONFIG_SPIRAM_SUPPORT || CONFIG_SPIRAM
ESP_SYSTEM_INIT_FN(init_psram_new, CORE, BIT(0), 99) {
psramInit();
return ESP_OK;
}
#endif
void initArduino() {
//init proper ref tick value for PLL (uncomment if REF_TICK is different than 1MHz)
//ESP_REG(APB_CTRL_PLL_TICK_CONF_REG) = APB_CLK_FREQ / REF_CLK_FREQ - 1;
#if CONFIG_SPIRAM_SUPPORT || CONFIG_SPIRAM
#ifndef CONFIG_SPIRAM_BOOT_INIT
psramAddToHeap();
#endif
#endif
#ifdef CONFIG_APP_ROLLBACK_ENABLE
if (!verifyRollbackLater()) {
const esp_partition_t *running = esp_ota_get_running_partition();
esp_ota_img_states_t ota_state;
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) {
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) {
if (verifyOta()) {
esp_ota_mark_app_valid_cancel_rollback();
} else {
log_e("OTA verification failed! Start rollback to the previous version ...");
esp_ota_mark_app_invalid_rollback_and_reboot();
}
}
}
}
#endif
esp_log_level_set("*", CONFIG_LOG_DEFAULT_LEVEL);
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL);
if (partition != NULL) {
err = esp_partition_erase_range(partition, 0, partition->size);
if (!err) {
err = nvs_flash_init();
} else {
log_e("Failed to format the broken NVS partition!");
}
} else {
log_e("Could not find NVS partition");
}
}
if (err) {
log_e("Failed to initialize NVS! Error: %u", err);
}
#if (defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)) && SOC_BT_SUPPORTED && __has_include("esp_bt.h")
if (!btInUse()) {
esp_bt_controller_mem_release(ESP_BT_MODE_BTDM);
}
#endif
init();
initVariant();
}
//used by hal log
const char *ARDUINO_ISR_ATTR pathToFileName(const char *path) {
size_t i = 0;
size_t pos = 0;
char *p = (char *)path;
while (*p) {
i++;
if (*p == '/' || *p == '\\') {
pos = i;
}
p++;
}
return path + pos;
}
#include "esp_rom_sys.h"
#include "esp_debug_helpers.h"
#if CONFIG_IDF_TARGET_ARCH_XTENSA
#include "esp_cpu_utils.h"
#else
#include "riscv/rvruntime-frames.h"
#endif
#include "esp_memory_utils.h"
#include "esp_private/panic_internal.h"
static arduino_panic_handler_t _panic_handler = NULL;
static void *_panic_handler_arg = NULL;
void set_arduino_panic_handler(arduino_panic_handler_t handler, void *arg) {
_panic_handler = handler;
_panic_handler_arg = arg;
}
arduino_panic_handler_t get_arduino_panic_handler(void) {
return _panic_handler;
}
void *get_arduino_panic_handler_arg(void) {
return _panic_handler_arg;
}
static void handle_custom_backtrace(panic_info_t *info) {
arduino_panic_info_t p_info;
p_info.reason = info->reason;
p_info.core = info->core;
p_info.pc = info->addr;
p_info.backtrace_len = 0;
p_info.backtrace_corrupt = false;
p_info.backtrace_continues = false;
#if CONFIG_IDF_TARGET_ARCH_XTENSA
XtExcFrame *xt_frame = (XtExcFrame *)info->frame;
esp_backtrace_frame_t stk_frame = {.pc = xt_frame->pc, .sp = xt_frame->a1, .next_pc = xt_frame->a0, .exc_frame = xt_frame};
uint32_t i = 100, pc_ptr = esp_cpu_process_stack_pc(stk_frame.pc);
p_info.backtrace[p_info.backtrace_len++] = pc_ptr;
bool corrupted = !(esp_stack_ptr_is_sane(stk_frame.sp) && (esp_ptr_executable((void *)esp_cpu_process_stack_pc(stk_frame.pc)) ||
/* Ignore the first corrupted PC in case of InstrFetchProhibited */
(stk_frame.exc_frame && ((XtExcFrame *)stk_frame.exc_frame)->exccause == EXCCAUSE_INSTR_PROHIBITED)));
while (i-- > 0 && stk_frame.next_pc != 0 && !corrupted) {
if (!esp_backtrace_get_next_frame(&stk_frame)) {
corrupted = true;
}
pc_ptr = esp_cpu_process_stack_pc(stk_frame.pc);
if (esp_ptr_executable((void *)pc_ptr)) {
p_info.backtrace[p_info.backtrace_len++] = pc_ptr;
if (p_info.backtrace_len == 60) {
break;
}
}
}
if (corrupted) {
p_info.backtrace_corrupt = true;
} else if (stk_frame.next_pc != 0) {
p_info.backtrace_continues = true;
}
#elif CONFIG_IDF_TARGET_ARCH_RISCV
uint32_t sp = (uint32_t)((RvExcFrame *)info->frame)->sp;
p_info.backtrace[p_info.backtrace_len++] = sp;
uint32_t *spptr = (uint32_t *)(sp);
for (int i = 0; i < 256; i++) {
if (esp_ptr_executable((void *)spptr[i])) {
p_info.backtrace[p_info.backtrace_len++] = spptr[i];
if (p_info.backtrace_len == 60) {
if (i < 255) {
p_info.backtrace_continues = true;
}
break;
}
}
}
#endif
_panic_handler(&p_info, _panic_handler_arg);
}
void __real_esp_panic_handler(panic_info_t *);
void __wrap_esp_panic_handler(panic_info_t *info) {
if (_panic_handler != NULL) {
handle_custom_backtrace(info);
}
__real_esp_panic_handler(info);
}
+268
View File
@@ -0,0 +1,268 @@
/*
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp32-hal-log.h"
#include "esp32-hal-periman.h"
#include "esp_bit_defs.h"
typedef struct ATTR_PACKED {
peripheral_bus_type_t type;
const char *extra_type;
void *bus;
int8_t bus_num;
int8_t bus_channel;
} peripheral_pin_item_t;
static peripheral_bus_deinit_cb_t deinit_functions[ESP32_BUS_TYPE_MAX] = {NULL};
static peripheral_pin_item_t pins[SOC_GPIO_PIN_COUNT];
#define GPIO_NOT_VALID(p) ((p >= SOC_GPIO_PIN_COUNT) || ((SOC_GPIO_VALID_GPIO_MASK & (1ULL << p)) == 0))
const char *perimanGetTypeName(peripheral_bus_type_t type) {
switch (type) {
case ESP32_BUS_TYPE_INIT: return "INIT";
case ESP32_BUS_TYPE_GPIO: return "GPIO";
case ESP32_BUS_TYPE_UART_RX: return "UART_RX";
case ESP32_BUS_TYPE_UART_TX: return "UART_TX";
case ESP32_BUS_TYPE_UART_CTS: return "UART_CTS";
case ESP32_BUS_TYPE_UART_RTS: return "UART_RTS";
#if SOC_SDM_SUPPORTED
case ESP32_BUS_TYPE_SIGMADELTA: return "SIGMADELTA";
#endif
#if SOC_ADC_SUPPORTED
case ESP32_BUS_TYPE_ADC_ONESHOT: return "ADC_ONESHOT";
case ESP32_BUS_TYPE_ADC_CONT: return "ADC_CONT";
#endif
#if SOC_DAC_SUPPORTED
case ESP32_BUS_TYPE_DAC_ONESHOT: return "DAC_ONESHOT";
case ESP32_BUS_TYPE_DAC_CONT: return "DAC_CONT";
case ESP32_BUS_TYPE_DAC_COSINE: return "DAC_COSINE";
#endif
#if SOC_LEDC_SUPPORTED
case ESP32_BUS_TYPE_LEDC: return "LEDC";
#endif
#if SOC_RMT_SUPPORTED
case ESP32_BUS_TYPE_RMT_TX: return "RMT_TX";
case ESP32_BUS_TYPE_RMT_RX: return "RMT_RX";
#endif
#if SOC_I2S_SUPPORTED
case ESP32_BUS_TYPE_I2S_STD_MCLK: return "I2S_STD_MCLK";
case ESP32_BUS_TYPE_I2S_STD_BCLK: return "I2S_STD_BCLK";
case ESP32_BUS_TYPE_I2S_STD_WS: return "I2S_STD_WS";
case ESP32_BUS_TYPE_I2S_STD_DOUT: return "I2S_STD_DOUT";
case ESP32_BUS_TYPE_I2S_STD_DIN: return "I2S_STD_DIN";
case ESP32_BUS_TYPE_I2S_TDM_MCLK: return "I2S_TDM_MCLK";
case ESP32_BUS_TYPE_I2S_TDM_BCLK: return "I2S_TDM_BCLK";
case ESP32_BUS_TYPE_I2S_TDM_WS: return "I2S_TDM_WS";
case ESP32_BUS_TYPE_I2S_TDM_DOUT: return "I2S_TDM_DOUT";
case ESP32_BUS_TYPE_I2S_TDM_DIN: return "I2S_TDM_DIN";
case ESP32_BUS_TYPE_I2S_PDM_TX_CLK: return "I2S_PDM_TX_CLK";
case ESP32_BUS_TYPE_I2S_PDM_TX_DOUT0: return "I2S_PDM_TX_DOUT0";
case ESP32_BUS_TYPE_I2S_PDM_TX_DOUT1: return "I2S_PDM_TX_DOUT1";
case ESP32_BUS_TYPE_I2S_PDM_RX_CLK: return "I2S_PDM_RX_CLK";
case ESP32_BUS_TYPE_I2S_PDM_RX_DIN0: return "I2S_PDM_RX_DIN0";
case ESP32_BUS_TYPE_I2S_PDM_RX_DIN1: return "I2S_PDM_RX_DIN1";
case ESP32_BUS_TYPE_I2S_PDM_RX_DIN2: return "I2S_PDM_RX_DIN2";
case ESP32_BUS_TYPE_I2S_PDM_RX_DIN3: return "I2S_PDM_RX_DIN3";
#endif
#if SOC_I2C_SUPPORTED
case ESP32_BUS_TYPE_I2C_MASTER_SDA: return "I2C_MASTER_SDA";
case ESP32_BUS_TYPE_I2C_MASTER_SCL: return "I2C_MASTER_SCL";
case ESP32_BUS_TYPE_I2C_SLAVE_SDA: return "I2C_SLAVE_SDA";
case ESP32_BUS_TYPE_I2C_SLAVE_SCL: return "I2C_SLAVE_SCL";
#endif
#if SOC_GPSPI_SUPPORTED
case ESP32_BUS_TYPE_SPI_MASTER_SCK: return "SPI_MASTER_SCK";
case ESP32_BUS_TYPE_SPI_MASTER_MISO: return "SPI_MASTER_MISO";
case ESP32_BUS_TYPE_SPI_MASTER_MOSI: return "SPI_MASTER_MOSI";
case ESP32_BUS_TYPE_SPI_MASTER_SS: return "SPI_MASTER_SS";
#endif
#if SOC_SDMMC_HOST_SUPPORTED
case ESP32_BUS_TYPE_SDMMC_CLK: return "SDMMC_CLK";
case ESP32_BUS_TYPE_SDMMC_CMD: return "SDMMC_CMD";
case ESP32_BUS_TYPE_SDMMC_D0: return "SDMMC_D0";
case ESP32_BUS_TYPE_SDMMC_D1: return "SDMMC_D1";
case ESP32_BUS_TYPE_SDMMC_D2: return "SDMMC_D2";
case ESP32_BUS_TYPE_SDMMC_D3: return "SDMMC_D3";
#endif
#if SOC_TOUCH_SENSOR_SUPPORTED
case ESP32_BUS_TYPE_TOUCH: return "TOUCH";
#endif
#if SOC_USB_SERIAL_JTAG_SUPPORTED || SOC_USB_OTG_SUPPORTED
case ESP32_BUS_TYPE_USB_DM: return "USB_DM";
case ESP32_BUS_TYPE_USB_DP: return "USB_DP";
#endif
#if SOC_GPSPI_SUPPORTED
case ESP32_BUS_TYPE_ETHERNET_SPI: return "ETHERNET_SPI";
#endif
#if CONFIG_ETH_USE_ESP32_EMAC
case ESP32_BUS_TYPE_ETHERNET_RMII: return "ETHERNET_RMII";
case ESP32_BUS_TYPE_ETHERNET_CLK: return "ETHERNET_CLK";
case ESP32_BUS_TYPE_ETHERNET_MCD: return "ETHERNET_MCD";
case ESP32_BUS_TYPE_ETHERNET_MDIO: return "ETHERNET_MDIO";
case ESP32_BUS_TYPE_ETHERNET_PWR: return "ETHERNET_PWR";
#endif
#if CONFIG_LWIP_PPP_SUPPORT
case ESP32_BUS_TYPE_PPP_TX: return "PPP_MODEM_TX";
case ESP32_BUS_TYPE_PPP_RX: return "PPP_MODEM_RX";
case ESP32_BUS_TYPE_PPP_RTS: return "PPP_MODEM_RTS";
case ESP32_BUS_TYPE_PPP_CTS: return "PPP_MODEM_CTS";
#endif
default: return "UNKNOWN";
}
}
bool perimanSetPinBus(uint8_t pin, peripheral_bus_type_t type, void *bus, int8_t bus_num, int8_t bus_channel) {
peripheral_bus_type_t otype = ESP32_BUS_TYPE_INIT;
void *obus = NULL;
if (GPIO_NOT_VALID(pin)) {
log_e("Invalid pin: %u", pin);
return false;
}
if (type >= ESP32_BUS_TYPE_MAX) {
log_e("Invalid type: %s (%u) when setting pin %u", perimanGetTypeName(type), (unsigned int)type, pin);
return false;
}
if (type > ESP32_BUS_TYPE_GPIO && bus == NULL) {
log_e("Bus is NULL for pin %u with type %s (%u)", pin, perimanGetTypeName(type), (unsigned int)type);
return false;
}
if (type == ESP32_BUS_TYPE_INIT && bus != NULL) {
log_e("Can't set a Bus to INIT Type (pin %u)", pin);
return false;
}
otype = pins[pin].type;
obus = pins[pin].bus;
if (type == otype && bus == obus) {
if (type != ESP32_BUS_TYPE_INIT) {
log_i("Pin %u already has type %s (%u) with bus %p", pin, perimanGetTypeName(type), (unsigned int)type, bus);
}
return true;
}
if (obus != NULL) {
if (deinit_functions[otype] == NULL) {
log_e("No deinit function for type %s (%u) (pin %u)", perimanGetTypeName(otype), (unsigned int)otype, pin);
return false;
}
if (!deinit_functions[otype](obus)) {
log_e("Deinit function for previous bus type %s (%u) failed (pin %u)", perimanGetTypeName(otype), (unsigned int)otype, pin);
return false;
}
}
pins[pin].type = type;
pins[pin].bus = bus;
pins[pin].bus_num = bus_num;
pins[pin].bus_channel = bus_channel;
pins[pin].extra_type = NULL;
log_v("Pin %u successfully set to type %s (%u) with bus %p", pin, perimanGetTypeName(type), (unsigned int)type, bus);
return true;
}
bool perimanSetPinBusExtraType(uint8_t pin, const char *extra_type) {
if (GPIO_NOT_VALID(pin)) {
log_e("Invalid pin: %u", pin);
return false;
}
if (pins[pin].type == ESP32_BUS_TYPE_INIT) {
log_e("Can't set extra type for Bus INIT Type (pin %u)", pin);
return false;
}
pins[pin].extra_type = extra_type;
log_v("Successfully set extra_type %s for pin %u", extra_type, pin);
return true;
}
void *perimanGetPinBus(uint8_t pin, peripheral_bus_type_t type) {
if (GPIO_NOT_VALID(pin)) {
log_e("Invalid pin: %u", pin);
return NULL;
}
if (type >= ESP32_BUS_TYPE_MAX || type == ESP32_BUS_TYPE_INIT) {
log_e("Invalid type %s (%u) for pin %u", perimanGetTypeName(type), (unsigned int)type, pin);
return NULL;
}
if (pins[pin].type == type) {
return pins[pin].bus;
}
return NULL;
}
peripheral_bus_type_t perimanGetPinBusType(uint8_t pin) {
if (GPIO_NOT_VALID(pin)) {
log_e("Invalid pin: %u", pin);
return ESP32_BUS_TYPE_MAX;
}
return pins[pin].type;
}
const char *perimanGetPinBusExtraType(uint8_t pin) {
if (GPIO_NOT_VALID(pin)) {
log_e("Invalid pin: %u", pin);
return NULL;
}
return pins[pin].extra_type;
}
int8_t perimanGetPinBusNum(uint8_t pin) {
if (GPIO_NOT_VALID(pin)) {
log_e("Invalid pin: %u", pin);
return -1;
}
return pins[pin].bus_num;
}
int8_t perimanGetPinBusChannel(uint8_t pin) {
if (GPIO_NOT_VALID(pin)) {
log_e("Invalid pin: %u", pin);
return -1;
}
return pins[pin].bus_channel;
}
bool perimanSetBusDeinit(peripheral_bus_type_t type, peripheral_bus_deinit_cb_t cb) {
if (type >= ESP32_BUS_TYPE_MAX || type == ESP32_BUS_TYPE_INIT) {
log_e("Invalid type: %s (%u)", perimanGetTypeName(type), (unsigned int)type);
return false;
}
if (cb == NULL) {
log_e("Callback is NULL when setting deinit function for type %s (%u)", perimanGetTypeName(type), (unsigned int)type);
return false;
}
deinit_functions[type] = cb;
log_v("Deinit function for type %s (%u) successfully set to %p", perimanGetTypeName(type), (unsigned int)type, cb);
return true;
}
// This no-op callback is used by perimanClearBusDeinit() to effectively disable bus deinit functionality
// without setting the callback to NULL, which would cause errors in perimanSetPinBus() at line 146.
static bool empty_bus_deinit_cb(void *bus) {
return true;
}
bool perimanClearBusDeinit(peripheral_bus_type_t type) {
if (type >= ESP32_BUS_TYPE_MAX || type == ESP32_BUS_TYPE_INIT) {
log_e("Invalid type: %s (%u)", perimanGetTypeName(type), (unsigned int)type);
return false;
}
deinit_functions[type] = empty_bus_deinit_cb;
log_v("Deinit function for type %s (%u) cleared", perimanGetTypeName(type), (unsigned int)type);
return true;
}
peripheral_bus_deinit_cb_t perimanGetBusDeinit(peripheral_bus_type_t type) {
if (type >= ESP32_BUS_TYPE_MAX || type == ESP32_BUS_TYPE_INIT) {
log_e("Invalid type: %s (%u)", perimanGetTypeName(type), (unsigned int)type);
return NULL;
}
if (deinit_functions[type] == empty_bus_deinit_cb) {
return NULL;
}
return deinit_functions[type];
}
bool perimanPinIsValid(uint8_t pin) {
return !(GPIO_NOT_VALID(pin));
}
+155
View File
@@ -0,0 +1,155 @@
/*
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "soc/soc_caps.h"
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#define perimanClearPinBus(p) perimanSetPinBus(p, ESP32_BUS_TYPE_INIT, NULL, -1, -1)
typedef enum {
ESP32_BUS_TYPE_INIT, // IO has not been attached to a bus yet
ESP32_BUS_TYPE_GPIO, // IO is used as GPIO
ESP32_BUS_TYPE_UART_RX, // IO is used as UART RX pin
ESP32_BUS_TYPE_UART_TX, // IO is used as UART TX pin
ESP32_BUS_TYPE_UART_CTS, // IO is used as UART CTS pin
ESP32_BUS_TYPE_UART_RTS, // IO is used as UART RTS pin
#if SOC_SDM_SUPPORTED
ESP32_BUS_TYPE_SIGMADELTA, // IO is used as SigmeDelta output
#endif
#if SOC_ADC_SUPPORTED
ESP32_BUS_TYPE_ADC_ONESHOT, // IO is used as ADC OneShot input
ESP32_BUS_TYPE_ADC_CONT, // IO is used as ADC continuous input
#endif
#if SOC_DAC_SUPPORTED
ESP32_BUS_TYPE_DAC_ONESHOT, // IO is used as DAC OneShot output
ESP32_BUS_TYPE_DAC_CONT, // IO is used as DAC continuous output
ESP32_BUS_TYPE_DAC_COSINE, // IO is used as DAC cosine output
#endif
#if SOC_LEDC_SUPPORTED
ESP32_BUS_TYPE_LEDC, // IO is used as LEDC output
#endif
#if SOC_RMT_SUPPORTED
ESP32_BUS_TYPE_RMT_TX, // IO is used as RMT output
ESP32_BUS_TYPE_RMT_RX, // IO is used as RMT input
#endif
#if SOC_I2S_SUPPORTED
ESP32_BUS_TYPE_I2S_STD_MCLK, // IO is used as I2S STD MCLK pin
ESP32_BUS_TYPE_I2S_STD_BCLK, // IO is used as I2S STD BCLK pin
ESP32_BUS_TYPE_I2S_STD_WS, // IO is used as I2S STD WS pin
ESP32_BUS_TYPE_I2S_STD_DOUT, // IO is used as I2S STD DOUT pin
ESP32_BUS_TYPE_I2S_STD_DIN, // IO is used as I2S STD DIN pin
ESP32_BUS_TYPE_I2S_TDM_MCLK, // IO is used as I2S TDM MCLK pin
ESP32_BUS_TYPE_I2S_TDM_BCLK, // IO is used as I2S TDM BCLK pin
ESP32_BUS_TYPE_I2S_TDM_WS, // IO is used as I2S TDM WS pin
ESP32_BUS_TYPE_I2S_TDM_DOUT, // IO is used as I2S TDM DOUT pin
ESP32_BUS_TYPE_I2S_TDM_DIN, // IO is used as I2S TDM DIN pin
ESP32_BUS_TYPE_I2S_PDM_TX_CLK, // IO is used as I2S PDM CLK pin
ESP32_BUS_TYPE_I2S_PDM_TX_DOUT0, // IO is used as I2S PDM DOUT0 pin
ESP32_BUS_TYPE_I2S_PDM_TX_DOUT1, // IO is used as I2S PDM DOUT1 pin
ESP32_BUS_TYPE_I2S_PDM_RX_CLK, // IO is used as I2S PDM CLK pin
ESP32_BUS_TYPE_I2S_PDM_RX_DIN0, // IO is used as I2S PDM DIN0 pin
ESP32_BUS_TYPE_I2S_PDM_RX_DIN1, // IO is used as I2S PDM DIN1 pin
ESP32_BUS_TYPE_I2S_PDM_RX_DIN2, // IO is used as I2S PDM DIN2 pin
ESP32_BUS_TYPE_I2S_PDM_RX_DIN3, // IO is used as I2S PDM DIN3 pin
#endif
#if SOC_I2C_SUPPORTED
ESP32_BUS_TYPE_I2C_MASTER_SDA, // IO is used as I2C master SDA pin
ESP32_BUS_TYPE_I2C_MASTER_SCL, // IO is used as I2C master SCL pin
ESP32_BUS_TYPE_I2C_SLAVE_SDA, // IO is used as I2C slave SDA pin
ESP32_BUS_TYPE_I2C_SLAVE_SCL, // IO is used as I2C slave SCL pin
#endif
#if SOC_GPSPI_SUPPORTED
ESP32_BUS_TYPE_SPI_MASTER_SCK, // IO is used as SPI master SCK pin
ESP32_BUS_TYPE_SPI_MASTER_MISO, // IO is used as SPI master MISO pin
ESP32_BUS_TYPE_SPI_MASTER_MOSI, // IO is used as SPI master MOSI pin
ESP32_BUS_TYPE_SPI_MASTER_SS, // IO is used as SPI master SS pin
#endif
#if SOC_SDMMC_HOST_SUPPORTED
ESP32_BUS_TYPE_SDMMC_CLK, // IO is used as SDMMC CLK pin
ESP32_BUS_TYPE_SDMMC_CMD, // IO is used as SDMMC CMD pin
ESP32_BUS_TYPE_SDMMC_D0, // IO is used as SDMMC D0 pin
ESP32_BUS_TYPE_SDMMC_D1, // IO is used as SDMMC D1 pin
ESP32_BUS_TYPE_SDMMC_D2, // IO is used as SDMMC D2 pin
ESP32_BUS_TYPE_SDMMC_D3, // IO is used as SDMMC D3 pin
#endif
#if SOC_TOUCH_SENSOR_SUPPORTED
ESP32_BUS_TYPE_TOUCH, // IO is used as TOUCH pin
#endif
#if SOC_USB_SERIAL_JTAG_SUPPORTED || SOC_USB_OTG_SUPPORTED
ESP32_BUS_TYPE_USB_DM, // IO is used as USB DM (+) pin
ESP32_BUS_TYPE_USB_DP, // IO is used as USB DP (-) pin
#endif
#if SOC_GPSPI_SUPPORTED
ESP32_BUS_TYPE_ETHERNET_SPI, // IO is used as ETHERNET SPI pin
#endif
#if CONFIG_ETH_USE_ESP32_EMAC
ESP32_BUS_TYPE_ETHERNET_RMII, // IO is used as ETHERNET RMII pin
ESP32_BUS_TYPE_ETHERNET_CLK, // IO is used as ETHERNET CLK pin
ESP32_BUS_TYPE_ETHERNET_MCD, // IO is used as ETHERNET MCD pin
ESP32_BUS_TYPE_ETHERNET_MDIO, // IO is used as ETHERNET MDIO pin
ESP32_BUS_TYPE_ETHERNET_PWR, // IO is used as ETHERNET PWR pin
#endif
#if CONFIG_LWIP_PPP_SUPPORT
ESP32_BUS_TYPE_PPP_TX, // IO is used as PPP Modem TX pin
ESP32_BUS_TYPE_PPP_RX, // IO is used as PPP Modem RX pin
ESP32_BUS_TYPE_PPP_RTS, // IO is used as PPP Modem RTS pin
ESP32_BUS_TYPE_PPP_CTS, // IO is used as PPP Modem CTS pin
#endif
ESP32_BUS_TYPE_MAX
} peripheral_bus_type_t;
typedef bool (*peripheral_bus_deinit_cb_t)(void *bus);
const char *perimanGetTypeName(peripheral_bus_type_t type);
// Sets the bus type, bus handle, bus number and bus channel for given pin.
bool perimanSetPinBus(uint8_t pin, peripheral_bus_type_t type, void *bus, int8_t bus_num, int8_t bus_channel);
// Returns handle of the bus for the given pin if type of bus matches. NULL otherwise
void *perimanGetPinBus(uint8_t pin, peripheral_bus_type_t type);
// Returns the type of the bus for the given pin if attached. ESP32_BUS_TYPE_MAX otherwise
peripheral_bus_type_t perimanGetPinBusType(uint8_t pin);
// Returns the bus number or unit of the bus for the given pin if set. -1 otherwise
int8_t perimanGetPinBusNum(uint8_t pin);
// Returns the bus channel of the bus for the given pin if set. -1 otherwise
int8_t perimanGetPinBusChannel(uint8_t pin);
// Sets the peripheral destructor callback. Used to destroy bus when pin is assigned another function
bool perimanSetBusDeinit(peripheral_bus_type_t type, peripheral_bus_deinit_cb_t cb);
// Clears the peripheral destructor callback
bool perimanClearBusDeinit(peripheral_bus_type_t type);
// Get the peripheral destructor callback. It allows changing/restoring the peripheral pin function detaching, if necessary
// returns NULL if none is set
peripheral_bus_deinit_cb_t perimanGetBusDeinit(peripheral_bus_type_t type);
// Check if given pin is a valid GPIO number
bool perimanPinIsValid(uint8_t pin);
// Sets the extra type for non Init bus. Used to customize pin bus name which can be printed by printPerimanInfo().
bool perimanSetPinBusExtraType(uint8_t pin, const char *extra_type);
// Returns the extra type of the bus for given pin if set. NULL otherwise
const char *perimanGetPinBusExtraType(uint8_t pin);
#ifdef __cplusplus
}
#endif
+157
View File
@@ -0,0 +1,157 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "esp32-hal.h"
#if CONFIG_SPIRAM_SUPPORT || CONFIG_SPIRAM
#include "soc/efuse_reg.h"
#include "esp_heap_caps.h"
#include "esp_system.h"
#include "esp_psram.h"
#include "esp_private/esp_psram_extram.h"
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
#include "esp32/rom/cache.h"
#elif CONFIG_IDF_TARGET_ESP32S2
#include "esp32s2/rom/cache.h"
#elif CONFIG_IDF_TARGET_ESP32S3
#include "esp32s3/rom/cache.h"
#elif CONFIG_IDF_TARGET_ESP32P4
#include "esp32p4/rom/cache.h"
#elif CONFIG_IDF_TARGET_ESP32C5
#include "esp32c5/rom/cache.h"
#elif CONFIG_IDF_TARGET_ESP32C61
#include "esp32c61/rom/cache.h"
#else
#error Target CONFIG_IDF_TARGET is not supported
#endif
#define TAG "arduino-psram"
static volatile bool spiramDetected = false;
static volatile bool spiramFailed = false;
//allows user to bypass SPI RAM test routine
__attribute__((weak)) bool testSPIRAM(void) {
return esp_psram_extram_test();
}
bool psramInit() {
if (spiramDetected) {
return true;
}
#ifndef CONFIG_SPIRAM_BOOT_INIT
if (spiramFailed) {
return false;
}
#if CONFIG_IDF_TARGET_ESP32
uint32_t chip_ver = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_PACKAGE);
uint32_t pkg_ver = chip_ver & 0x7;
if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5 || pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2) {
spiramFailed = true;
ESP_EARLY_LOGW(TAG, "PSRAM not supported!");
return false;
}
#elif CONFIG_IDF_TARGET_ESP32S2
extern void esp_config_data_cache_mode(void);
esp_config_data_cache_mode();
Cache_Enable_DCache(0);
#endif
if (esp_psram_init() != ESP_OK) {
spiramFailed = true;
ESP_EARLY_LOGW(TAG, "PSRAM init failed!");
#if CONFIG_IDF_TARGET_ESP32
if (pkg_ver != EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4) {
pinMatrixOutDetach(16, false, false);
pinMatrixOutDetach(17, false, false);
}
#endif
return false;
}
//testSPIRAM() allows user to bypass SPI RAM test routine
if (!testSPIRAM()) {
spiramFailed = true;
ESP_EARLY_LOGE(TAG, "PSRAM test failed!");
return false;
}
//ESP_EARLY_LOGI(TAG, "PSRAM enabled");
#endif /* CONFIG_SPIRAM_BOOT_INIT */
spiramDetected = true;
return true;
}
bool psramAddToHeap() {
if (!spiramDetected) {
log_e("PSRAM not initialized!");
return false;
}
if (esp_psram_extram_add_to_heap_allocator() != ESP_OK) {
log_e("PSRAM could not be added to the heap!");
return false;
}
#if CONFIG_SPIRAM_USE_MALLOC && !CONFIG_ARDUINO_ISR_IRAM
heap_caps_malloc_extmem_enable(CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL);
#endif
log_i("PSRAM added to the heap.");
return true;
}
bool ARDUINO_ISR_ATTR psramFound() {
return spiramDetected;
}
void ARDUINO_ISR_ATTR *ps_malloc(size_t size) {
if (!spiramDetected) {
return NULL;
}
return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
}
void ARDUINO_ISR_ATTR *ps_calloc(size_t n, size_t size) {
if (!spiramDetected) {
return NULL;
}
return heap_caps_calloc(n, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
}
void ARDUINO_ISR_ATTR *ps_realloc(void *ptr, size_t size) {
if (!spiramDetected) {
return NULL;
}
return heap_caps_realloc(ptr, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
}
#else
bool psramInit() {
return false;
}
bool ARDUINO_ISR_ATTR psramFound() {
return false;
}
void ARDUINO_ISR_ATTR *ps_malloc(size_t size) {
return NULL;
}
void ARDUINO_ISR_ATTR *ps_calloc(size_t n, size_t size) {
return NULL;
}
void ARDUINO_ISR_ATTR *ps_realloc(void *ptr, size_t size) {
return NULL;
}
#endif
+46
View File
@@ -0,0 +1,46 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP32_HAL_PSRAM_H_
#define _ESP32_HAL_PSRAM_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "sdkconfig.h"
// Clear flags in Arduino IDE when PSRAM is disabled
#if defined(ESP32_ARDUINO_LIB_BUILDER) && !defined(BOARD_HAS_PSRAM)
#ifdef CONFIG_SPIRAM_SUPPORT
#undef CONFIG_SPIRAM_SUPPORT
#endif
#ifdef CONFIG_SPIRAM
#undef CONFIG_SPIRAM
#endif
#endif
bool psramInit();
bool psramAddToHeap();
bool psramFound();
void *ps_malloc(size_t size);
void *ps_calloc(size_t n, size_t size);
void *ps_realloc(void *ptr, size_t size);
#ifdef __cplusplus
}
#endif
#endif /* _ESP32_HAL_PSRAM_H_ */
+98
View File
@@ -0,0 +1,98 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "soc/soc_caps.h"
#include "esp32-hal-rgb-led.h"
// Backward compatibility - Deprecated. It will be removed in future releases.
void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val) {
log_w("neopixelWrite() is deprecated. Use rgbLedWrite().");
rgbLedWrite(pin, red_val, green_val, blue_val);
}
void rgbLedWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val) {
rgbLedWriteOrdered(pin, RGB_BUILTIN_LED_COLOR_ORDER, red_val, green_val, blue_val);
}
void rgbLedWriteOrdered(uint8_t pin, rgb_led_color_order_t order, uint8_t red_val, uint8_t green_val, uint8_t blue_val) {
#if SOC_RMT_SUPPORTED
rmt_data_t led_data[24];
// Verify if the pin used is RGB_BUILTIN and fix GPIO number
#ifdef RGB_BUILTIN
pin = pin == RGB_BUILTIN ? pin - SOC_GPIO_PIN_COUNT : pin;
#endif
if (!rmtInit(pin, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, 10000000)) {
log_e("RGB LED driver initialization failed for GPIO%d!", pin);
return;
}
// default WS2812B color order is G, R, B
int color[3] = {green_val, red_val, blue_val};
switch (order) {
case LED_COLOR_ORDER_RGB:
color[0] = red_val;
color[1] = green_val;
color[2] = blue_val;
break;
case LED_COLOR_ORDER_BGR:
color[0] = blue_val;
color[1] = green_val;
color[2] = red_val;
break;
case LED_COLOR_ORDER_BRG:
color[0] = blue_val;
color[1] = red_val;
color[2] = green_val;
break;
case LED_COLOR_ORDER_RBG:
color[0] = red_val;
color[1] = blue_val;
color[2] = green_val;
break;
case LED_COLOR_ORDER_GBR:
color[0] = green_val;
color[1] = blue_val;
color[2] = red_val;
break;
default: // GRB
break;
}
int i = 0;
for (int col = 0; col < 3; col++) {
for (int bit = 0; bit < 8; bit++) {
if ((color[col] & (1 << (7 - bit)))) {
// HIGH bit
led_data[i].level0 = 1; // T1H
led_data[i].duration0 = 8; // 0.8us
led_data[i].level1 = 0; // T1L
led_data[i].duration1 = 4; // 0.4us
} else {
// LOW bit
led_data[i].level0 = 1; // T0H
led_data[i].duration0 = 4; // 0.4us
led_data[i].level1 = 0; // T0L
led_data[i].duration1 = 8; // 0.8us
}
i++;
}
}
rmtWrite(pin, led_data, RMT_SYMBOLS_OF(led_data), RMT_WAIT_FOR_EVER);
#else
log_e("RMT is not supported on " CONFIG_IDF_TARGET);
#endif /* SOC_RMT_SUPPORTED */
}
+40
View File
@@ -0,0 +1,40 @@
#ifndef MAIN_ESP32_HAL_RGB_LED_H_
#define MAIN_ESP32_HAL_RGB_LED_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "esp32-hal.h"
#ifndef RGB_BRIGHTNESS
#define RGB_BRIGHTNESS 64
#endif
#ifndef RGB_BUILTIN_LED_COLOR_ORDER
#define RGB_BUILTIN_LED_COLOR_ORDER LED_COLOR_ORDER_GRB // default WS2812B color order
#endif
typedef enum {
LED_COLOR_ORDER_RGB,
LED_COLOR_ORDER_BGR,
LED_COLOR_ORDER_BRG,
LED_COLOR_ORDER_RBG,
LED_COLOR_ORDER_GBR,
LED_COLOR_ORDER_GRB
} rgb_led_color_order_t;
void rgbLedWriteOrdered(uint8_t pin, rgb_led_color_order_t order, uint8_t red_val, uint8_t green_val, uint8_t blue_val);
// Will use RGB_BUILTIN_LED_COLOR_ORDER
void rgbLedWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val);
// Backward compatibility - Deprecated. It will be removed in future releases.
[[deprecated("Use rgbLedWrite() instead.")]]
void neopixelWrite(uint8_t p, uint8_t r, uint8_t g, uint8_t b);
#ifdef __cplusplus
}
#endif
#endif /* MAIN_ESP32_HAL_RGB_LED_H_ */
+675
View File
@@ -0,0 +1,675 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "soc/soc_caps.h"
#if SOC_RMT_SUPPORTED
#include "esp32-hal.h"
#include "driver/gpio.h"
#include "driver/rmt_tx.h"
#include "driver/rmt_rx.h"
#include "hal/rmt_ll.h"
#include "esp32-hal-rmt.h"
#include "esp32-hal-periman.h"
#include "esp_idf_version.h"
// Arduino Task Handle indicates if the Arduino Task has been started already
extern TaskHandle_t loopTaskHandle;
// RMT Events
#define RMT_FLAG_RX_DONE (1)
#define RMT_FLAG_TX_DONE (2)
/**
Internal macros
*/
#if CONFIG_DISABLE_HAL_LOCKS
#define RMT_MUTEX_LOCK(busptr)
#define RMT_MUTEX_UNLOCK(busptr)
#else
#define RMT_MUTEX_LOCK(busptr) \
do { \
} while (xSemaphoreTake(busptr->g_rmt_objlocks, portMAX_DELAY) != pdPASS)
#define RMT_MUTEX_UNLOCK(busptr) xSemaphoreGive(busptr->g_rmt_objlocks)
#endif /* CONFIG_DISABLE_HAL_LOCKS */
/**
Typedefs for internal structures, enums
*/
struct rmt_obj_s {
// general RMT information
rmt_channel_handle_t rmt_channel_h; // IDF RMT channel handler
rmt_encoder_handle_t rmt_copy_encoder_h; // RMT simple copy encoder handle
uint32_t signal_range_min_ns; // RX Filter data - Low Pass pulse width
uint32_t signal_range_max_ns; // RX idle time that defines end of reading
EventGroupHandle_t rmt_events; // read/write done event RMT callback handle
bool rmt_ch_is_looping; // Is this RMT TX Channel in LOOPING MODE?
size_t *num_symbols_read; // Pointer to the number of RMT symbol read by IDF RMT RX Done
rmt_reserve_memsize_t mem_size; // RMT Memory size
uint32_t frequency_Hz; // RMT Frequency
uint8_t rmt_EOT_Level; // RMT End of Transmission Level - default is LOW
#if !CONFIG_DISABLE_HAL_LOCKS
SemaphoreHandle_t g_rmt_objlocks; // Channel Semaphore Lock
#endif /* CONFIG_DISABLE_HAL_LOCKS */
};
typedef struct rmt_obj_s *rmt_bus_handle_t;
/**
Internal variables used in RMT API
*/
static SemaphoreHandle_t g_rmt_block_lock = NULL;
/**
Internal method (private) declarations
*/
// This is called from an IDF ISR code, therefore this code is part of an ISR
static bool _rmt_rx_done_callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *data, void *args) {
BaseType_t high_task_wakeup = pdFALSE;
rmt_bus_handle_t bus = (rmt_bus_handle_t)args;
// sets the returning number of RMT symbols (32 bits) effectively read
*bus->num_symbols_read = data->num_symbols;
// set RX event group and signal the received RMT symbols of that channel
xEventGroupSetBitsFromISR(bus->rmt_events, RMT_FLAG_RX_DONE, &high_task_wakeup);
// A "need to yield" is returned in order to execute portYIELD_FROM_ISR() in the main IDF RX ISR
return high_task_wakeup == pdTRUE;
}
// This is called from an IDF ISR code, therefore this code is part of an ISR
static bool _rmt_tx_done_callback(rmt_channel_handle_t channel, const rmt_tx_done_event_data_t *data, void *args) {
BaseType_t high_task_wakeup = pdFALSE;
rmt_bus_handle_t bus = (rmt_bus_handle_t)args;
// set RX event group and signal the received RMT symbols of that channel
xEventGroupSetBitsFromISR(bus->rmt_events, RMT_FLAG_TX_DONE, &high_task_wakeup);
// A "need to yield" is returned in order to execute portYIELD_FROM_ISR() in the main IDF RX ISR
return high_task_wakeup == pdTRUE;
}
// This function must be called only after checking the pin and its bus with _rmtGetBus()
static bool _rmtCheckDirection(uint8_t gpio_num, rmt_ch_dir_t rmt_dir, const char *labelFunc) {
// gets bus RMT direction from the Peripheral Manager information
rmt_ch_dir_t bus_rmt_dir = perimanGetPinBusType(gpio_num) == ESP32_BUS_TYPE_RMT_TX ? RMT_TX_MODE : RMT_RX_MODE;
if (bus_rmt_dir == rmt_dir) { // matches expected RX/TX channel
return true;
}
// print error message
if (rmt_dir == RMT_RX_MODE) {
log_w("==>%s():Channel set as TX instead of RX.", labelFunc);
} else {
log_w("==>%s():Channel set as RX instead of TX.", labelFunc);
}
return false; // mismatched
}
static rmt_bus_handle_t _rmtGetBus(int pin, const char *labelFunc) {
// Is pin RX or TX? Let's find it out
peripheral_bus_type_t rmt_bus_type = perimanGetPinBusType(pin);
if (rmt_bus_type != ESP32_BUS_TYPE_RMT_TX && rmt_bus_type != ESP32_BUS_TYPE_RMT_RX) {
log_e("==>%s():GPIO %u is not attached to an RMT channel.", labelFunc, pin);
return NULL;
}
return (rmt_bus_handle_t)perimanGetPinBus(pin, rmt_bus_type);
}
// Peripheral Manager detach callback
static bool _rmtDetachBus(void *busptr) {
// sanity check - it should never happen
assert(busptr && "_rmtDetachBus bus NULL pointer.");
bool retCode = true;
rmt_bus_handle_t bus = (rmt_bus_handle_t)busptr;
log_v("Detaching RMT GPIO Bus");
// lock it
while (xSemaphoreTake(g_rmt_block_lock, portMAX_DELAY) != pdPASS) {}
// free Event Group
if (bus->rmt_events != NULL) {
vEventGroupDelete(bus->rmt_events);
bus->rmt_events = NULL;
}
// deallocate the channel encoder
if (bus->rmt_copy_encoder_h != NULL) {
if (ESP_OK != rmt_del_encoder(bus->rmt_copy_encoder_h)) {
log_w("RMT Encoder Deletion has failed.");
retCode = false;
}
}
// disable and deallocate RMT channel
if (bus->rmt_channel_h != NULL) {
// force stopping rmt TX/RX processing and unlock Power Management (APB Freq)
rmt_disable(bus->rmt_channel_h);
if (ESP_OK != rmt_del_channel(bus->rmt_channel_h)) {
log_w("RMT Channel Deletion has failed.");
retCode = false;
}
}
#if !CONFIG_DISABLE_HAL_LOCKS
// deallocate channel semaphore
if (bus->g_rmt_objlocks != NULL) {
vSemaphoreDelete(bus->g_rmt_objlocks);
}
#endif
// free the allocated bus data structure
free(bus);
// release the mutex
xSemaphoreGive(g_rmt_block_lock);
return retCode;
}
/**
Public method definitions
*/
bool rmtSetEOT(int pin, uint8_t EOT_Level) {
rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__);
if (bus == NULL) {
return false;
}
if (!_rmtCheckDirection(pin, RMT_TX_MODE, __FUNCTION__)) {
return false;
}
bus->rmt_EOT_Level = EOT_Level > 0 ? 1 : 0;
return true;
}
bool rmtSetCarrier(int pin, bool carrier_en, bool carrier_level, uint32_t frequency_Hz, float duty_percent) {
rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__);
if (bus == NULL) {
return false;
}
if (duty_percent > 1) {
log_w("GPIO %d - RMT Carrier must be a float percentage from 0 to 1. Setting to 50%.", pin);
duty_percent = 0.5;
}
rmt_carrier_config_t carrier_cfg;
memset((void *)&carrier_cfg, 0, sizeof(rmt_carrier_config_t));
carrier_cfg.duty_cycle = duty_percent; // duty cycle
carrier_cfg.frequency_hz = carrier_en ? frequency_Hz : 0; // carrier frequency in Hz
carrier_cfg.flags.polarity_active_low = carrier_level; // carrier modulation polarity level
bool retCode = true;
RMT_MUTEX_LOCK(bus);
// modulate carrier to TX channel
if (ESP_OK != rmt_apply_carrier(bus->rmt_channel_h, &carrier_cfg)) {
log_w("GPIO %d - Error applying RMT carrier.", pin);
retCode = false;
}
RMT_MUTEX_UNLOCK(bus);
return retCode;
}
bool rmtSetRxMinThreshold(int pin, uint8_t filter_pulse_ticks) {
rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__);
if (bus == NULL) {
return false;
}
if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) {
return false;
}
uint32_t filter_pulse_ns = (1000000000 / bus->frequency_Hz) * filter_pulse_ticks;
// RMT_LL_MAX_FILTER_VALUE is 255 for ESP32, S2, S3, C3, C6 and H2;
// filter_pulse_ticks is 8 bits, thus it will not exceed 255
#if 0 // for the future, in case some other SoC has different limit
if (filter_pulse_ticks > RMT_LL_MAX_FILTER_VALUE) {
log_e("filter_pulse_ticks is too big. Max = %d", RMT_LL_MAX_FILTER_VALUE);
return false;
}
#endif
RMT_MUTEX_LOCK(bus);
bus->signal_range_min_ns = filter_pulse_ns; // set zero to disable it
RMT_MUTEX_UNLOCK(bus);
return true;
}
bool rmtSetRxMaxThreshold(int pin, uint16_t idle_thres_ticks) {
rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__);
if (bus == NULL) {
return false;
}
if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) {
return false;
}
uint32_t idle_thres_ns = (1000000000 / bus->frequency_Hz) * idle_thres_ticks;
// RMT_LL_MAX_IDLE_VALUE is 65535 for ESP32,S2 and 32767 for S3, C3, C6 and H2
#if RMT_LL_MAX_IDLE_VALUE < 65535 // idle_thres_ticks is 16 bits anyway - save some bytes
if (idle_thres_ticks > RMT_LL_MAX_IDLE_VALUE) {
log_e("idle_thres_ticks is too big. Max = %ld", RMT_LL_MAX_IDLE_VALUE);
return false;
}
#endif
RMT_MUTEX_LOCK(bus);
bus->signal_range_max_ns = idle_thres_ns;
RMT_MUTEX_UNLOCK(bus);
return true;
}
bool rmtDeinit(int pin) {
log_v("Deiniting RMT GPIO %d", pin);
if (_rmtGetBus(pin, __FUNCTION__) != NULL) {
// release all allocated data
return perimanClearPinBus(pin);
}
log_e("GPIO %d - No RMT channel associated.", pin);
return false;
}
static bool _rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, bool blocking, uint32_t loop, uint32_t timeout_ms) {
rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__);
if (bus == NULL) {
return false;
}
if (!_rmtCheckDirection(pin, RMT_TX_MODE, __FUNCTION__)) {
return false;
}
bool loopCancel = false; // user wants to cancel the writing loop mode
if (data == NULL || num_rmt_symbols == 0) {
if (!loop) {
log_w("GPIO %d - RMT Write Data NULL pointer or size is zero.", pin);
return false;
} else {
loopCancel = true;
}
}
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE
log_v("GPIO: %d - Request: %d RMT Symbols - %s - Timeout: %d", pin, num_rmt_symbols, blocking ? "Blocking" : "Non-Blocking", timeout_ms);
// loop parameter semantics:
// loop == 0: no looping (single transmission)
// loop == 1: infinite looping
// loop > 1: transmit the data 'loop' times
{
char buf[17]; // placeholder for up to maximum uint32_t value (4294967295) = 10 digits + " times" (6 chars) + null terminator (17 bytes)
snprintf(buf, sizeof(buf), "%lu times", loop);
log_v(
"GPIO: %d - Currently in Loop Mode: [%s] | Loop Request: [%s], LoopCancel: [%s]", pin, bus->rmt_ch_is_looping ? "YES" : "NO",
loop == 0 ? "NO" : (loop == 1 ? "FOREVER" : buf), loopCancel ? "YES" : "NO"
);
}
#endif
if ((xEventGroupGetBits(bus->rmt_events) & RMT_FLAG_TX_DONE) == 0) {
log_v("GPIO %d - RMT Write still pending to be completed.", pin);
return false;
}
rmt_transmit_config_t transmit_cfg; // loop mode disabled
memset((void *)&transmit_cfg, 0, sizeof(rmt_transmit_config_t));
bool retCode = true;
RMT_MUTEX_LOCK(bus);
// wants to start in writing or looping over a previous looping --> resets the channel
if (bus->rmt_ch_is_looping == true) {
// must force stopping a previous loop transmission first
rmt_disable(bus->rmt_channel_h);
// enable it again for looping or writing
rmt_enable(bus->rmt_channel_h);
bus->rmt_ch_is_looping = false; // not looping anymore
}
// sets the End of Transmission level to HIGH if the user has requested so
if (bus->rmt_EOT_Level) {
transmit_cfg.flags.eot_level = 1; // EOT is HIGH
}
if (loopCancel) {
// just resets and releases the channel, maybe, already done above, then exits
bus->rmt_ch_is_looping = false;
} else { // new writing | looping request
// looping | Writing over a previous looping state is valid
if (loop > 0) {
transmit_cfg.loop_count = (loop == 1) ? -1 : loop;
// keeps RMT_FLAG_TX_DONE set - it never changes
} else {
// looping mode never sets this flag (IDF 5.1) in the callback
xEventGroupClearBits(bus->rmt_events, RMT_FLAG_TX_DONE);
}
// transmits just once or looping data
if (ESP_OK != rmt_transmit(bus->rmt_channel_h, bus->rmt_copy_encoder_h, (const void *)data, num_rmt_symbols * sizeof(rmt_data_t), &transmit_cfg)) {
retCode = false;
log_w("GPIO %d - RMT Transmission failed.", pin);
} else { // transmit OK
if (loop > 0) {
// rmt_ch_is_looping is used as a flag to indicate that RMT is in looping execution in order to
// be canceled whenever a new _rmtWrite() is executed while it is looping
bus->rmt_ch_is_looping = true;
} else {
if (blocking) {
// wait for transmission confirmation | timeout
retCode = (xEventGroupWaitBits(bus->rmt_events, RMT_FLAG_TX_DONE, pdFALSE /* do not clear on exit */, pdFALSE /* wait for all bits */, timeout_ms)
& RMT_FLAG_TX_DONE)
!= 0;
}
}
}
}
RMT_MUTEX_UNLOCK(bus);
return retCode;
}
static bool _rmtRead(int pin, rmt_data_t *data, size_t *num_rmt_symbols, bool waitForData, uint32_t timeout_ms) {
rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__);
if (bus == NULL) {
return false;
}
if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) {
return false;
}
if (data == NULL || num_rmt_symbols == NULL) {
log_w("GPIO %d - RMT Read Data and/or Size NULL pointer.", pin);
return false;
}
log_v("GPIO: %d - Request: %d RMT Symbols - %s - Timeout: %d", pin, *num_rmt_symbols, waitForData ? "Blocking" : "Non-Blocking", timeout_ms);
bool retCode = true;
RMT_MUTEX_LOCK(bus);
// request reading RMT Channel Data
rmt_receive_config_t receive_config;
memset((void *)&receive_config, 0, sizeof(rmt_receive_config_t));
receive_config.signal_range_min_ns = bus->signal_range_min_ns;
receive_config.signal_range_max_ns = bus->signal_range_max_ns;
xEventGroupClearBits(bus->rmt_events, RMT_FLAG_RX_DONE);
bus->num_symbols_read = num_rmt_symbols;
if (waitForData) {
// resets the reading channel to start fresh
rmt_disable(bus->rmt_channel_h);
rmt_enable(bus->rmt_channel_h);
}
rmt_receive(bus->rmt_channel_h, data, *num_rmt_symbols * sizeof(rmt_data_t), &receive_config);
// wait for data if requested
if (waitForData) {
retCode = (xEventGroupWaitBits(bus->rmt_events, RMT_FLAG_RX_DONE, pdFALSE /* do not clear on exit */, pdFALSE /* wait for all bits */, timeout_ms)
& RMT_FLAG_RX_DONE)
!= 0;
}
RMT_MUTEX_UNLOCK(bus);
return retCode;
}
bool rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t timeout_ms) {
return _rmtWrite(pin, data, num_rmt_symbols, true /*blocks*/, 0 /*looping*/, timeout_ms);
}
bool rmtWriteAsync(int pin, rmt_data_t *data, size_t num_rmt_symbols) {
return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, 0 /*looping*/, 0 /*N/A*/);
}
bool rmtWriteLooping(int pin, rmt_data_t *data, size_t num_rmt_symbols) {
return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, 1 /*looping*/, 0 /*N/A*/);
}
// Same as rmtWriteLooping(...) but it transmits the data a fixed number of times ("loop_count").
// loop_count == 0 is invalid (no transmission); loop_count == 1 transmits once (no looping); loop_count > 1 transmits the data repeatedly (looping).
bool rmtWriteRepeated(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t loop_count) {
if (loop_count == 0) {
log_e("RMT TX GPIO %d : Invalid loop_count (%u). Must be at least 1.", pin, loop_count);
return false;
}
if (loop_count == 1) {
// send the RMT symbols once using non-blocking write (single non-looping transmission)
return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, 0 /*looping*/, 0 /*N/A*/);
} else {
// write the RMT symbols for loop_count times
#if SOC_RMT_SUPPORT_TX_LOOP_COUNT
return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, loop_count /*looping*/, 0 /*N/A*/);
#else
log_e("RMT TX GPIO %d : Loop Count is not supported. Writing failed.", pin);
return false;
#endif
}
}
bool rmtTransmitCompleted(int pin) {
rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__);
if (bus == NULL) {
return false;
}
if (!_rmtCheckDirection(pin, RMT_TX_MODE, __FUNCTION__)) {
return false;
}
bool retCode = true;
RMT_MUTEX_LOCK(bus);
retCode = (xEventGroupGetBits(bus->rmt_events) & RMT_FLAG_TX_DONE) != 0;
RMT_MUTEX_UNLOCK(bus);
return retCode;
}
bool rmtRead(int pin, rmt_data_t *data, size_t *num_rmt_symbols, uint32_t timeout_ms) {
return _rmtRead(pin, data, num_rmt_symbols, true /* blocking */, timeout_ms);
}
bool rmtReadAsync(int pin, rmt_data_t *data, size_t *num_rmt_symbols) {
return _rmtRead(pin, data, num_rmt_symbols, false /* non-blocking */, 0 /* N/A */);
}
bool rmtReceiveCompleted(int pin) {
rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__);
if (bus == NULL) {
return false;
}
if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) {
return false;
}
bool retCode = true;
RMT_MUTEX_LOCK(bus);
retCode = (xEventGroupGetBits(bus->rmt_events) & RMT_FLAG_RX_DONE) != 0;
RMT_MUTEX_UNLOCK(bus);
return retCode;
}
bool rmtInit(int pin, rmt_ch_dir_t channel_direction, rmt_reserve_memsize_t mem_size, uint32_t frequency_Hz) {
log_v(
"GPIO %d - %s - MemSize[%d] - Freq=%dHz", pin, channel_direction == RMT_RX_MODE ? "RX MODE" : "TX MODE", mem_size * RMT_SYMBOLS_PER_CHANNEL_BLOCK,
frequency_Hz
);
// create common block mutex for protecting allocs from multiple threads allocating RMT channels
if (!g_rmt_block_lock) {
g_rmt_block_lock = xSemaphoreCreateMutex();
if (g_rmt_block_lock == NULL) {
log_e("GPIO %d - Failed creating RMT Mutex.", pin);
return false;
}
}
// check if the RMT peripheral is already initialized with the same parameters
rmt_bus_handle_t bus = NULL;
peripheral_bus_type_t rmt_bus_type = perimanGetPinBusType(pin);
if (rmt_bus_type == ESP32_BUS_TYPE_RMT_TX || rmt_bus_type == ESP32_BUS_TYPE_RMT_RX) {
rmt_ch_dir_t bus_rmt_dir = rmt_bus_type == ESP32_BUS_TYPE_RMT_TX ? RMT_TX_MODE : RMT_RX_MODE;
bus = (rmt_bus_handle_t)perimanGetPinBus(pin, rmt_bus_type);
if (bus->frequency_Hz == frequency_Hz && bus_rmt_dir == channel_direction && bus->mem_size == mem_size) {
return true; // already initialized with the same parameters
}
}
// set Peripheral Manager deInit Callback
perimanSetBusDeinit(ESP32_BUS_TYPE_RMT_TX, _rmtDetachBus);
perimanSetBusDeinit(ESP32_BUS_TYPE_RMT_RX, _rmtDetachBus);
// check is pin is valid and in the right direction
if ((channel_direction == RMT_TX_MODE && !GPIO_IS_VALID_OUTPUT_GPIO(pin)) || (!GPIO_IS_VALID_GPIO(pin))) {
log_e("GPIO %d is not valid or can't be used for output in TX mode.", pin);
return false;
}
// validate the RMT ticks by the requested frequency
// Based on 80Mhz using a divider of 8 bits (calculated as 1..256)
if (frequency_Hz > 80000000 || frequency_Hz < 312500) {
log_e("GPIO %d - Bad RMT frequency resolution. Must be between 312.5KHz to 80MHz.", pin);
return false;
}
// Try to detach any (Tx|Rx|Whatever) previous bus or just keep it as not attached
if (!perimanClearPinBus(pin)) {
log_w("GPIO %d - Can't detach previous peripheral.", pin);
return false;
}
// lock it
while (xSemaphoreTake(g_rmt_block_lock, portMAX_DELAY) != pdPASS) {}
// allocate the rmt bus object and sets all fields to NULL
bus = (rmt_bus_handle_t)heap_caps_calloc(1, sizeof(struct rmt_obj_s), MALLOC_CAP_DEFAULT);
if (bus == NULL) {
log_e("GPIO %d - Bus Memory allocation fault.", pin);
goto Err;
}
// store the RMT Freq and mem_size to check Initialization, Filter and Idle valid values in the RMT API
bus->frequency_Hz = frequency_Hz;
bus->mem_size = mem_size;
// pulses with width smaller than min_ns will be ignored (as a glitch)
//bus->signal_range_min_ns = 0; // disabled --> not necessary CALLOC set all to ZERO.
// RMT stops reading if the input stays idle for longer than max_ns
bus->signal_range_max_ns = (1000000000 / frequency_Hz) * RMT_LL_MAX_IDLE_VALUE; // maximum possible
// creates the event group to control read_done and write_done
bus->rmt_events = xEventGroupCreate();
if (bus->rmt_events == NULL) {
log_e("GPIO %d - RMT Group Event allocation fault.", pin);
goto Err;
}
// Starting with Receive|Transmit DONE bits set, for allowing a new request from user
xEventGroupSetBits(bus->rmt_events, RMT_FLAG_RX_DONE | RMT_FLAG_TX_DONE);
// channel particular configuration
if (channel_direction == RMT_TX_MODE) {
// TX Channel
rmt_tx_channel_config_t tx_cfg;
memset((void *)&tx_cfg, 0, sizeof(rmt_tx_channel_config_t));
tx_cfg.gpio_num = pin;
// CLK_APB for ESP32|S2|S3|C3 -- CLK_PLL_F80M for C6 -- CLK_XTAL for H2
tx_cfg.clk_src = RMT_CLK_SRC_DEFAULT;
tx_cfg.resolution_hz = frequency_Hz;
tx_cfg.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL * mem_size;
tx_cfg.trans_queue_depth = 10; // maximum allowed
tx_cfg.flags.invert_out = 0;
tx_cfg.flags.with_dma = 0;
tx_cfg.flags.io_loop_back = 0;
tx_cfg.flags.io_od_mode = 0;
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 2)
tx_cfg.intr_priority = 0;
#endif
if (rmt_new_tx_channel(&tx_cfg, &bus->rmt_channel_h) != ESP_OK) {
log_e("GPIO %d - RMT TX Initialization error.", pin);
goto Err;
}
// set TX Callback
rmt_tx_event_callbacks_t cbs = {.on_trans_done = _rmt_tx_done_callback};
if (ESP_OK != rmt_tx_register_event_callbacks(bus->rmt_channel_h, &cbs, bus)) {
log_e("GPIO %d RMT - Error registering TX Callback.", pin);
goto Err;
}
} else {
// RX Channel
rmt_rx_channel_config_t rx_cfg;
memset((void *)&rx_cfg, 0, sizeof(rmt_rx_channel_config_t));
rx_cfg.gpio_num = pin;
// CLK_APB for ESP32|S2|S3|C3 -- CLK_PLL_F80M for C6 -- CLK_XTAL for H2
rx_cfg.clk_src = RMT_CLK_SRC_DEFAULT;
rx_cfg.resolution_hz = frequency_Hz;
rx_cfg.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL * mem_size;
rx_cfg.flags.invert_in = 0;
rx_cfg.flags.with_dma = 0;
rx_cfg.flags.io_loop_back = 0;
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 2)
rx_cfg.intr_priority = 0;
#endif
// try to allocate the RMT Channel
if (ESP_OK != rmt_new_rx_channel(&rx_cfg, &bus->rmt_channel_h)) {
log_e("GPIO %d RMT - RX Initialization error.", pin);
goto Err;
}
// set RX Callback
rmt_rx_event_callbacks_t cbs = {.on_recv_done = _rmt_rx_done_callback};
if (ESP_OK != rmt_rx_register_event_callbacks(bus->rmt_channel_h, &cbs, bus)) {
log_e("GPIO %d RMT - Error registering RX Callback.", pin);
goto Err;
}
}
// allocate memory for the RMT Copy encoder
rmt_copy_encoder_config_t copy_encoder_config;
memset((void *)&copy_encoder_config, 0, sizeof(rmt_copy_encoder_config_t));
if (rmt_new_copy_encoder(&copy_encoder_config, &bus->rmt_copy_encoder_h) != ESP_OK) {
log_e("GPIO %d - RMT Encoder Memory Allocation error.", pin);
goto Err;
}
// create each channel Mutex for multi thread operations
#if !CONFIG_DISABLE_HAL_LOCKS
bus->g_rmt_objlocks = xSemaphoreCreateMutex();
if (bus->g_rmt_objlocks == NULL) {
log_e("GPIO %d - Failed creating RMT Channel Mutex.", pin);
goto Err;
}
#endif
rmt_enable(bus->rmt_channel_h); // starts/enables the channel
// Finally, allocate Peripheral Manager RMT bus and associate it to its GPIO
peripheral_bus_type_t pinBusType = channel_direction == RMT_TX_MODE ? ESP32_BUS_TYPE_RMT_TX : ESP32_BUS_TYPE_RMT_RX;
if (!perimanSetPinBus(pin, pinBusType, (void *)bus, -1, -1)) {
log_e("Can't allocate the GPIO %d in the Peripheral Manager.", pin);
goto Err;
}
// this delay is necessary when CPU frequency changes, but internal RMT setup is "old/wrong"
// The use case is related to the RMT_CPUFreq_Test example. The very first RMT Write
// goes in the wrong pace (frequency). The delay allows other IDF tasks to run to fix it.
if (loopTaskHandle != NULL) {
// it can only run when Arduino task has been already started.
delay(1);
} // prevent panic when rmtInit() is executed within an C++ object constructor
// release the mutex
xSemaphoreGive(g_rmt_block_lock);
return true;
Err:
// release LOCK and the RMT object
xSemaphoreGive(g_rmt_block_lock);
_rmtDetachBus((void *)bus);
return false;
}
#endif /* SOC_RMT_SUPPORTED */
+240
View File
@@ -0,0 +1,240 @@
// Copyright 2023 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef MAIN_ESP32_HAL_RMT_H_
#define MAIN_ESP32_HAL_RMT_H_
#include "soc/soc_caps.h"
#if SOC_RMT_SUPPORTED
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
RMT_RX_MODE = 0, // false
RMT_TX_MODE = 1, // true
} rmt_ch_dir_t;
typedef enum {
RMT_MEM_NUM_BLOCKS_1 = 1,
RMT_MEM_NUM_BLOCKS_2 = 2,
#if SOC_RMT_TX_CANDIDATES_PER_GROUP > 2
RMT_MEM_NUM_BLOCKS_3 = 3,
RMT_MEM_NUM_BLOCKS_4 = 4,
#if SOC_RMT_TX_CANDIDATES_PER_GROUP > 4
RMT_MEM_NUM_BLOCKS_5 = 5,
RMT_MEM_NUM_BLOCKS_6 = 6,
RMT_MEM_NUM_BLOCKS_7 = 7,
RMT_MEM_NUM_BLOCKS_8 = 8,
#endif
#endif
} rmt_reserve_memsize_t;
// Each RMT Symbols has 4 bytes
// Total number of bytes per RMT_MEM_BLOCK is RMT_SYMBOLS_PER_CHANNEL_BLOCK * 4 bytes
typedef union {
struct {
uint32_t duration0 : 15;
uint32_t level0 : 1;
uint32_t duration1 : 15;
uint32_t level1 : 1;
};
uint32_t val;
} rmt_data_t;
// Reading and Writing shall use as rmt_symbols_size this unit
// ESP32 has 8 MEM BLOCKS in total shared with Reading and/or Writing
// ESP32-S2 has 4 MEM BLOCKS in total shared with Reading and/or Writing
// ESP32-S3 has 4 MEM BLOCKS for Reading and another 4 MEM BLOCKS for Writing
// ESP32-C3 has 2 MEM BLOCKS for Reading and another 2 MEM BLOCKS for Writing
#define RMT_SYMBOLS_PER_CHANNEL_BLOCK SOC_RMT_MEM_WORDS_PER_CHANNEL
// Used to tell rmtRead() to wait for ever until reading data from the RMT channel
#define RMT_WAIT_FOR_EVER ((uint32_t)portMAX_DELAY)
// Helper macro to calculate the number of RTM symbols in a array or type
#define RMT_SYMBOLS_OF(x) (sizeof(x) / sizeof(rmt_data_t))
/**
Initialize the object
New Parameters in Arduino Core 3: RMT tick is set in the rmtInit() function by the
frequency of the RMT channel. Example: 100ns tick => 10MHz, thus frequency will be 10,000,000 Hz
Returns <true> on execution success, <false> otherwise
*/
bool rmtInit(int pin, rmt_ch_dir_t channel_direction, rmt_reserve_memsize_t memsize, uint32_t frequency_Hz);
/**
Sets the End of Transmission level to be set for the <pin> when the RMT transmission ends.
This function affects how rmtWrite(), rmtWriteAsync() or rmtWriteLooping() will set the pin after writing the data.
The default EOT level is LOW, in case this function isn't used before RMT Writing.
This level can be set for each RMT pin and can be changed between writings to the same pin.
<EOT_Level> shall be Zero (LOW) or non-zero (HIGH) value.
It only affects the transmission process, therefore, it doesn't affect any IDLE LEVEL before starting the RMT transmission.
The pre-transmission idle level can be set manually calling, for instance, digitalWrite(pin, Level).
Returns <true> when EOT has been correctly set for <pin>, <false> otherwise.
*/
bool rmtSetEOT(int pin, uint8_t EOT_Level);
/**
Sending data in Blocking Mode.
<rmt_symbol> is a 32 bits structure as defined by rmt_data_t type.
It is possible to use the macro RMT_SYMBOLS_OF(data), if data is an array of <rmt_data_t>.
Blocking mode - only returns after sending all data or by timeout.
If the writing operation takes longer than <timeout_ms> in milliseconds, it will end its
execution returning <false>.
Timeout can be set as undefined time by passing <RMT_WAIT_FOR_EVER> as <timeout_ms> parameter.
When the operation is timed out, rmtTransmitCompleted() will return <false> until the transmission
is finished, when rmtTransmitCompleted() will return <true>.
Returns <true> when there is no error in the write operation, <false> otherwise, including when it
exits by timeout.
*/
bool rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t timeout_ms);
/**
Sending data in Async Mode.
<rmt_symbol> is a 32 bits structure as defined by rmt_data_t type.
It is possible to use the macro RMT_SYMBOLS_OF(data), if <data> is an array of <rmt_data_t>
If more than one rmtWriteAsync() is executed in sequence, it will wait for the first transmission
to finish, resulting in a return <false> that indicates that the rmtWriteAsync() call has failed.
In such case, this channel will have to finish the previous transmission before starting a new one.
Non-Blocking mode - returns right after execution.
Returns <true> on execution success, <false> otherwise.
<bool rmtTransmitCompleted(int pin)> will return <true> when all data is sent.
*/
bool rmtWriteAsync(int pin, rmt_data_t *data, size_t num_rmt_symbols);
/**
Writing data up to the reserved memsize, looping continuously (rmtWriteLooping()) or fixed
number of <loop_count> times (rmtWriteRepeated())
<rmt_symbol> is a 32 bits structure as defined by rmt_data_t type.
It is possible to use the macro RMT_SYMBOLS_OF(data), if data is an array of rmt_data_t
If *data or size_byte are NULL | Zero, it will disable the writing loop and stop transmission
Non-Blocking mode - returns right after execution
Returns <true> on execution success, <false> otherwise
<bool rmtTransmitCompleted(int pin)> will return always <true> while it is looping mode.
looping mode is active for rmtWriteLooping() and for rmtWriteRepeated() when loop_count > 1.
*/
bool rmtWriteLooping(int pin, rmt_data_t *data, size_t num_rmt_symbols);
bool rmtWriteRepeated(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t loop_count);
/**
Checks if transmission is completed and the rmtChannel ready for transmitting new data.
To be ready for a new transmission, means that the previous transmission is completed.
Returns <true> when all data has been sent, <false> otherwise.
The data transmission information is reset when a new rmtWrite/Async function is called.
If rmtWrite() times out or rmtWriteAsync() is called, this function will return <false> until
all data is sent out.
rmtTranmitCompleted() will always return <true> when rmtWriteLooping() is called,
because it has no effect in such case.
*/
bool rmtTransmitCompleted(int pin);
/**
Initiates blocking receive. Read data will be stored in a user provided buffer <*data>
It will read up to <num_rmt_symbols> RMT Symbols and the value of this variable will
change to the effective number of symbols read.
<rmt_symbol> is a 32 bits structure as defined by rmt_data_t type.
If the reading operation takes longer than <timeout_ms> in milliseconds, it will end its
execution and the function will return <false>. In a time out scenario, <num_rmt_symbols> won't
change and rmtReceiveCompleted() can be used latter to check if there is data available.
Timeout can be set as undefined time by passing RMT_WAIT_FOR_EVER as <timeout_ms> parameter
Returns <true> when there is no error in the read operation, <false> otherwise, including when it
exits by timeout.
Returns, by value, the number of RMT Symbols read in <num_rmt_symbols> and the user buffer <data>
when the read operation has success within the defined <timeout_ms>. If the function times out, it
will read RMT data latter asynchronously, affecting <*data> and <*num_rmt_symbols>. After timeout,
the application can check if data is already available using <rmtReceiveCompleted(int pin)>
*/
bool rmtRead(int pin, rmt_data_t *data, size_t *num_rmt_symbols, uint32_t timeout_ms);
/**
Initiates async (non-blocking) receive. It will return immediately after execution.
Read data will be stored in a user provided buffer <*data>.
It will read up to <num_rmt_symbols> RMT Symbols and the value of this variable will
change to the effective number of symbols read, whenever the read is completed.
<rmt_symbol> is a 32 bits structure as defined by <rmt_data_t> type.
Returns <true> when there is no error in the read operation, <false> otherwise.
Returns asynchronously, by value, the number of RMT Symbols read, and also, it will copy
the RMT received data to the user buffer <data> when the read operation happens.
The application can check if data is already available using <rmtReceiveCompleted(int pin)>
*/
bool rmtReadAsync(int pin, rmt_data_t *data, size_t *num_rmt_symbols);
/**
Checks if a data reception is completed and the rmtChannel has new data for processing.
Returns <true> when data has been received, <false> otherwise.
The data reception information is reset when a new rmtRead/Async function is called.
*/
bool rmtReceiveCompleted(int pin);
/**
Function used to set a threshold (in ticks) used to consider that a data reception has ended.
In receive mode, when no edge is detected on the input signal for longer than idle_thres_ticks
time, the receiving process is finished and the Data is made available by
the rmtRead/Async functions. Note that this time (in RMT channel frequency cycles) will also
define how many low/high bits are read at the end of the received data.
The function returns <true> if it is correctly executed, <false> otherwise.
*/
bool rmtSetRxMaxThreshold(int pin, uint16_t idle_thres_ticks);
/**
Parameters changed in Arduino Core 3: low and high (ticks) are now expressed in Carrier Freq in Hz and
duty cycle in percentage float 0.0 to 1.0 - example: 38.5KHz 33% High => 38500, 0.33
Function to set a RX demodulation carrier or TX modulation carrier
<carrier_en> is used to enable/disable the use of demodulation/modulation for RX/TX
<carrier_level> true means that the polarity level for the (de)modulation is positive
<frequency_Hz> is the carrier frequency used
<duty_percent> is a float deom 0 to 1 (0.5 means a square wave) of the carrier frequency
The function returns <true> if it is correctly executed, <false> otherwise.
*/
bool rmtSetCarrier(int pin, bool carrier_en, bool carrier_level, uint32_t frequency_Hz, float duty_percent);
/**
Function used to filter input noise in the RX channel.
In receiving mode, channel will ignore any input pulse which width (high or low)
is smaller than <filter_pulse_ticks>
If <filter_pulse_ns> is Zero, it will to disable the filter.
The function returns <true> if it is correctly executed, <false> otherwise.
*/
bool rmtSetRxMinThreshold(int pin, uint8_t filter_pulse_ticks);
/**
Deinitializes the driver and releases all allocated memory
It also disables RMT for this gpio
*/
bool rmtDeinit(int pin);
#ifdef __cplusplus
}
#endif
#endif /* SOC_RMT_SUPPORTED */
#endif /* MAIN_ESP32_HAL_RMT_H_ */
+83
View File
@@ -0,0 +1,83 @@
/*
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp32-hal-sigmadelta.h"
#if SOC_SDM_SUPPORTED
#include "esp32-hal.h"
#include "esp32-hal-periman.h"
#include "driver/sdm.h"
static bool sigmaDeltaDetachBus(void *bus) {
esp_err_t err = sdm_channel_disable((sdm_channel_handle_t)bus);
if (err != ESP_OK) {
log_w("sdm_channel_disable failed with error: %d", err);
}
err = sdm_del_channel((sdm_channel_handle_t)bus);
if (err != ESP_OK) {
log_e("sdm_del_channel failed with error: %d", err);
return false;
}
return true;
}
bool sigmaDeltaAttach(uint8_t pin, uint32_t freq) //freq 1220-312500
{
perimanSetBusDeinit(ESP32_BUS_TYPE_SIGMADELTA, sigmaDeltaDetachBus);
sdm_channel_handle_t bus = NULL;
// pin may be previously attached to other peripheral -> detach it.
// if attached to sigmaDelta, detach it and set the new frequency
if (perimanGetPinBusType(pin) != ESP32_BUS_TYPE_INIT && !perimanClearPinBus(pin)) {
log_e("Pin %u could not be detached.", pin);
return false;
}
sdm_config_t config = {.gpio_num = (int)pin, .clk_src = SDM_CLK_SRC_DEFAULT, .sample_rate_hz = freq, .flags = {.invert_out = 0, .io_loop_back = 0}};
esp_err_t err = sdm_new_channel(&config, &bus);
if (err != ESP_OK) {
log_e("sdm_new_channel failed with error: %d", err);
return false;
}
err = sdm_channel_enable(bus);
if (err != ESP_OK) {
sigmaDeltaDetachBus((void *)bus);
log_e("sdm_channel_enable failed with error: %d", err);
return false;
}
if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_SIGMADELTA, (void *)bus, -1, -1)) {
sigmaDeltaDetachBus((void *)bus);
return false;
}
return true;
}
bool sigmaDeltaWrite(uint8_t pin, uint8_t duty) //chan 0-x according to SOC duty 8 bit
{
sdm_channel_handle_t bus = (sdm_channel_handle_t)perimanGetPinBus(pin, ESP32_BUS_TYPE_SIGMADELTA);
if (bus != NULL) {
int8_t d = duty - 128;
esp_err_t err = sdm_channel_set_duty(bus, d);
if (err != ESP_OK) {
log_e("sdm_channel_set_duty failed with error: %d", err);
return false;
}
return true;
} else {
log_e("pin %u is not attached to SigmaDelta", pin);
}
return false;
}
bool sigmaDeltaDetach(uint8_t pin) {
void *bus = perimanGetPinBus(pin, ESP32_BUS_TYPE_SIGMADELTA);
if (bus != NULL) {
// will call sigmaDeltaDetachBus
return perimanClearPinBus(pin);
} else {
log_e("pin %u is not attached to SigmaDelta", pin);
}
return false;
}
#endif
+28
View File
@@ -0,0 +1,28 @@
/*
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "soc/soc_caps.h"
#if SOC_SDM_SUPPORTED
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
//freq 1220-312500 duty 0-255
bool sigmaDeltaAttach(uint8_t pin, uint32_t freq);
bool sigmaDeltaWrite(uint8_t pin, uint8_t duty);
bool sigmaDeltaDetach(uint8_t pin);
#ifdef __cplusplus
}
#endif
#endif /* SOC_SDM_SUPPORTED */
File diff suppressed because it is too large Load Diff
+151
View File
@@ -0,0 +1,151 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef MAIN_ESP32_HAL_SPI_H_
#define MAIN_ESP32_HAL_SPI_H_
#include "soc/soc_caps.h"
#if SOC_GPSPI_SUPPORTED
#ifdef __cplusplus
extern "C" {
#endif
#include "sdkconfig.h"
#include <stdint.h>
#include <stdbool.h>
#define SPI_HAS_TRANSACTION
#ifdef CONFIG_IDF_TARGET_ESP32
#define FSPI 1 //SPI 1 bus attached to the flash (can use the same data lines but different SS)
#define HSPI 2 //SPI 2 bus normally mapped to pins 12 - 15, but can be matrixed to any pins
#define VSPI 3 //SPI 3 bus normally attached to pins 5, 18, 19 and 23, but can be matrixed to any pins
#else
#define FSPI 0 // ESP32C2, C3, C5, C6, C61, H2, S2, S3, P4 - SPI 2 bus
#define HSPI 1 // ESP32S2, S3, P4 - SPI 3 bus
#endif
// This defines are not representing the real Divider of the ESP32
// the Defines match to an AVR Arduino on 16MHz for better compatibility
#define SPI_CLOCK_DIV2 0x00101001 //8 MHz
#define SPI_CLOCK_DIV4 0x00241001 //4 MHz
#define SPI_CLOCK_DIV8 0x004c1001 //2 MHz
#define SPI_CLOCK_DIV16 0x009c1001 //1 MHz
#define SPI_CLOCK_DIV32 0x013c1001 //500 KHz
#define SPI_CLOCK_DIV64 0x027c1001 //250 KHz
#define SPI_CLOCK_DIV128 0x04fc1001 //125 KHz
#define SPI_MODE0 0
#define SPI_MODE1 1
#define SPI_MODE2 2
#define SPI_MODE3 3
#define SPI_SS0 0
#define SPI_SS1 1
#define SPI_SS2 2
#define SPI_SS_MASK_ALL 0x7
#define SPI_LSBFIRST 0
#define SPI_MSBFIRST 1
struct spi_struct_t;
typedef struct spi_struct_t spi_t;
spi_t *spiStartBus(uint8_t spi_num, uint32_t clockDiv, uint8_t dataMode, uint8_t bitOrder);
void spiStopBus(spi_t *spi);
//Attach/Detach Signal Pins
bool spiAttachSCK(spi_t *spi, int8_t sck);
bool spiAttachMISO(spi_t *spi, int8_t miso);
bool spiAttachMOSI(spi_t *spi, int8_t mosi);
bool spiDetachSCK(spi_t *spi);
bool spiDetachMISO(spi_t *spi);
bool spiDetachMOSI(spi_t *spi);
//Attach/Detach SS pin to SPI_SSx signal
bool spiAttachSS(spi_t *spi, uint8_t ss_num, int8_t ss);
bool spiDetachSS(spi_t *spi);
//Enable/Disable SPI_SSx pins
void spiEnableSSPins(spi_t *spi, uint8_t ss_mask);
void spiDisableSSPins(spi_t *spi, uint8_t ss_mask);
//Enable/Disable hardware control of SPI_SSx pins
void spiSSEnable(spi_t *spi);
void spiSSDisable(spi_t *spi);
//Activate enabled SPI_SSx pins
void spiSSSet(spi_t *spi);
//Deactivate enabled SPI_SSx pins
void spiSSClear(spi_t *spi);
void spiWaitReady(spi_t *spi);
//invert hardware SS
void spiSSInvert(spi_t *spi, bool invert);
uint32_t spiGetClockDiv(spi_t *spi);
uint8_t spiGetDataMode(spi_t *spi);
uint8_t spiGetBitOrder(spi_t *spi);
/*
* Non transaction based lock methods (each locks and unlocks when called)
* */
void spiSetClockDiv(spi_t *spi, uint32_t clockDiv);
void spiSetDataMode(spi_t *spi, uint8_t dataMode);
void spiSetBitOrder(spi_t *spi, uint8_t bitOrder);
void spiWrite(spi_t *spi, const uint32_t *data, uint8_t len);
void spiWriteByte(spi_t *spi, uint8_t data);
void spiWriteWord(spi_t *spi, uint16_t data);
void spiWriteLong(spi_t *spi, uint32_t data);
void spiTransfer(spi_t *spi, uint32_t *out, uint8_t len);
uint8_t spiTransferByte(spi_t *spi, uint8_t data);
uint16_t spiTransferWord(spi_t *spi, uint16_t data);
uint32_t spiTransferLong(spi_t *spi, uint32_t data);
void spiTransferBytes(spi_t *spi, const uint8_t *data, uint8_t *out, uint32_t size);
void spiTransferBits(spi_t *spi, uint32_t data, uint32_t *out, uint8_t bits);
/*
* New (EXPERIMENTAL) Transaction lock based API (lock once until endTransaction)
* */
void spiTransaction(spi_t *spi, uint32_t clockDiv, uint8_t dataMode, uint8_t bitOrder);
void spiSimpleTransaction(spi_t *spi);
void spiEndTransaction(spi_t *spi);
void spiWriteNL(spi_t *spi, const void *data_in, uint32_t len);
void spiWriteByteNL(spi_t *spi, uint8_t data);
void spiWriteShortNL(spi_t *spi, uint16_t data);
void spiWriteLongNL(spi_t *spi, uint32_t data);
void spiWritePixelsNL(spi_t *spi, const void *data_in, uint32_t len);
#define spiTransferNL(spi, data, len) spiTransferBytesNL(spi, data, data, len)
uint8_t spiTransferByteNL(spi_t *spi, uint8_t data);
uint16_t spiTransferShortNL(spi_t *spi, uint16_t data);
uint32_t spiTransferLongNL(spi_t *spi, uint32_t data);
void spiTransferBytesNL(spi_t *spi, const void *data_in, uint8_t *data_out, uint32_t len);
void spiTransferBitsNL(spi_t *spi, uint32_t data_in, uint32_t *data_out, uint8_t bits);
/*
* Helper functions to translate frequency to clock divider and back
* */
uint32_t spiFrequencyToClockDiv(spi_t *spi, uint32_t freq);
uint32_t spiClockDivToFrequency(spi_t *spi, uint32_t clockDiv);
#ifdef __cplusplus
}
#endif
#endif /* SOC_GPSPI_SUPPORTED */
#endif /* MAIN_ESP32_HAL_SPI_H_ */
+126
View File
@@ -0,0 +1,126 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "esp32-hal.h"
#include "lwip/apps/sntp.h"
//#include "tcpip_adapter.h"
#include "esp_netif.h"
#ifdef CONFIG_LWIP_TCPIP_CORE_LOCKING
#include "lwip/priv/tcpip_priv.h"
#endif
static void setTimeZone(long offset, int daylight) {
char cst[21] = {0};
char cdt[21] = "DST";
char tz[41] = {0};
if (offset % 3600) {
snprintf(cst, sizeof(cst), "UTC%ld:%02u:%02u", offset / 3600, abs((offset % 3600) / 60), abs(offset % 60));
} else {
snprintf(cst, sizeof(cst), "UTC%ld", offset / 3600);
}
if (daylight != 3600) {
long tz_dst = offset - daylight;
if (tz_dst % 3600) {
snprintf(cdt, sizeof(cdt), "DST%ld:%02u:%02u", tz_dst / 3600, abs((tz_dst % 3600) / 60), abs(tz_dst % 60));
} else {
snprintf(cdt, sizeof(cdt), "DST%ld", tz_dst / 3600);
}
}
snprintf(tz, sizeof(tz), "%s%s", cst, cdt);
setenv("TZ", tz, 1);
tzset();
}
/*
* configTime
* Source: https://github.com/esp8266/Arduino/blob/master/cores/esp8266/time.c
* */
void configTime(long gmtOffset_sec, int daylightOffset_sec, const char *server1, const char *server2, const char *server3) {
//tcpip_adapter_init(); // Should not hurt anything if already inited
esp_netif_init();
#ifdef CONFIG_LWIP_TCPIP_CORE_LOCKING
if (!sys_thread_tcpip(LWIP_CORE_LOCK_QUERY_HOLDER)) {
LOCK_TCPIP_CORE();
}
#endif
if (sntp_enabled()) {
sntp_stop();
}
sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_setservername(0, (char *)server1);
sntp_setservername(1, (char *)server2);
sntp_setservername(2, (char *)server3);
sntp_init();
#ifdef CONFIG_LWIP_TCPIP_CORE_LOCKING
if (sys_thread_tcpip(LWIP_CORE_LOCK_QUERY_HOLDER)) {
UNLOCK_TCPIP_CORE();
}
#endif
setTimeZone(-gmtOffset_sec, daylightOffset_sec);
}
/*
* configTzTime
* sntp setup using TZ environment variable
* */
void configTzTime(const char *tz, const char *server1, const char *server2, const char *server3) {
//tcpip_adapter_init(); // Should not hurt anything if already inited
esp_netif_init();
#ifdef CONFIG_LWIP_TCPIP_CORE_LOCKING
if (!sys_thread_tcpip(LWIP_CORE_LOCK_QUERY_HOLDER)) {
LOCK_TCPIP_CORE();
}
#endif
if (sntp_enabled()) {
sntp_stop();
}
sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_setservername(0, (char *)server1);
sntp_setservername(1, (char *)server2);
sntp_setservername(2, (char *)server3);
sntp_init();
#ifdef CONFIG_LWIP_TCPIP_CORE_LOCKING
if (sys_thread_tcpip(LWIP_CORE_LOCK_QUERY_HOLDER)) {
UNLOCK_TCPIP_CORE();
}
#endif
setenv("TZ", tz, 1);
tzset();
}
bool getLocalTime(struct tm *info, uint32_t ms) {
uint32_t start = millis();
time_t now;
while ((millis() - start) <= ms) {
time(&now);
localtime_r(&now, info);
if (info->tm_year > (2016 - 1900)) {
return true;
}
delay(10);
}
return false;
}
+285
View File
@@ -0,0 +1,285 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "esp32-hal-timer.h"
#if SOC_GPTIMER_SUPPORTED
#include "driver/gptimer.h"
#if defined __has_include && __has_include("clk_tree.h")
#include "clk_tree.h"
#else
#include "esp_clk_tree.h"
#endif
#if CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM
#define TIMER_IRAM IRAM_ATTR
#else
#define TIMER_IRAM
#endif
typedef void (*voidFuncPtr)(void);
typedef void (*voidFuncPtrArg)(void *);
typedef struct {
voidFuncPtr fn;
void *arg;
} interrupt_config_t;
struct timer_struct_t {
gptimer_handle_t timer_handle;
interrupt_config_t interrupt_handle;
bool timer_started;
};
inline TIMER_IRAM uint64_t timerRead(hw_timer_t *timer) {
if (timer == NULL) {
#ifndef CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM
log_e("Timer handle is NULL");
#endif
return 0;
}
uint64_t value;
gptimer_get_raw_count(timer->timer_handle, &value);
return value;
}
void TIMER_IRAM timerWrite(hw_timer_t *timer, uint64_t val) {
if (timer == NULL) {
#ifndef CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM
log_e("Timer handle is NULL");
#endif
return;
}
gptimer_set_raw_count(timer->timer_handle, val);
}
void TIMER_IRAM timerAlarm(hw_timer_t *timer, uint64_t alarm_value, bool autoreload, uint64_t reload_count) {
if (timer == NULL) {
#ifndef CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM
log_e("Timer handle is NULL");
#endif
return;
}
esp_err_t err = ESP_OK;
gptimer_alarm_config_t alarm_cfg = {
.alarm_count = alarm_value,
.reload_count = reload_count,
.flags.auto_reload_on_alarm = autoreload,
};
err = gptimer_set_alarm_action(timer->timer_handle, &alarm_cfg);
if (err != ESP_OK) {
#ifndef CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM
log_e("Timer Alarm Write failed, error num=%d", err);
#endif
}
}
uint32_t timerGetFrequency(hw_timer_t *timer) {
if (timer == NULL) {
return 0;
}
uint32_t frequency;
gptimer_get_resolution(timer->timer_handle, &frequency);
return frequency;
}
void TIMER_IRAM timerStart(hw_timer_t *timer) {
if (timer == NULL) {
#ifndef CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM
log_e("Timer handle is NULL");
#endif
return;
}
gptimer_start(timer->timer_handle);
timer->timer_started = true;
}
void TIMER_IRAM timerStop(hw_timer_t *timer) {
if (timer == NULL) {
#ifndef CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM
log_e("Timer handle is NULL");
#endif
return;
}
gptimer_stop(timer->timer_handle);
timer->timer_started = false;
}
void TIMER_IRAM timerRestart(hw_timer_t *timer) {
if (timer == NULL) {
#ifndef CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM
log_e("Timer handle is NULL");
#endif
return;
}
gptimer_set_raw_count(timer->timer_handle, 0);
}
hw_timer_t *timerBegin(uint32_t frequency) {
esp_err_t err = ESP_OK;
uint32_t counter_src_hz = 0;
uint32_t divider = 0;
soc_periph_gptimer_clk_src_t clk;
soc_periph_gptimer_clk_src_t gptimer_clks[] = SOC_GPTIMER_CLKS;
for (size_t i = 0; i < sizeof(gptimer_clks) / sizeof(gptimer_clks[0]); i++) {
clk = gptimer_clks[i];
#if defined __has_include && __has_include("clk_tree.h")
clk_tree_src_get_freq_hz(clk, CLK_TREE_SRC_FREQ_PRECISION_CACHED, &counter_src_hz);
#else
esp_clk_tree_src_get_freq_hz(clk, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &counter_src_hz);
#endif
divider = counter_src_hz / frequency;
if ((divider >= 2) && (divider <= 65536)) {
break;
} else {
divider = 0;
}
}
if (divider == 0) {
log_e("Resolution cannot be reached with any clock source, aborting!");
return NULL;
}
gptimer_config_t config = {
.clk_src = clk,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = frequency,
.flags.intr_shared = true,
};
hw_timer_t *timer = malloc(sizeof(hw_timer_t));
err = gptimer_new_timer(&config, &timer->timer_handle);
if (err != ESP_OK) {
log_e("Failed to create a new GPTimer, error num=%d", err);
free(timer);
return NULL;
}
gptimer_enable(timer->timer_handle);
gptimer_start(timer->timer_handle);
timer->timer_started = true;
return timer;
}
void timerEnd(hw_timer_t *timer) {
if (timer != NULL) {
esp_err_t err = ESP_OK;
if (timer->timer_started == true) {
gptimer_stop(timer->timer_handle);
}
gptimer_disable(timer->timer_handle);
err = gptimer_del_timer(timer->timer_handle);
if (err != ESP_OK) {
log_e("Failed to destroy GPTimer, error num=%d", err);
return;
}
free(timer);
}
}
bool IRAM_ATTR timerFnWrapper(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *args) {
interrupt_config_t *isr = (interrupt_config_t *)args;
if (isr->fn) {
if (isr->arg) {
((voidFuncPtrArg)isr->fn)(isr->arg);
} else {
isr->fn();
}
}
// some additional logic or handling may be required here to appropriately yield or not
return false;
}
void timerAttachInterruptFunctionalArg(hw_timer_t *timer, void (*userFunc)(void *), void *arg) {
if (timer == NULL) {
log_e("Timer handle is NULL");
return;
}
esp_err_t err = ESP_OK;
gptimer_event_callbacks_t cbs = {
.on_alarm = timerFnWrapper,
};
timer->interrupt_handle.fn = (voidFuncPtr)userFunc;
timer->interrupt_handle.arg = arg;
if (timer->timer_started == true) {
gptimer_stop(timer->timer_handle);
}
gptimer_disable(timer->timer_handle);
err = gptimer_register_event_callbacks(timer->timer_handle, &cbs, &timer->interrupt_handle);
if (err != ESP_OK) {
log_e("Timer Attach Interrupt failed, error num=%d", err);
}
gptimer_enable(timer->timer_handle);
if (timer->timer_started == true) {
gptimer_start(timer->timer_handle);
}
}
void timerAttachInterruptArg(hw_timer_t *timer, void (*userFunc)(void *), void *arg) {
timerAttachInterruptFunctionalArg(timer, userFunc, arg);
}
void timerAttachInterrupt(hw_timer_t *timer, voidFuncPtr userFunc) {
timerAttachInterruptFunctionalArg(timer, (voidFuncPtrArg)userFunc, NULL);
}
void timerDetachInterrupt(hw_timer_t *timer) {
if (timer == NULL) {
log_e("Timer handle is NULL");
return;
}
esp_err_t err = ESP_OK;
err = gptimer_set_alarm_action(timer->timer_handle, NULL);
timer->interrupt_handle.fn = NULL;
timer->interrupt_handle.arg = NULL;
if (err != ESP_OK) {
log_e("Timer Detach Interrupt failed, error num=%d", err);
}
}
uint64_t timerReadMicros(hw_timer_t *timer) {
if (timer == NULL) {
log_e("Timer handle is NULL");
return 0;
}
uint64_t timer_val = timerRead(timer);
uint32_t frequency = timerGetFrequency(timer);
return timer_val * 1000000 / frequency;
}
uint64_t timerReadMillis(hw_timer_t *timer) {
if (timer == NULL) {
log_e("Timer handle is NULL");
return 0;
}
uint64_t timer_val = timerRead(timer);
uint32_t frequency = timerGetFrequency(timer);
return timer_val * 1000 / frequency;
}
double timerReadSeconds(hw_timer_t *timer) {
if (timer == NULL) {
log_e("Timer handle is NULL");
return 0;
}
uint64_t timer_val = timerRead(timer);
uint32_t frequency = timerGetFrequency(timer);
return (double)timer_val / frequency;
}
#endif /* SOC_GPTIMER_SUPPORTED */
+60
View File
@@ -0,0 +1,60 @@
/*
Arduino.h - Main include file for the Arduino SDK
Copyright (c) 2005-2013 Arduino Team. All right 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 "soc/soc_caps.h"
#if SOC_GPTIMER_SUPPORTED
#include "esp32-hal.h"
#include "driver/gptimer_types.h"
#ifdef __cplusplus
extern "C" {
#endif
struct timer_struct_t;
typedef struct timer_struct_t hw_timer_t;
hw_timer_t *timerBegin(uint32_t frequency);
void timerEnd(hw_timer_t *timer);
void timerStart(hw_timer_t *timer);
void timerStop(hw_timer_t *timer);
void timerRestart(hw_timer_t *timer);
void timerWrite(hw_timer_t *timer, uint64_t val);
uint64_t timerRead(hw_timer_t *timer);
uint64_t timerReadMicros(hw_timer_t *timer);
uint64_t timerReadMillis(hw_timer_t *timer);
double timerReadSeconds(hw_timer_t *timer);
uint32_t timerGetFrequency(hw_timer_t *timer);
void timerAttachInterrupt(hw_timer_t *timer, void (*userFunc)(void));
void timerAttachInterruptArg(hw_timer_t *timer, void (*userFunc)(void *), void *arg);
void timerDetachInterrupt(hw_timer_t *timer);
void timerAlarm(hw_timer_t *timer, uint64_t alarm_value, bool autoreload, uint64_t reload_count);
#ifdef __cplusplus
}
#endif
#endif /* SOC_GPTIMER_SUPPORTED */
+931
View File
@@ -0,0 +1,931 @@
#include "soc/soc_caps.h"
#if SOC_USB_OTG_SUPPORTED
#include "sdkconfig.h"
#if CONFIG_TINYUSB_ENABLED
#include <stdlib.h>
#include <stdbool.h>
#include "esp_log.h"
#include "soc/soc.h"
#include "soc/efuse_reg.h"
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
#include "soc/rtc_cntl_reg.h"
#include "soc/usb_struct.h"
#include "soc/usb_reg.h"
#include "soc/usb_wrap_reg.h"
#include "soc/usb_wrap_struct.h"
#include "soc/usb_periph.h"
#endif
#include "soc/periph_defs.h"
#include "soc/timer_group_struct.h"
#include "soc/system_reg.h"
#include "rom/gpio.h"
#include "hal/gpio_ll.h"
#include "hal/clk_gate_ll.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_rom_gpio.h"
#include "esp32-hal.h"
#include "esp32-hal-periman.h"
#include "esp32-hal-tinyusb.h"
#if CONFIG_IDF_TARGET_ESP32S2
#include "esp32s2/rom/usb/usb_persist.h"
#include "esp32s2/rom/usb/usb_dc.h"
#include "esp32s2/rom/usb/chip_usb_dw_wrapper.h"
#elif CONFIG_IDF_TARGET_ESP32S3
#if defined __has_include && __has_include("hal/usb_phy_ll.h")
#include "hal/usb_phy_ll.h"
#elif defined __has_include && __has_include("hal/usb_fsls_phy_ll.h")
#include "hal/usb_fsls_phy_ll.h"
#endif
#include "hal/usb_serial_jtag_ll.h"
#include "esp32s3/rom/usb/usb_persist.h"
#include "esp32s3/rom/usb/usb_dc.h"
#include "esp32s3/rom/usb/chip_usb_dw_wrapper.h"
#elif CONFIG_IDF_TARGET_ESP32P4
#endif
typedef enum {
TINYUSB_USBDEV_0,
} tinyusb_usbdev_t;
typedef char *tusb_desc_strarray_device_t[USB_STRING_DESCRIPTOR_ARRAY_SIZE];
typedef struct {
bool external_phy;
} tinyusb_config_t;
#if __has_include("hal/usb_hal.h")
#include "hal/usb_hal.h"
static bool usb_otg_deinit(void *busptr) {
// Once USB OTG is initialized, its GPIOs are assigned and it shall never be deinited
// except when S3 swithicng usb from cdc to jtag while resetting to bootrom
#if CONFIG_IDF_TARGET_ESP32S3
return true;
#else
return false;
#endif
}
static void configure_pins(usb_hal_context_t *usb) {
for (const usb_iopin_dsc_t *iopin = usb_periph_iopins; iopin->pin != -1; ++iopin) {
if ((usb->use_external_phy) || (iopin->ext_phy_only == 0)) {
esp_rom_gpio_pad_select_gpio(iopin->pin);
if (iopin->is_output) {
esp_rom_gpio_connect_out_signal(iopin->pin, iopin->func, false, false);
} else {
esp_rom_gpio_connect_in_signal(iopin->pin, iopin->func, false);
if ((iopin->pin != GPIO_FUNC_IN_LOW) && (iopin->pin != GPIO_FUNC_IN_HIGH)) {
gpio_ll_input_enable(&GPIO, iopin->pin);
}
}
esp_rom_gpio_pad_unhold(iopin->pin);
}
}
if (!usb->use_external_phy) {
gpio_set_drive_capability(USBPHY_DM_NUM, GPIO_DRIVE_CAP_3);
gpio_set_drive_capability(USBPHY_DP_NUM, GPIO_DRIVE_CAP_3);
if (perimanSetBusDeinit(ESP32_BUS_TYPE_USB_DM, usb_otg_deinit) && perimanSetBusDeinit(ESP32_BUS_TYPE_USB_DP, usb_otg_deinit)) {
// Bus Pointer is not used anyway - once the USB GPIOs are assigned, they can't be detached
perimanSetPinBus(USBPHY_DM_NUM, ESP32_BUS_TYPE_USB_DM, (void *)usb, -1, -1);
perimanSetPinBus(USBPHY_DP_NUM, ESP32_BUS_TYPE_USB_DP, (void *)usb, -1, -1);
} else {
log_e("USB OTG Pins can't be set into Peripheral Manager.");
}
}
}
esp_err_t init_usb_hal(bool external_phy) {
usb_hal_context_t hal = {.use_external_phy = external_phy};
usb_hal_init(&hal);
configure_pins(&hal);
return ESP_OK;
}
esp_err_t deinit_usb_hal() {
return ESP_OK;
}
#elif __has_include("esp_private/usb_phy.h")
#include "esp_private/usb_phy.h"
static usb_phy_handle_t phy_handle = NULL;
esp_err_t init_usb_hal(bool external_phy) {
esp_err_t ret = ESP_OK;
usb_phy_config_t phy_config = {
.controller = USB_PHY_CTRL_OTG,
.target = USB_PHY_TARGET_INT,
.otg_mode = USB_OTG_MODE_DEVICE,
#if CONFIG_IDF_TARGET_ESP32P4
.otg_speed = USB_PHY_SPEED_HIGH,
#else
.otg_speed = USB_PHY_SPEED_FULL,
#endif
.ext_io_conf = NULL,
.otg_io_conf = NULL,
};
ret = usb_new_phy(&phy_config, &phy_handle);
if (ret != ESP_OK) {
log_e("Failed to init USB PHY");
}
return ret;
}
esp_err_t deinit_usb_hal() {
esp_err_t ret = ESP_OK;
if (phy_handle) {
ret = usb_del_phy(phy_handle);
if (ret != ESP_OK) {
log_e("Failed to deinit USB PHY");
}
}
return ret;
}
#else
#error No way to initialize USP PHY
void init_usb_hal(bool external_phy) {
return ESP_OK;
}
void deinit_usb_hal() {
return ESP_OK;
}
#endif
esp_err_t tinyusb_driver_install(const tinyusb_config_t *config) {
init_usb_hal(config->external_phy);
tusb_rhport_init_t tinit;
memset(&tinit, 0, sizeof(tusb_rhport_init_t));
tinit.role = TUSB_ROLE_DEVICE;
#if CONFIG_IDF_TARGET_ESP32P4
tinit.speed = TUSB_SPEED_HIGH;
if (!tusb_init(1, &tinit)) {
#else
tinit.speed = TUSB_SPEED_FULL;
if (!tusb_init(0, &tinit)) {
#endif
log_e("Can't initialize the TinyUSB stack.");
return ESP_FAIL;
}
return ESP_OK;
}
typedef char tusb_str_t[127];
static bool WEBUSB_ENABLED = false;
static tusb_str_t WEBUSB_URL = "";
static tusb_str_t USB_DEVICE_PRODUCT = "";
static tusb_str_t USB_DEVICE_MANUFACTURER = "";
static tusb_str_t USB_DEVICE_SERIAL = "";
static tusb_str_t USB_DEVICE_LANGUAGE = "\x09\x04"; //English (0x0409)
static uint8_t USB_DEVICE_ATTRIBUTES = 0;
static uint16_t USB_DEVICE_POWER = 0;
/*
* Device Descriptor
* */
static tusb_desc_device_t tinyusb_device_descriptor = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0,
.bDeviceClass = 0,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
.bMaxPacketSize0 = CFG_TUD_ENDOINT0_SIZE,
.idVendor = 0,
.idProduct = 0,
.bcdDevice = 0,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01
};
/*
* String Descriptors
* */
#define MAX_STRING_DESCRIPTORS 20
static uint32_t tinyusb_string_descriptor_len = 4;
static char *tinyusb_string_descriptor[MAX_STRING_DESCRIPTORS] = {
// array of pointer to string descriptors
USB_DEVICE_LANGUAGE, // 0: is supported language
USB_DEVICE_MANUFACTURER, // 1: Manufacturer
USB_DEVICE_PRODUCT, // 2: Product
USB_DEVICE_SERIAL, // 3: Serials, should use chip ID
};
/* Microsoft OS 2.0 registry property descriptor
Per MS requirements https://msdn.microsoft.com/en-us/library/windows/hardware/hh450799(v=vs.85).aspx
device should create DeviceInterfaceGUIDs. It can be done by driver and
in case of real PnP solution device should expose MS "Microsoft OS 2.0
registry property descriptor". Such descriptor can insert any record
into Windows registry per device/configuration/interface. In our case it
will insert "DeviceInterfaceGUIDs" multistring property.
GUID is freshly generated and should be OK to use.
https://developers.google.com/web/fundamentals/native-hardware/build-for-webusb/
(Section Microsoft OS compatibility descriptors)
*/
#define MS_OS_20_DESC_LEN 0xB2
static uint8_t const tinyusb_ms_os_20_descriptor[] = {
// Set header: length, type, windows version, total length
U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(MS_OS_20_SET_HEADER_DESCRIPTOR), U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(MS_OS_20_DESC_LEN),
// Configuration subset header: length, type, configuration index, reserved, configuration total length
U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_CONFIGURATION), 0, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A),
// Function Subset header: length, type, first interface, reserved, subset length
U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_FUNCTION), 0, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A - 0x08),
// MS OS 2.0 Compatible ID descriptor: length, type, compatible ID, sub compatible ID
U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, // sub-compatible
// MS OS 2.0 Registry property descriptor: length, type
U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A - 0x08 - 0x08 - 0x14), U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY), U16_TO_U8S_LE(0x0007),
U16_TO_U8S_LE(0x002A), // wPropertyDataType, wPropertyNameLength and PropertyName "DeviceInterfaceGUIDs\0" in UTF-16
'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00, 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e',
0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, 0x00, 0x00,
U16_TO_U8S_LE(0x0050), // wPropertyDataLength
//bPropertyData: “{975F44D9-0D08-43FD-8B3E-127CA8AFFF9D}”.
'{', 0x00, '9', 0x00, '7', 0x00, '5', 0x00, 'F', 0x00, '4', 0x00, '4', 0x00, 'D', 0x00, '9', 0x00, '-', 0x00, '0', 0x00, 'D', 0x00, '0', 0x00, '8', 0x00, '-',
0x00, '4', 0x00, '3', 0x00, 'F', 0x00, 'D', 0x00, '-', 0x00, '8', 0x00, 'B', 0x00, '3', 0x00, 'E', 0x00, '-', 0x00, '1', 0x00, '2', 0x00, '7', 0x00, 'C',
0x00, 'A', 0x00, '8', 0x00, 'A', 0x00, 'F', 0x00, 'F', 0x00, 'F', 0x00, '9', 0x00, 'D', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00
};
TU_VERIFY_STATIC(sizeof(tinyusb_ms_os_20_descriptor) == MS_OS_20_DESC_LEN, "Incorrect size");
/*
* BOS Descriptor (required for webUSB)
* */
#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_WEBUSB_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN)
enum {
VENDOR_REQUEST_WEBUSB = 1,
VENDOR_REQUEST_MICROSOFT = 2
};
static uint8_t const tinyusb_bos_descriptor[] = {// total length, number of device caps
TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 2),
// Vendor Code, iLandingPage
TUD_BOS_WEBUSB_DESCRIPTOR(VENDOR_REQUEST_WEBUSB, 1),
// Microsoft OS 2.0 descriptor
TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, VENDOR_REQUEST_MICROSOFT)
};
/*
* URL Descriptor (required for webUSB)
* */
typedef struct TU_ATTR_PACKED {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bScheme;
char url[127];
} tinyusb_desc_webusb_url_t;
static tinyusb_desc_webusb_url_t tinyusb_url_descriptor = {
.bLength = 3,
.bDescriptorType = 3, // WEBUSB URL type
.bScheme = 255, // URL Scheme Prefix: 0: "http://", 1: "https://", 255: ""
.url = ""
};
/*
* Configuration Descriptor
* */
static tinyusb_descriptor_cb_t tinyusb_loaded_interfaces_callbacks[USB_INTERFACE_MAX];
static uint32_t tinyusb_loaded_interfaces_mask = 0;
static uint8_t tinyusb_loaded_interfaces_num = 0;
static uint16_t tinyusb_config_descriptor_len = 0;
static uint8_t *tinyusb_config_descriptor = NULL;
/*
* Endpoint Usage Tracking
* */
typedef union {
struct {
uint32_t in : 16;
uint32_t out : 16;
};
uint32_t val;
} tinyusb_endpoints_usage_t;
static tinyusb_endpoints_usage_t tinyusb_endpoints;
/*
* TinyUSB Callbacks
* */
/**
* @brief Invoked when received GET CONFIGURATION DESCRIPTOR.
*/
__attribute__((weak)) uint8_t const *tud_descriptor_configuration_cb(uint8_t index) {
//log_d("%u", index);
return tinyusb_config_descriptor;
}
/**
* @brief Invoked when received GET DEVICE DESCRIPTOR.
*/
__attribute__((weak)) uint8_t const *tud_descriptor_device_cb(void) {
//log_d("");
return (uint8_t const *)&tinyusb_device_descriptor;
}
/**
* @brief Invoked when received GET STRING DESCRIPTOR request.
*/
__attribute__((weak)) uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
//log_d("%u (0x%x)", index, langid);
static uint16_t _desc_str[127];
uint8_t chr_count;
if (index == 0) {
memcpy(&_desc_str[1], tinyusb_string_descriptor[0], 2);
chr_count = 1;
} else {
// Convert ASCII string into UTF-16
if (index >= tinyusb_string_descriptor_len) {
return NULL;
}
const char *str = tinyusb_string_descriptor[index];
// Cap at max char
chr_count = strlen(str);
if (chr_count > 126) {
chr_count = 126;
}
for (uint8_t i = 0; i < chr_count; i++) {
_desc_str[1 + i] = str[i];
}
}
// first byte is len, second byte is string type
_desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * chr_count + 2);
return _desc_str;
}
/**
* @brief Invoked when received GET BOS DESCRIPTOR request.
*/
uint8_t const *tud_descriptor_bos_cb(void) {
//log_v("");
return tinyusb_bos_descriptor;
}
__attribute__((weak)) bool tinyusb_vendor_control_request_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) {
return false;
}
/**
* @brief Handle WebUSB and Vendor requests.
*/
bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) {
if (WEBUSB_ENABLED && (request->bRequest == VENDOR_REQUEST_WEBUSB || (request->bRequest == VENDOR_REQUEST_MICROSOFT && request->wIndex == 7))) {
// we only care for SETUP stage
if (stage == CONTROL_STAGE_SETUP) {
if (request->bRequest == VENDOR_REQUEST_WEBUSB) {
// match vendor request in BOS descriptor
// Get landing page url
tinyusb_url_descriptor.bLength = 3 + strlen(WEBUSB_URL);
snprintf(tinyusb_url_descriptor.url, 127, "%s", WEBUSB_URL);
return tud_control_xfer(rhport, request, (void *)&tinyusb_url_descriptor, tinyusb_url_descriptor.bLength);
}
// Get Microsoft OS 2.0 compatible descriptor
uint16_t total_len;
memcpy(&total_len, tinyusb_ms_os_20_descriptor + 8, 2);
return tud_control_xfer(rhport, request, (void *)tinyusb_ms_os_20_descriptor, total_len);
}
return true;
}
log_v("rhport: %u, stage: %u, type: 0x%x, request: 0x%x", rhport, stage, request->bmRequestType_bit.type, request->bRequest);
return tinyusb_vendor_control_request_cb(rhport, stage, request);
}
/*
* Required Callbacks
* */
#if CFG_TUD_DFU
__attribute__((weak)) uint32_t tud_dfu_get_timeout_cb(uint8_t alt, uint8_t state) {
return 0;
}
__attribute__((weak)) void tud_dfu_download_cb(uint8_t alt, uint16_t block_num, uint8_t const *data, uint16_t length) {}
__attribute__((weak)) void tud_dfu_manifest_cb(uint8_t alt) {}
#endif
#if CFG_TUD_HID
__attribute__((weak)) const uint8_t *tud_hid_descriptor_report_cb(uint8_t itf) {
return NULL;
}
__attribute__((weak)) uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) {
return 0;
}
__attribute__((weak)) void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, const uint8_t *buffer, uint16_t bufsize) {}
#endif
#if CFG_TUD_MSC
__attribute__((weak)) bool tud_msc_test_unit_ready_cb(uint8_t lun) {
return false;
}
__attribute__((weak)) 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)) void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size) {}
__attribute__((weak)) int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize) {
return -1;
}
__attribute__((weak)) int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) {
return -1;
}
__attribute__((weak)) int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize) {
return -1;
}
#endif
#if CFG_TUD_NCM
__attribute__((weak)) bool tud_network_recv_cb(const uint8_t *src, uint16_t size) {
return false;
}
__attribute__((weak)) uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) {
return 0;
}
__attribute__((weak)) void tud_network_init_cb(void) {}
#endif
#if CFG_TUH_HID
__attribute__((weak)) void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t idx, uint8_t const *report_desc, uint16_t desc_len) {}
__attribute__((weak)) void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t idx) {}
__attribute__((weak)) void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t idx, uint8_t const *report, uint16_t len) {}
__attribute__((weak)) void tuh_hid_report_sent_cb(uint8_t dev_addr, uint8_t idx, uint8_t const *report, uint16_t len) {}
__attribute__((weak)) void tuh_hid_get_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len) {}
__attribute__((weak)) void tuh_hid_set_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len) {}
__attribute__((weak)) void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t protocol) {}
#endif
#if CFG_TUH_CDC
__attribute__((weak)) void tuh_cdc_mount_cb(uint8_t idx) {}
__attribute__((weak)) void tuh_cdc_umount_cb(uint8_t idx) {}
__attribute__((weak)) void tuh_cdc_rx_cb(uint8_t idx) {}
__attribute__((weak)) void tuh_cdc_tx_complete_cb(uint8_t idx) {}
#endif
#if CFG_TUH_MSC
__attribute__((weak)) void tuh_msc_mount_cb(uint8_t dev_addr) {}
__attribute__((weak)) void tuh_msc_umount_cb(uint8_t dev_addr) {}
#endif
/*
* Private API
* */
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
static bool usb_persist_enabled = false;
static restart_type_t usb_persist_mode = RESTART_NO_PERSIST;
#endif
#if CONFIG_IDF_TARGET_ESP32S3
static void hw_cdc_reset_handler(void *arg) {
portBASE_TYPE xTaskWoken = 0;
uint32_t usbjtag_intr_status = usb_serial_jtag_ll_get_intsts_mask();
usb_serial_jtag_ll_clr_intsts_mask(usbjtag_intr_status);
if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_BUS_RESET) {
xSemaphoreGiveFromISR((SemaphoreHandle_t)arg, &xTaskWoken);
}
if (xTaskWoken == pdTRUE) {
portYIELD_FROM_ISR();
}
}
static void usb_switch_to_cdc_jtag() {
// Disable USB-OTG
deinit_usb_hal();
periph_ll_reset(PERIPH_USB_MODULE);
//periph_ll_enable_clk_clear_rst(PERIPH_USB_MODULE);
periph_ll_disable_clk_set_rst(PERIPH_USB_MODULE);
// Switch to hardware CDC+JTAG
CLEAR_PERI_REG_MASK(RTC_CNTL_USB_CONF_REG, (RTC_CNTL_SW_HW_USB_PHY_SEL | RTC_CNTL_SW_USB_PHY_SEL | RTC_CNTL_USB_PAD_ENABLE));
// Do not use external PHY
CLEAR_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_PHY_SEL);
// Release GPIO pins from CDC+JTAG
CLEAR_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_USB_PAD_ENABLE);
// Force the host to re-enumerate (BUS_RESET)
pinMode(USBPHY_DM_NUM, OUTPUT_OPEN_DRAIN);
pinMode(USBPHY_DP_NUM, OUTPUT_OPEN_DRAIN);
digitalWrite(USBPHY_DM_NUM, LOW);
digitalWrite(USBPHY_DP_NUM, LOW);
// Initialize CDC+JTAG ISR to listen for BUS_RESET
#if defined __has_include && __has_include("hal/usb_phy_ll.h")
usb_phy_ll_int_jtag_enable(&USB_SERIAL_JTAG);
#elif defined __has_include && __has_include("hal/usb_fsls_phy_ll.h")
usb_fsls_phy_ll_int_jtag_enable(&USB_SERIAL_JTAG);
#else
// usb_serial_jtag_ll_phy_set_defaults();
const usb_serial_jtag_pull_override_vals_t pull_conf = {.dp_pu = 1, .dm_pu = 0, .dp_pd = 0, .dm_pd = 0};
usb_serial_jtag_ll_phy_enable_pull_override(&pull_conf);
usb_serial_jtag_ll_phy_disable_pull_override();
#endif
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_LL_INTR_MASK);
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_LL_INTR_MASK);
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_BUS_RESET);
intr_handle_t intr_handle = NULL;
SemaphoreHandle_t reset_sem = xSemaphoreCreateBinary();
if (reset_sem) {
if (esp_intr_alloc(ETS_USB_SERIAL_JTAG_INTR_SOURCE, 0, hw_cdc_reset_handler, reset_sem, &intr_handle) != ESP_OK) {
vSemaphoreDelete(reset_sem);
reset_sem = NULL;
log_e("HW USB CDC failed to init interrupts");
}
} else {
log_e("reset_sem init failed");
}
// Connect GPIOs to integrated CDC+JTAG
SET_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_USB_PAD_ENABLE);
// Wait for BUS_RESET to give us back the semaphore
if (reset_sem) {
if (xSemaphoreTake(reset_sem, 1000 / portTICK_PERIOD_MS) != pdPASS) {
log_e("reset_sem timeout");
}
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_LL_INTR_MASK);
esp_intr_free(intr_handle);
vSemaphoreDelete(reset_sem);
}
}
#endif
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
static void IRAM_ATTR usb_persist_shutdown_handler(void) {
if (usb_persist_mode != RESTART_NO_PERSIST) {
if (usb_persist_enabled) {
usb_dc_prepare_persist();
}
if (usb_persist_mode == RESTART_BOOTLOADER) {
//USB CDC Download
if (usb_persist_enabled) {
chip_usb_set_persist_flags(USBDC_PERSIST_ENA);
#if CONFIG_IDF_TARGET_ESP32S2
} else {
periph_ll_reset(PERIPH_USB_MODULE);
periph_ll_enable_clk_clear_rst(PERIPH_USB_MODULE);
#endif
}
REG_WRITE(RTC_CNTL_OPTION1_REG, RTC_CNTL_FORCE_DOWNLOAD_BOOT);
} else if (usb_persist_mode == RESTART_BOOTLOADER_DFU) {
//DFU Download
#if CONFIG_IDF_TARGET_ESP32S2
// Reset USB Core
USB0.grstctl |= USB_CSFTRST;
while ((USB0.grstctl & USB_CSFTRST) == USB_CSFTRST) {}
#endif
chip_usb_set_persist_flags(USBDC_BOOT_DFU);
REG_WRITE(RTC_CNTL_OPTION1_REG, RTC_CNTL_FORCE_DOWNLOAD_BOOT);
} else if (usb_persist_enabled) {
//USB Persist reboot
chip_usb_set_persist_flags(USBDC_PERSIST_ENA);
}
}
}
#endif
void usb_persist_restart(restart_type_t mode) {
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
if (mode < RESTART_TYPE_MAX && esp_register_shutdown_handler(usb_persist_shutdown_handler) == ESP_OK) {
usb_persist_mode = mode;
#if CONFIG_IDF_TARGET_ESP32S3
if (mode == RESTART_BOOTLOADER) {
usb_switch_to_cdc_jtag();
}
#endif
esp_restart();
}
#endif
}
static bool tinyusb_reserve_in_endpoint(uint8_t endpoint) {
if (endpoint > CFG_TUD_NUM_EPS || (tinyusb_endpoints.in & BIT(endpoint)) != 0) {
return false;
}
tinyusb_endpoints.in |= BIT(endpoint);
return true;
}
static bool tinyusb_reserve_out_endpoint(uint8_t endpoint) {
if (endpoint > CFG_TUD_NUM_EPS || (tinyusb_endpoints.out & BIT(endpoint)) != 0) {
return false;
}
tinyusb_endpoints.out |= BIT(endpoint);
return true;
}
static bool tinyusb_has_available_fifos(void) {
uint8_t max_endpoints = CFG_TUD_NUM_IN_EPS - 1, active_endpoints = 0;
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
if (tinyusb_loaded_interfaces_mask & BIT(USB_INTERFACE_CDC)) {
max_endpoints = CFG_TUD_NUM_IN_EPS; //CDC endpoint 0x85 is actually not linked to FIFO and not used
}
#endif
for (uint8_t i = 1; i <= CFG_TUD_NUM_EPS; i++) {
if ((tinyusb_endpoints.in & BIT(i)) != 0) {
active_endpoints++;
}
}
return active_endpoints < max_endpoints;
}
static uint16_t tinyusb_load_descriptor(tinyusb_interface_t interface, uint8_t *dst, uint8_t *itf) {
if (tinyusb_loaded_interfaces_callbacks[interface]) {
return tinyusb_loaded_interfaces_callbacks[interface](dst, itf);
}
return 0;
}
static bool tinyusb_load_enabled_interfaces() {
tinyusb_config_descriptor_len += TUD_CONFIG_DESC_LEN;
tinyusb_config_descriptor = (uint8_t *)malloc(tinyusb_config_descriptor_len);
if (tinyusb_config_descriptor == NULL) {
log_e("Descriptor Malloc Failed");
return false;
}
uint8_t *dst = tinyusb_config_descriptor + TUD_CONFIG_DESC_LEN;
for (int i = 0; i < USB_INTERFACE_MAX; i++) {
if (tinyusb_loaded_interfaces_mask & (1U << i)) {
uint16_t len = tinyusb_load_descriptor((tinyusb_interface_t)i, dst, &tinyusb_loaded_interfaces_num);
if (!len) {
log_e("Descriptor Load Failed");
return false;
} else {
dst += len;
}
}
}
uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB Device");
uint8_t descriptor[TUD_CONFIG_DESC_LEN] = {
//num configs, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, tinyusb_loaded_interfaces_num, str_index, tinyusb_config_descriptor_len, USB_DEVICE_ATTRIBUTES, USB_DEVICE_POWER)
};
memcpy(tinyusb_config_descriptor, descriptor, TUD_CONFIG_DESC_LEN);
if ((tinyusb_loaded_interfaces_mask == (BIT(USB_INTERFACE_CDC) | BIT(USB_INTERFACE_DFU))) || (tinyusb_loaded_interfaces_mask == BIT(USB_INTERFACE_CDC))) {
//usb_persist_enabled = true;
//log_d("USB Persist enabled");
}
log_d("Load Done: if_num: %u, descr_len: %u, if_mask: 0x%x", tinyusb_loaded_interfaces_num, tinyusb_config_descriptor_len, tinyusb_loaded_interfaces_mask);
return true;
}
static inline char nibble_to_hex_char(uint8_t b) {
if (b < 0xa) {
return '0' + b;
} else {
return 'a' + b - 0xa;
}
}
static void set_usb_serial_num(void) {
/* Get the MAC address */
#if CONFIG_IDF_TARGET_ESP32P4
const uint32_t mac0 = REG_GET_FIELD(EFUSE_RD_MAC_SYS_0_REG, EFUSE_MAC_0);
const uint32_t mac1 = REG_GET_FIELD(EFUSE_RD_MAC_SYS_0_REG, EFUSE_MAC_1);
#else
const uint32_t mac0 = REG_GET_FIELD(EFUSE_RD_MAC_SPI_SYS_0_REG, EFUSE_MAC_0);
const uint32_t mac1 = REG_GET_FIELD(EFUSE_RD_MAC_SPI_SYS_1_REG, EFUSE_MAC_1);
#endif
uint8_t mac_bytes[6];
memcpy(mac_bytes, &mac0, 4);
memcpy(mac_bytes + 4, &mac1, 2);
/* Convert to UTF16 string */
uint8_t *srl = (uint8_t *)USB_DEVICE_SERIAL;
for (int i = 0; i < 6; ++i) {
uint8_t b = mac_bytes[5 - i]; /* printing from the MSB */
if (i) {
*srl++ = ':';
}
*srl++ = nibble_to_hex_char(b >> 4);
*srl++ = nibble_to_hex_char(b & 0xf);
}
*srl++ = '\0';
}
static void tinyusb_apply_device_config(tinyusb_device_config_t *config) {
if (config->product_name) {
snprintf(USB_DEVICE_PRODUCT, 126, "%s", config->product_name);
}
if (config->manufacturer_name) {
snprintf(USB_DEVICE_MANUFACTURER, 126, "%s", config->manufacturer_name);
}
if (config->serial_number && config->serial_number[0]) {
snprintf(USB_DEVICE_SERIAL, 126, "%s", config->serial_number);
} else {
set_usb_serial_num();
}
if (config->webusb_url) {
snprintf(WEBUSB_URL, 126, "%s", config->webusb_url);
}
// Windows 10 will not recognize the CDC device if WebUSB is enabled and USB Class is not 2 (CDC)
if ((tinyusb_loaded_interfaces_mask & BIT(USB_INTERFACE_CDC)) && config->webusb_enabled && (config->usb_class != TUSB_CLASS_CDC)) {
config->usb_class = TUSB_CLASS_CDC;
config->usb_protocol = 0x00;
}
WEBUSB_ENABLED = config->webusb_enabled;
USB_DEVICE_ATTRIBUTES = config->usb_attributes;
USB_DEVICE_POWER = config->usb_power_ma;
tinyusb_device_descriptor.bcdUSB = config->usb_version;
tinyusb_device_descriptor.idVendor = config->vid;
tinyusb_device_descriptor.idProduct = config->pid;
tinyusb_device_descriptor.bcdDevice = config->fw_version;
tinyusb_device_descriptor.bDeviceClass = config->usb_class;
tinyusb_device_descriptor.bDeviceSubClass = config->usb_subclass;
tinyusb_device_descriptor.bDeviceProtocol = config->usb_protocol;
}
// USB Device Driver task
// This top level thread processes all usb events and invokes callbacks
static void usb_device_task(void *param) {
(void)param;
while (1) {
tud_task(); // RTOS forever loop
}
}
/*
* PUBLIC API
* */
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR
const char *tinyusb_interface_names[USB_INTERFACE_MAX] = {"MSC", "DFU", "HID", "VENDOR", "CDC", "CDC2", "MIDI", "CUSTOM"};
#endif
static bool tinyusb_is_initialized = false;
esp_err_t tinyusb_enable_interface(tinyusb_interface_t interface, uint16_t descriptor_len, tinyusb_descriptor_cb_t cb) {
return tinyusb_enable_interface2(interface, descriptor_len, cb, false);
}
esp_err_t tinyusb_enable_interface2(tinyusb_interface_t interface, uint16_t descriptor_len, tinyusb_descriptor_cb_t cb, bool reserve_endpoints) {
if (tinyusb_is_initialized) {
log_e("TinyUSB has already started! Interface %s not enabled", (interface >= USB_INTERFACE_MAX) ? "" : tinyusb_interface_names[interface]);
return ESP_FAIL;
}
if ((interface >= USB_INTERFACE_MAX) || (tinyusb_loaded_interfaces_mask & (1U << interface))) {
log_e("Interface %s invalid or already enabled", (interface >= USB_INTERFACE_MAX) ? "" : tinyusb_interface_names[interface]);
return ESP_FAIL;
}
if (interface == USB_INTERFACE_HID && reserve_endpoints) {
// Some simple PC BIOS requires specific endpoint addresses for keyboard at boot
if (!tinyusb_reserve_out_endpoint(1) || !tinyusb_reserve_in_endpoint(1)) {
log_e("HID Reserve Endpoints Failed");
return ESP_FAIL;
}
}
if (interface == USB_INTERFACE_CDC) {
if (!tinyusb_reserve_out_endpoint(3) || !tinyusb_reserve_in_endpoint(4) || !tinyusb_reserve_in_endpoint(5)) {
log_e("CDC Reserve Endpoints Failed");
return ESP_FAIL;
}
}
tinyusb_loaded_interfaces_mask |= (1U << interface);
tinyusb_config_descriptor_len += descriptor_len;
tinyusb_loaded_interfaces_callbacks[interface] = cb;
log_d("Interface %s enabled", tinyusb_interface_names[interface]);
return ESP_OK;
}
esp_err_t tinyusb_init(tinyusb_device_config_t *config) {
if (tinyusb_is_initialized) {
return ESP_OK;
}
tinyusb_is_initialized = true;
//tinyusb_endpoints.val = 0;
tinyusb_apply_device_config(config);
if (!tinyusb_load_enabled_interfaces()) {
tinyusb_is_initialized = false;
return ESP_FAIL;
}
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
bool usb_did_persist = (USB_WRAP.date.val == USBDC_PERSIST_ENA);
//if(usb_did_persist && usb_persist_enabled){
// Enable USB/IO_MUX peripheral reset, if coming from persistent reboot
REG_CLR_BIT(RTC_CNTL_USB_CONF_REG, RTC_CNTL_IO_MUX_RESET_DISABLE);
REG_CLR_BIT(RTC_CNTL_USB_CONF_REG, RTC_CNTL_USB_RESET_DISABLE);
//} else
if (!usb_did_persist || !usb_persist_enabled) {
// Reset USB module
periph_ll_reset(PERIPH_USB_MODULE);
periph_ll_enable_clk_clear_rst(PERIPH_USB_MODULE);
}
#endif
tinyusb_config_t tusb_cfg = {
.external_phy = false // In the most cases you need to use a `false` value
};
esp_err_t err = tinyusb_driver_install(&tusb_cfg);
if (err != ESP_OK) {
tinyusb_is_initialized = false;
return err;
}
xTaskCreate(usb_device_task, "usbd", 4096, NULL, configMAX_PRIORITIES - 1, NULL);
return err;
}
uint8_t tinyusb_add_string_descriptor(const char *str) {
if (str == NULL || tinyusb_string_descriptor_len >= MAX_STRING_DESCRIPTORS) {
return 0;
}
uint8_t index = tinyusb_string_descriptor_len;
tinyusb_string_descriptor[tinyusb_string_descriptor_len++] = (char *)str;
return index;
}
uint8_t tinyusb_get_free_duplex_endpoint(void) {
if (!tinyusb_has_available_fifos()) {
log_e("No available IN endpoints");
return 0;
}
for (uint8_t i = 1; i <= CFG_TUD_NUM_IN_EPS; i++) {
if ((tinyusb_endpoints.in & BIT(i)) == 0 && (tinyusb_endpoints.out & BIT(i)) == 0) {
tinyusb_endpoints.in |= BIT(i);
tinyusb_endpoints.out |= BIT(i);
return i;
}
}
log_e("No available duplex endpoints");
return 0;
}
uint8_t tinyusb_get_free_in_endpoint(void) {
if (!tinyusb_has_available_fifos()) {
log_e("No available IN endpoints");
return 0;
}
for (uint8_t i = 1; i <= CFG_TUD_NUM_IN_EPS; i++) {
if ((tinyusb_endpoints.in & BIT(i)) == 0 && (tinyusb_endpoints.out & BIT(i)) != 0) {
tinyusb_endpoints.in |= BIT(i);
return i;
}
}
for (uint8_t i = 1; i <= CFG_TUD_NUM_IN_EPS; i++) {
if ((tinyusb_endpoints.in & BIT(i)) == 0) {
tinyusb_endpoints.in |= BIT(i);
return i;
}
}
return 0;
}
uint8_t tinyusb_get_free_out_endpoint(void) {
for (uint8_t i = 1; i <= CFG_TUD_NUM_EPS; i++) {
if ((tinyusb_endpoints.out & BIT(i)) == 0 && (tinyusb_endpoints.in & BIT(i)) != 0) {
tinyusb_endpoints.out |= BIT(i);
return i;
}
}
for (uint8_t i = 1; i <= CFG_TUD_NUM_EPS; i++) {
if ((tinyusb_endpoints.out & BIT(i)) == 0) {
tinyusb_endpoints.out |= BIT(i);
return i;
}
}
return 0;
}
#endif /* CONFIG_TINYUSB_ENABLED */
#endif /* SOC_USB_OTG_SUPPORTED */
+118
View File
@@ -0,0 +1,118 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "soc/soc_caps.h"
#if SOC_USB_OTG_SUPPORTED
#include "esp32-hal.h"
#if CONFIG_TINYUSB_ENABLED
#ifdef __cplusplus
extern "C" {
#endif
#include "tusb.h"
#include "tusb_option.h"
#include "tusb_config.h"
#define USB_ESPRESSIF_VID 0x303A
#define USB_STRING_DESCRIPTOR_ARRAY_SIZE 10
#ifndef CFG_TUD_ENDOINT_SIZE
#if CONFIG_IDF_TARGET_ESP32P4
#define CFG_TUD_ENDOINT_SIZE 512
#else
#define CFG_TUD_ENDOINT_SIZE 64
#endif
#endif
#if CONFIG_IDF_TARGET_ESP32P4
#define CFG_TUD_NUM_EPS 15
#define CFG_TUD_NUM_IN_EPS 8
#else
#define CFG_TUD_NUM_EPS 6
#define CFG_TUD_NUM_IN_EPS 5
#endif
typedef struct {
uint16_t vid;
uint16_t pid;
const char *product_name;
const char *manufacturer_name;
const char *serial_number;
uint16_t fw_version;
uint16_t usb_version;
uint8_t usb_class;
uint8_t usb_subclass;
uint8_t usb_protocol;
uint8_t usb_attributes;
uint16_t usb_power_ma;
bool webusb_enabled;
const char *webusb_url;
} tinyusb_device_config_t;
#define TINYUSB_CONFIG_DEFAULT() \
{ \
.vid = USB_ESPRESSIF_VID, .pid = 0x0002, .product_name = CONFIG_TINYUSB_DESC_PRODUCT_STRING, .manufacturer_name = CONFIG_TINYUSB_DESC_MANUFACTURER_STRING, \
.serial_number = CONFIG_TINYUSB_DESC_SERIAL_STRING, .fw_version = CONFIG_TINYUSB_DESC_BCDDEVICE, .usb_version = 0x0200, .usb_class = TUSB_CLASS_MISC, \
.usb_subclass = MISC_SUBCLASS_COMMON, .usb_protocol = MISC_PROTOCOL_IAD, .usb_attributes = TUSB_DESC_CONFIG_ATT_SELF_POWERED, .usb_power_ma = 500, \
.webusb_enabled = false, .webusb_url = "espressif.github.io/arduino-esp32/webusb.html" \
}
esp_err_t tinyusb_init(tinyusb_device_config_t *config);
/*
* USB Persistence API
* */
typedef enum {
RESTART_NO_PERSIST,
RESTART_PERSIST,
RESTART_BOOTLOADER,
RESTART_BOOTLOADER_DFU,
RESTART_TYPE_MAX
} restart_type_t;
void usb_persist_restart(restart_type_t mode);
// The following definitions and functions are to be used only by the drivers
typedef enum {
USB_INTERFACE_MSC,
USB_INTERFACE_DFU,
USB_INTERFACE_HID,
USB_INTERFACE_VENDOR,
USB_INTERFACE_CDC,
USB_INTERFACE_CDC2,
USB_INTERFACE_MIDI,
USB_INTERFACE_CUSTOM,
USB_INTERFACE_MAX
} tinyusb_interface_t;
typedef uint16_t (*tinyusb_descriptor_cb_t)(uint8_t *dst, uint8_t *itf);
esp_err_t tinyusb_enable_interface(tinyusb_interface_t interface, uint16_t descriptor_len, tinyusb_descriptor_cb_t cb);
esp_err_t tinyusb_enable_interface2(tinyusb_interface_t interface, uint16_t descriptor_len, tinyusb_descriptor_cb_t cb, bool reserve_endpoints);
uint8_t tinyusb_add_string_descriptor(const char *str);
uint8_t tinyusb_get_free_duplex_endpoint(void);
uint8_t tinyusb_get_free_in_endpoint(void);
uint8_t tinyusb_get_free_out_endpoint(void);
#ifdef __cplusplus
}
#endif
#endif /* CONFIG_TINYUSB_ENABLED */
#endif /* SOC_USB_OTG_SUPPORTED */
+560
View File
@@ -0,0 +1,560 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "soc/soc_caps.h"
#include "esp_idf_version.h"
#if SOC_TOUCH_SENSOR_SUPPORTED
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) || SOC_TOUCH_SENSOR_VERSION == 3
#include "esp32-hal-touch-ng.h"
#include "esp32-hal-periman.h"
/*
Internal Private Touch Data Structure and Functions
*/
typedef void (*voidFuncPtr)(void);
typedef void (*voidArgFuncPtr)(void *);
typedef struct {
voidFuncPtr fn;
bool callWithArgs;
void *arg;
bool lastStatusIsPressed;
} TouchInterruptHandle_t;
static TouchInterruptHandle_t __touchInterruptHandlers[SOC_TOUCH_SENSOR_NUM] = {
0,
};
#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32
static uint8_t _sample_num = 1; // only one sample configuration supported
static float _duration_ms = 5.0f;
static touch_volt_lim_l_t _volt_low = TOUCH_VOLT_LIM_L_0V5;
static touch_volt_lim_h_t _volt_high = TOUCH_VOLT_LIM_H_1V7;
static touch_intr_trig_mode_t _intr_trig_mode = TOUCH_INTR_TRIG_ON_BELOW_THRESH;
#elif SOC_TOUCH_SENSOR_VERSION == 2 // ESP32S2, ESP32S3
static uint8_t _sample_num = 1; // only one sample configuration supported
static uint32_t _chg_times = 500;
static touch_volt_lim_l_t _volt_low = TOUCH_VOLT_LIM_L_0V5;
static touch_volt_lim_h_t _volt_high = TOUCH_VOLT_LIM_H_2V2;
#elif SOC_TOUCH_SENSOR_VERSION == 3 // ESP32P4
static uint8_t _sample_num = 1; // TODO: can be extended to multiple samples
static uint32_t _div_num = 1;
static uint8_t _coarse_freq_tune = 1;
static uint8_t _fine_freq_tune = 1;
#endif
static uint8_t used_pads = 0;
static uint32_t __touchSleepTime = 256;
static float __touchMeasureTime = 32.0f;
static touch_sensor_config_t sensor_config;
static bool initialized = false;
static bool enabled = false;
static bool running = false;
static bool channels_initialized[SOC_TOUCH_SENSOR_NUM] = {false};
static touch_sensor_handle_t touch_sensor_handle = NULL;
static touch_channel_handle_t touch_channel_handle[SOC_TOUCH_SENSOR_NUM] = {};
// Active threshold to benchmark ratio. (i.e., touch will be activated when data >= benchmark * (1 + ratio))
static float s_thresh2bm_ratio = 0.015f; // 1.5% for all channels
static bool ARDUINO_ISR_ATTR __touchOnActiveISR(touch_sensor_handle_t sens_handle, const touch_active_event_data_t *event, void *user_ctx) {
uint8_t pad_num = (uint8_t)event->chan_id;
__touchInterruptHandlers[pad_num].lastStatusIsPressed = true;
if (__touchInterruptHandlers[pad_num].fn) {
// keeping backward compatibility with "void cb(void)" and with new "void cb(void *)"
if (__touchInterruptHandlers[pad_num].callWithArgs) {
((voidArgFuncPtr)__touchInterruptHandlers[pad_num].fn)(__touchInterruptHandlers[pad_num].arg);
} else {
__touchInterruptHandlers[pad_num].fn();
}
}
return false;
}
static bool ARDUINO_ISR_ATTR __touchOnInactiveISR(touch_sensor_handle_t sens_handle, const touch_inactive_event_data_t *event, void *user_ctx) {
uint8_t pad_num = (uint8_t)event->chan_id;
__touchInterruptHandlers[pad_num].lastStatusIsPressed = false;
if (__touchInterruptHandlers[pad_num].fn) {
// keeping backward compatibility with "void cb(void)" and with new "void cb(void *)"
if (__touchInterruptHandlers[pad_num].callWithArgs) {
((voidArgFuncPtr)__touchInterruptHandlers[pad_num].fn)(__touchInterruptHandlers[pad_num].arg);
} else {
__touchInterruptHandlers[pad_num].fn();
}
}
return false;
}
bool touchStop() {
if (!running) { // Already stopped
return true;
}
if (touch_sensor_stop_continuous_scanning(touch_sensor_handle) != ESP_OK) {
log_e("Touch sensor stop scanning failed!");
return false;
}
running = false;
return true;
}
bool touchDisable() {
if (!enabled) { // Already disabled
return true;
}
if (running) {
log_e("Touch sensor still running!");
return false;
}
if (touch_sensor_disable(touch_sensor_handle) != ESP_OK) {
log_e("Touch sensor disable failed!");
return false;
}
enabled = false;
return true;
}
bool touchStart() {
if (running) { // Already running
return true;
}
if (!enabled) {
log_e("Touch sensor not enabled!");
return false;
}
if (touch_sensor_start_continuous_scanning(touch_sensor_handle) != ESP_OK) {
log_e("Touch sensor failed to start continuous scanning!");
return false;
}
running = true;
return true;
}
bool touchEnable() {
if (enabled) { // Already enabled
return true;
}
if (touch_sensor_enable(touch_sensor_handle) != ESP_OK) {
log_e("Touch sensor enable failed!");
return false;
}
enabled = true;
return true;
}
bool touchBenchmarkThreshold(uint8_t pad) {
if (!touchEnable()) {
return false;
}
/* Scan the enabled touch channels for several times, to make sure the initial channel data is stable */
for (int i = 0; i < 3; i++) {
if (touch_sensor_trigger_oneshot_scanning(touch_sensor_handle, 2000) != ESP_OK) {
log_e("Touch sensor trigger oneshot scanning failed!");
return false;
}
}
/* Disable the touch channel to rollback the state */
if (!touchDisable()) {
return false;
}
// Reconfigure passed pad with new threshold
uint32_t benchmark[_sample_num] = {};
#if SOC_TOUCH_SUPPORT_BENCHMARK // ESP32S2, ESP32S3,ESP32P4
if (touch_channel_read_data(touch_channel_handle[pad], TOUCH_CHAN_DATA_TYPE_BENCHMARK, benchmark) != ESP_OK) {
log_e("Touch channel read data failed!");
return false;
}
#else
if (touch_channel_read_data(touch_channel_handle[pad], TOUCH_CHAN_DATA_TYPE_SMOOTH, benchmark) != ESP_OK) {
log_e("Touch channel read data failed!");
return false;
}
#endif
/* Calculate the proper active thresholds regarding the initial benchmark */
touch_channel_config_t chan_cfg = TOUCH_CHANNEL_DEFAULT_CONFIG();
for (int i = 0; i < _sample_num; i++) {
#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32
chan_cfg.abs_active_thresh[i] = (uint32_t)(benchmark[i] * (1 - s_thresh2bm_ratio));
log_v("Configured [CH %d] sample %d: benchmark = %" PRIu32 ", threshold = %" PRIu32 "\t", pad, i, benchmark[i], chan_cfg.abs_active_thresh[i]);
#else
chan_cfg.active_thresh[i] = (uint32_t)(benchmark[i] * s_thresh2bm_ratio);
log_v("Configured [CH %d] sample %d: benchmark = %" PRIu32 ", threshold = %" PRIu32 "\t", pad, i, benchmark[i], chan_cfg.active_thresh[i]);
#endif
}
/* Update the channel configuration */
if (touch_sensor_reconfig_channel(touch_channel_handle[pad], &chan_cfg) != ESP_OK) {
log_e("Touch sensor threshold reconfig channel failed!");
return false;
}
return true;
}
static bool touchDetachBus(void *pin) {
int8_t pad = digitalPinToTouchChannel((int)(pin - 1));
channels_initialized[pad] = false;
//disable touch pad and delete the channel
if (!touchStop()) {
log_e("touchStop() failed!");
return false;
}
if (!touchDisable()) {
log_e("touchDisable() failed!");
return false;
}
touch_sensor_del_channel(touch_channel_handle[pad]);
used_pads--;
if (used_pads == 0) {
if (touch_sensor_del_controller(touch_sensor_handle) != ESP_OK) //deinit touch module, as no pads are used
{
log_e("Touch module deinit failed!");
return false;
}
initialized = false;
} else {
touchEnable();
touchStart();
}
return true;
}
static void __touchInit() {
if (initialized) {
return;
}
// Support only one sample configuration for now
#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32
touch_sensor_sample_config_t single_sample_cfg = TOUCH_SENSOR_V1_DEFAULT_SAMPLE_CONFIG(_duration_ms, _volt_low, _volt_high);
#elif SOC_TOUCH_SENSOR_VERSION == 2 // ESP32S2, ESP32S3
touch_sensor_sample_config_t single_sample_cfg = TOUCH_SENSOR_V2_DEFAULT_SAMPLE_CONFIG(_chg_times, _volt_low, _volt_high);
#elif SOC_TOUCH_SENSOR_VERSION == 3 // ESP32P4
touch_sensor_sample_config_t single_sample_cfg = TOUCH_SENSOR_V3_DEFAULT_SAMPLE_CONFIG(_div_num, _coarse_freq_tune, _fine_freq_tune);
#endif
touch_sensor_sample_config_t sample_cfg[_sample_num] = {};
sample_cfg[0] = single_sample_cfg;
touch_sensor_config_t sens_cfg = {
#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32
.power_on_wait_us = __touchSleepTime,
.meas_interval_us = __touchMeasureTime,
.intr_trig_mode = _intr_trig_mode,
.intr_trig_group = TOUCH_INTR_TRIG_GROUP_BOTH,
.sample_cfg_num = _sample_num,
.sample_cfg = sample_cfg,
#elif SOC_TOUCH_SENSOR_VERSION == 2 // ESP32S2, ESP32S3
.power_on_wait_us = __touchSleepTime,
.meas_interval_us = __touchMeasureTime,
.max_meas_time_us = 0,
.sample_cfg_num = _sample_num,
.sample_cfg = sample_cfg,
#elif SOC_TOUCH_SENSOR_VERSION == 3 // ESP32P4
.power_on_wait_us = __touchSleepTime,
.meas_interval_us = __touchMeasureTime,
.max_meas_time_us = 0,
.output_mode = TOUCH_PAD_OUT_AS_CLOCK,
.sample_cfg_num = _sample_num,
.sample_cfg = sample_cfg,
#endif
};
if (touch_sensor_new_controller(&sens_cfg, &touch_sensor_handle) != ESP_OK) {
goto err;
}
sensor_config = sens_cfg;
/* Configure the touch sensor filter */
touch_sensor_filter_config_t filter_cfg = TOUCH_SENSOR_DEFAULT_FILTER_CONFIG();
if (touch_sensor_config_filter(touch_sensor_handle, &filter_cfg) != ESP_OK) {
goto err;
}
/* Register the touch sensor on_active and on_inactive callbacks */
touch_event_callbacks_t callbacks = {0};
callbacks.on_active = __touchOnActiveISR;
callbacks.on_inactive = __touchOnInactiveISR;
if (touch_sensor_register_callbacks(touch_sensor_handle, &callbacks, NULL) != ESP_OK) {
goto err;
}
initialized = true;
return;
err:
log_e(" Touch sensor initialization error.");
initialized = false;
return;
}
static void __touchChannelInit(int pad) {
if (channels_initialized[pad]) {
return;
}
// Initial setup with default Threshold
__touchInterruptHandlers[pad].fn = NULL;
touch_channel_config_t chan_cfg = TOUCH_CHANNEL_DEFAULT_CONFIG();
if (!touchStop() || !touchDisable()) {
log_e("Touch sensor stop and disable failed!");
return;
}
if (touch_sensor_new_channel(touch_sensor_handle, pad, &chan_cfg, &touch_channel_handle[pad]) != ESP_OK) {
log_e("Touch sensor new channel failed!");
return;
}
// Benchmark active threshold and reconfigure pad
if (!touchBenchmarkThreshold(pad)) {
log_e("Touch sensor benchmark threshold failed!");
return;
}
channels_initialized[pad] = true;
used_pads++;
if (!touchEnable() || !touchStart()) {
log_e("Touch sensor enable and start failed!");
}
}
static touch_value_t __touchRead(uint8_t pin) {
int8_t pad = digitalPinToTouchChannel(pin);
if (pad < 0) {
return 0;
}
if (perimanGetPinBus(pin, ESP32_BUS_TYPE_TOUCH) == NULL) {
perimanSetBusDeinit(ESP32_BUS_TYPE_TOUCH, touchDetachBus);
if (!perimanClearPinBus(pin)) {
return 0;
}
__touchInit();
__touchChannelInit(pad);
if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_TOUCH, (void *)(pin + 1), -1, pad)) {
touchDetachBus((void *)(pin + 1));
return 0;
}
}
uint32_t touch_read[_sample_num] = {};
touch_channel_read_data(touch_channel_handle[pad], TOUCH_CHAN_DATA_TYPE_SMOOTH, touch_read);
touch_value_t touch_value = touch_read[0]; // only one sample configuration for now
return touch_value;
}
static void __touchConfigInterrupt(uint8_t pin, void (*userFunc)(void), void *Args, bool callWithArgs, touch_value_t threshold) {
int8_t pad = digitalPinToTouchChannel(pin);
if (pad < 0) {
return;
}
if (userFunc == NULL) {
// detach ISR User Call
__touchInterruptHandlers[pad].fn = NULL;
__touchInterruptHandlers[pad].callWithArgs = false;
__touchInterruptHandlers[pad].arg = NULL;
} else {
// attach ISR User Call
if (perimanGetPinBus(pin, ESP32_BUS_TYPE_TOUCH) == NULL) {
perimanSetBusDeinit(ESP32_BUS_TYPE_TOUCH, touchDetachBus);
if (!perimanClearPinBus(pin)) {
log_e("Failed to clear pin bus");
return;
}
__touchInit();
__touchChannelInit(pad);
if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_TOUCH, (void *)(pin + 1), -1, pad)) {
touchDetachBus((void *)(pin + 1));
log_e("Failed to set bus to Peripheral manager");
return;
}
}
__touchInterruptHandlers[pad].fn = userFunc;
__touchInterruptHandlers[pad].callWithArgs = callWithArgs;
__touchInterruptHandlers[pad].arg = Args;
}
if (threshold != 0) {
if (!touchStop() || !touchDisable()) {
log_e("Touch sensor stop and disable failed!");
return;
}
touch_channel_config_t chan_cfg = TOUCH_CHANNEL_DEFAULT_CONFIG();
for (int i = 0; i < _sample_num; i++) {
#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32
chan_cfg.abs_active_thresh[i] = threshold;
#else
chan_cfg.active_thresh[i] = threshold;
#endif
}
if (touch_sensor_reconfig_channel(touch_channel_handle[pad], &chan_cfg) != ESP_OK) {
log_e("Touch sensor threshold reconfig channel failed!");
}
if (!touchEnable() || !touchStart()) {
log_e("Touch sensor enable and start failed!");
}
}
}
// it keeps backwards compatibility
static void __touchAttachInterrupt(uint8_t pin, void (*userFunc)(void), touch_value_t threshold) {
__touchConfigInterrupt(pin, userFunc, NULL, false, threshold);
}
// new additional version of the API with User Args
static void __touchAttachArgsInterrupt(uint8_t pin, void (*userFunc)(void), void *args, touch_value_t threshold) {
__touchConfigInterrupt(pin, userFunc, args, true, threshold);
}
// new additional API to detach touch ISR
static void __touchDettachInterrupt(uint8_t pin) {
__touchConfigInterrupt(pin, NULL, NULL, false, 0); // userFunc as NULL acts as detaching
}
// /*
// External Public Touch API Functions
// */
bool touchInterruptGetLastStatus(uint8_t pin) {
int8_t pad = digitalPinToTouchChannel(pin);
if (pad < 0) {
return false;
}
return __touchInterruptHandlers[pad].lastStatusIsPressed;
}
void touchSleepWakeUpEnable(uint8_t pin, touch_value_t threshold) {
int8_t pad = digitalPinToTouchChannel(pin);
if (pad < 0) {
return;
}
if (perimanGetPinBus(pin, ESP32_BUS_TYPE_TOUCH) == NULL) {
perimanSetBusDeinit(ESP32_BUS_TYPE_TOUCH, touchDetachBus);
__touchInit();
__touchChannelInit(pad);
if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_TOUCH, (void *)(pin + 1), -1, pad)) {
log_e("Failed to set bus to Peripheral manager");
touchDetachBus((void *)(pin + 1));
return;
}
}
log_v("Touch sensor deep sleep wake-up configuration for pad %d with threshold %d", pad, threshold);
if (!touchStop() || !touchDisable()) {
log_e("Touch sensor stop and disable failed!");
return;
}
touch_sleep_config_t deep_slp_cfg = {
.slp_wakeup_lvl = TOUCH_DEEP_SLEEP_WAKEUP,
#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32
.deep_slp_sens_cfg = NULL, // Use the original touch sensor configuration
#else // SOC_TOUCH_SENSOR_VERSION 2 and 3// ESP32S2, ESP32S3, ESP32P4
.deep_slp_chan = touch_channel_handle[pad],
.deep_slp_thresh = {threshold},
.deep_slp_sens_cfg = NULL, // Use the original touch sensor configuration
#endif
};
// Register the deep sleep wake-up
if (touch_sensor_config_sleep_wakeup(touch_sensor_handle, &deep_slp_cfg) != ESP_OK) {
log_e("Touch sensor deep sleep wake-up failed!");
return;
}
if (!touchEnable() || !touchStart()) {
log_e("Touch sensor enable and start failed!");
}
}
void touchSetDefaultThreshold(float percentage) {
s_thresh2bm_ratio = (float)percentage / 100.0f;
}
void touchSetTiming(float measure, uint32_t sleep) {
if (initialized) {
log_e("Touch sensor already initialized. Cannot set cycles.");
return;
}
__touchSleepTime = sleep;
__touchMeasureTime = measure;
}
#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32
void touchSetConfig(float duration_ms, touch_volt_lim_l_t volt_low, touch_volt_lim_h_t volt_high) {
if (initialized) {
log_e("Touch sensor already initialized. Cannot set configuration.");
return;
}
_duration_ms = duration_ms;
_volt_low = volt_low;
_volt_high = volt_high;
}
#elif SOC_TOUCH_SENSOR_VERSION == 2 // ESP32S2, ESP32S3
void touchSetConfig(uint32_t chg_times, touch_volt_lim_l_t volt_low, touch_volt_lim_h_t volt_high) {
if (initialized) {
log_e("Touch sensor already initialized. Cannot set configuration.");
return;
}
_chg_times = chg_times;
_volt_low = volt_low;
_volt_high = volt_high;
}
#elif SOC_TOUCH_SENSOR_VERSION == 3 // ESP32P4
void touchSetConfig(uint32_t div_num, uint8_t coarse_freq_tune, uint8_t fine_freq_tune) {
if (initialized) {
log_e("Touch sensor already initialized. Cannot set configuration.");
return;
}
_div_num = div_num;
_coarse_freq_tune = coarse_freq_tune;
_fine_freq_tune = fine_freq_tune;
}
#endif
#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32
void touchInterruptSetThresholdDirection(bool mustbeLower) {
if (mustbeLower) {
_intr_trig_mode = TOUCH_INTR_TRIG_ON_BELOW_THRESH;
} else {
_intr_trig_mode = TOUCH_INTR_TRIG_ON_ABOVE_THRESH;
}
}
#endif
extern touch_value_t touchRead(uint8_t) __attribute__((weak, alias("__touchRead")));
extern void touchAttachInterrupt(uint8_t, voidFuncPtr, touch_value_t) __attribute__((weak, alias("__touchAttachInterrupt")));
extern void touchAttachInterruptArg(uint8_t, voidArgFuncPtr, void *, touch_value_t) __attribute__((weak, alias("__touchAttachArgsInterrupt")));
extern void touchDetachInterrupt(uint8_t) __attribute__((weak, alias("__touchDettachInterrupt")));
#endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) || SOC_TOUCH_SENSOR_VERSION == 3 */
#endif /* SOC_TOUCH_SENSOR_SUPPORTED */

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