Prvni ulozeni z chegewara githubu
This commit is contained in:
commit
01eb80dfe2
263
CMakeLists.txt
Normal file
263
CMakeLists.txt
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
# 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 "4.4.0")
|
||||||
|
set(max_supported_idf_version "4.4.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/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-i2c.c
|
||||||
|
cores/esp32/esp32-hal-i2c-slave.c
|
||||||
|
cores/esp32/esp32-hal-ledc.c
|
||||||
|
cores/esp32/esp32-hal-matrix.c
|
||||||
|
cores/esp32/esp32-hal-misc.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-uart.c
|
||||||
|
cores/esp32/esp32-hal-rmt.c
|
||||||
|
cores/esp32/Esp.cpp
|
||||||
|
cores/esp32/FunctionalInterrupt.cpp
|
||||||
|
cores/esp32/HardwareSerial.cpp
|
||||||
|
cores/esp32/IPAddress.cpp
|
||||||
|
cores/esp32/IPv6Address.cpp
|
||||||
|
cores/esp32/libb64/cdecode.c
|
||||||
|
cores/esp32/libb64/cencode.c
|
||||||
|
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(LIBRARY_SRCS
|
||||||
|
libraries/ArduinoOTA/src/ArduinoOTA.cpp
|
||||||
|
libraries/AsyncUDP/src/AsyncUDP.cpp
|
||||||
|
libraries/BluetoothSerial/src/BluetoothSerial.cpp
|
||||||
|
libraries/BluetoothSerial/src/BTAddress.cpp
|
||||||
|
libraries/BluetoothSerial/src/BTAdvertisedDeviceSet.cpp
|
||||||
|
libraries/BluetoothSerial/src/BTScanResultsSet.cpp
|
||||||
|
libraries/DNSServer/src/DNSServer.cpp
|
||||||
|
libraries/EEPROM/src/EEPROM.cpp
|
||||||
|
libraries/ESPmDNS/src/ESPmDNS.cpp
|
||||||
|
libraries/Ethernet/src/ETH.cpp
|
||||||
|
libraries/FFat/src/FFat.cpp
|
||||||
|
libraries/FS/src/FS.cpp
|
||||||
|
libraries/FS/src/vfs_api.cpp
|
||||||
|
libraries/HTTPClient/src/HTTPClient.cpp
|
||||||
|
libraries/HTTPUpdate/src/HTTPUpdate.cpp
|
||||||
|
libraries/LittleFS/src/LittleFS.cpp
|
||||||
|
libraries/I2S/src/I2S.cpp
|
||||||
|
libraries/NetBIOS/src/NetBIOS.cpp
|
||||||
|
libraries/Preferences/src/Preferences.cpp
|
||||||
|
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/SD_MMC/src/SD_MMC.cpp
|
||||||
|
libraries/SD/src/SD.cpp
|
||||||
|
libraries/SD/src/sd_diskio.cpp
|
||||||
|
libraries/SD/src/sd_diskio_crc.c
|
||||||
|
libraries/SimpleBLE/src/SimpleBLE.cpp
|
||||||
|
libraries/SPIFFS/src/SPIFFS.cpp
|
||||||
|
libraries/SPI/src/SPI.cpp
|
||||||
|
libraries/Ticker/src/Ticker.cpp
|
||||||
|
libraries/Update/src/Updater.cpp
|
||||||
|
libraries/Update/src/HttpsOTAUpdate.cpp
|
||||||
|
libraries/USB/src/USBHID.cpp
|
||||||
|
libraries/USB/src/USBHIDMouse.cpp
|
||||||
|
libraries/USB/src/USBHIDKeyboard.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
|
||||||
|
libraries/WebServer/src/WebServer.cpp
|
||||||
|
libraries/WebServer/src/Parsing.cpp
|
||||||
|
libraries/WebServer/src/detail/mimetable.cpp
|
||||||
|
libraries/WiFiClientSecure/src/ssl_client.cpp
|
||||||
|
libraries/WiFiClientSecure/src/esp_crt_bundle.c
|
||||||
|
libraries/WiFiClientSecure/src/WiFiClientSecure.cpp
|
||||||
|
libraries/WiFi/src/WiFiAP.cpp
|
||||||
|
libraries/WiFi/src/WiFiClient.cpp
|
||||||
|
libraries/WiFi/src/WiFi.cpp
|
||||||
|
libraries/WiFi/src/WiFiGeneric.cpp
|
||||||
|
libraries/WiFi/src/WiFiMulti.cpp
|
||||||
|
libraries/WiFi/src/WiFiScan.cpp
|
||||||
|
libraries/WiFi/src/WiFiServer.cpp
|
||||||
|
libraries/WiFi/src/WiFiSTA.cpp
|
||||||
|
libraries/WiFi/src/WiFiUdp.cpp
|
||||||
|
libraries/WiFiProv/src/WiFiProv.cpp
|
||||||
|
libraries/Wire/src/Wire.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(BLE_SRCS
|
||||||
|
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(includedirs
|
||||||
|
variants/${CONFIG_ARDUINO_VARIANT}/
|
||||||
|
cores/esp32/
|
||||||
|
libraries/ArduinoOTA/src
|
||||||
|
libraries/AsyncUDP/src
|
||||||
|
libraries/BLE/src
|
||||||
|
libraries/BluetoothSerial/src
|
||||||
|
libraries/DNSServer/src
|
||||||
|
libraries/EEPROM/src
|
||||||
|
libraries/ESP32/src
|
||||||
|
libraries/ESPmDNS/src
|
||||||
|
libraries/Ethernet/src
|
||||||
|
libraries/FFat/src
|
||||||
|
libraries/FS/src
|
||||||
|
libraries/HTTPClient/src
|
||||||
|
libraries/HTTPUpdate/src
|
||||||
|
libraries/LittleFS/src
|
||||||
|
libraries/I2S/src
|
||||||
|
libraries/NetBIOS/src
|
||||||
|
libraries/Preferences/src
|
||||||
|
libraries/RainMaker/src
|
||||||
|
libraries/SD_MMC/src
|
||||||
|
libraries/SD/src
|
||||||
|
libraries/SimpleBLE/src
|
||||||
|
libraries/SPIFFS/src
|
||||||
|
libraries/SPI/src
|
||||||
|
libraries/Ticker/src
|
||||||
|
libraries/Update/src
|
||||||
|
libraries/USB/src
|
||||||
|
libraries/WebServer/src
|
||||||
|
libraries/WiFiClientSecure/src
|
||||||
|
libraries/WiFi/src
|
||||||
|
libraries/WiFiProv/src
|
||||||
|
libraries/Wire/src
|
||||||
|
)
|
||||||
|
|
||||||
|
set(srcs ${CORE_SRCS} ${LIBRARY_SRCS} ${BLE_SRCS})
|
||||||
|
set(priv_includes cores/esp32/libb64)
|
||||||
|
set(requires spi_flash mbedtls mdns esp_adc_cal wifi_provisioning nghttp wpa_supplicant)
|
||||||
|
set(priv_requires fatfs nvs_flash app_update spiffs bootloader_support openssl bt esp_ipc esp_hid)
|
||||||
|
|
||||||
|
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)
|
||||||
|
target_compile_options(${COMPONENT_TARGET} PUBLIC
|
||||||
|
-DARDUINO=10812
|
||||||
|
-DARDUINO_${idf_target_caps}_DEV
|
||||||
|
-DARDUINO_ARCH_ESP32
|
||||||
|
-DARDUINO_BOARD="${idf_target_caps}_DEV"
|
||||||
|
-DARDUINO_VARIANT="${CONFIG_ARDUINO_VARIANT}"
|
||||||
|
-DESP32)
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
# 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()
|
||||||
|
|
||||||
|
maybe_add_component(esp-dsp)
|
||||||
|
|
||||||
|
if(CONFIG_ESP_RMAKER_WORK_QUEUE_TASK_STACK)
|
||||||
|
maybe_add_component(esp_rainmaker)
|
||||||
|
maybe_add_component(qrcode)
|
||||||
|
endif()
|
||||||
|
if(IDF_TARGET MATCHES "esp32s2|esp32s3" 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_LITTLEFS)
|
||||||
|
maybe_add_component(esp_littlefs)
|
||||||
|
endif()
|
50
CONTRIBUTING.rst
Normal file
50
CONTRIBUTING.rst
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
Contributions Guide
|
||||||
|
===================
|
||||||
|
|
||||||
|
We welcome contributions to the Arduino ESP32 project!
|
||||||
|
|
||||||
|
How to Contribute
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Contributions to Arduino ESP32 - fixing bugs, adding features, adding documentation - are welcome. We accept contributions via `Github Pull Requests <https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests>`_.
|
||||||
|
|
||||||
|
Before Contributing
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Before sending us a Pull Request, please consider this list of points:
|
||||||
|
|
||||||
|
* Is the contribution entirely your own work, or already licensed under an LGPL 2.1 compatible Open Source License? If not then we unfortunately cannot accept it.
|
||||||
|
|
||||||
|
* Is the code adequately commented for people to understand how it is structured?
|
||||||
|
|
||||||
|
* Is there documentation or examples that go with code contributions?
|
||||||
|
|
||||||
|
* Are comments and documentation written in clear English, with no spelling or grammar errors?
|
||||||
|
|
||||||
|
* Example contributions are also welcome.
|
||||||
|
|
||||||
|
* If the contribution contains multiple commits, are they grouped together into logical changes (one major change per pull request)? Are any commits with names like "fixed typo" `squashed into previous commits <https://eli.thegreenplace.net/2014/02/19/squashing-github-pull-requests-into-a-single-commit/>`_?
|
||||||
|
|
||||||
|
* If you're unsure about any of these points, please open the Pull Request anyhow and then ask us for feedback.
|
||||||
|
|
||||||
|
Pull Request Process
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
After you open the Pull Request, there will probably be some discussion in the comments field of the request itself.
|
||||||
|
|
||||||
|
Once the Pull Request is ready to merge, it will first be merged into our internal git system for in-house automated testing.
|
||||||
|
|
||||||
|
If this process passes, it will be merged onto the public github repository.
|
||||||
|
|
||||||
|
Legal Part
|
||||||
|
----------
|
||||||
|
|
||||||
|
Before a contribution can be accepted, you will need to sign our :doc:`contributor-agreement`. You will be prompted for this automatically as part of the Pull Request process.
|
||||||
|
|
||||||
|
Related Documents
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
|
contributor-agreement
|
412
Kconfig.projbuild
Normal file
412
Kconfig.projbuild
Normal file
@ -0,0 +1,412 @@
|
|||||||
|
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_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 follwing 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_ArduinoOTA
|
||||||
|
bool "Enable ArduinoOTA"
|
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||||
|
select ARDUINO_SELECTIVE_WiFi
|
||||||
|
select ARDUINO_SELECTIVE_ESPmDNS
|
||||||
|
default y
|
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_AsyncUDP
|
||||||
|
bool "Enable AsyncUDP"
|
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||||
|
default y
|
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_AzureIoT
|
||||||
|
bool "Enable AzureIoT"
|
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||||
|
select ARDUINO_SELECTIVE_HTTPClient
|
||||||
|
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_DNSServer
|
||||||
|
bool "Enable DNSServer"
|
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||||
|
select ARDUINO_SELECTIVE_WiFi
|
||||||
|
default y
|
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_EEPROM
|
||||||
|
bool "Enable EEPROM"
|
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||||
|
default y
|
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_ESP32
|
||||||
|
bool "Enable ESP32"
|
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||||
|
default y
|
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_ESPmDNS
|
||||||
|
bool "Enable ESPmDNS"
|
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||||
|
select ARDUINO_SELECTIVE_WiFi
|
||||||
|
default y
|
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_FFat
|
||||||
|
bool "Enable FFat"
|
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||||
|
select ARDUINO_SELECTIVE_FS
|
||||||
|
default y
|
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_FS
|
||||||
|
bool "Enable FS"
|
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||||
|
default y
|
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_HTTPClient
|
||||||
|
bool "Enable HTTPClient"
|
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||||
|
select ARDUINO_SELECTIVE_WiFi
|
||||||
|
select ARDUINO_SELECTIVE_WiFiClientSecure
|
||||||
|
default y
|
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_LITTLEFS
|
||||||
|
bool "Enable LITTLEFS"
|
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||||
|
select ARDUINO_SELECTIVE_FS
|
||||||
|
default y
|
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_NetBIOS
|
||||||
|
bool "Enable NetBIOS"
|
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||||
|
select ARDUINO_SELECTIVE_WiFi
|
||||||
|
default y
|
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_Preferences
|
||||||
|
bool "Enable Preferences"
|
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||||
|
default y
|
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_SD
|
||||||
|
bool "Enable SD"
|
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||||
|
select ARDUINO_SELECTIVE_FS
|
||||||
|
default y
|
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_SD_MMC
|
||||||
|
bool "Enable SD_MMC"
|
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||||
|
select ARDUINO_SELECTIVE_FS
|
||||||
|
default y
|
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_SimpleBLE
|
||||||
|
bool "Enable SimpleBLE"
|
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||||
|
default y
|
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_SPI
|
||||||
|
bool "Enable SPI"
|
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||||
|
default y
|
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_SPIFFS
|
||||||
|
bool "Enable SPIFFS"
|
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||||
|
select ARDUINO_SELECTIVE_FS
|
||||||
|
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_WebServer
|
||||||
|
bool "Enable WebServer"
|
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||||
|
default y
|
||||||
|
select ARDUINO_SELECTIVE_FS
|
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_WiFi
|
||||||
|
bool "Enable WiFi"
|
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||||
|
default y
|
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_WiFiClientSecure
|
||||||
|
bool "Enable WiFiClientSecure"
|
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||||
|
select ARDUINO_SELECTIVE_WiFi
|
||||||
|
default y
|
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_WiFiProv
|
||||||
|
bool "Enable WiFiProv"
|
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||||
|
select ARDUINO_SELECTIVE_WiFi
|
||||||
|
default y
|
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_Wire
|
||||||
|
bool "Enable Wire"
|
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||||
|
default y
|
||||||
|
|
||||||
|
|
||||||
|
endmenu
|
||||||
|
|
503
LICENSE.md
Normal file
503
LICENSE.md
Normal file
@ -0,0 +1,503 @@
|
|||||||
|
### GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
|
||||||
|
Version 2.1, February 1999
|
||||||
|
|
||||||
|
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
[This is the first released version of the Lesser GPL. It also counts
|
||||||
|
as the successor of the GNU Library Public License, version 2, hence
|
||||||
|
the version number 2.1.]
|
||||||
|
|
||||||
|
### Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your freedom
|
||||||
|
to share and change it. By contrast, the GNU General Public Licenses
|
||||||
|
are intended to guarantee your freedom to share and change free
|
||||||
|
software--to make sure the software is free for all its users.
|
||||||
|
|
||||||
|
This license, the Lesser General Public License, applies to some
|
||||||
|
specially designated software packages--typically libraries--of the
|
||||||
|
Free Software Foundation and other authors who decide to use it. You
|
||||||
|
can use it too, but we suggest you first think carefully about whether
|
||||||
|
this license or the ordinary General Public License is the better
|
||||||
|
strategy to use in any particular case, based on the explanations
|
||||||
|
below.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom of use,
|
||||||
|
not price. Our General Public Licenses are designed to make sure that
|
||||||
|
you have the freedom to distribute copies of free software (and charge
|
||||||
|
for this service if you wish); that you receive source code or can get
|
||||||
|
it if you want it; that you can change the software and use pieces of
|
||||||
|
it in new free programs; and that you are informed that you can do
|
||||||
|
these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
distributors to deny you these rights or to ask you to surrender these
|
||||||
|
rights. These restrictions translate to certain responsibilities for
|
||||||
|
you if you distribute copies of the library or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of the library, whether gratis
|
||||||
|
or for a fee, you must give the recipients all the rights that we gave
|
||||||
|
you. You must make sure that they, too, receive or can get the source
|
||||||
|
code. If you link other code with the library, you must provide
|
||||||
|
complete object files to the recipients, so that they can relink them
|
||||||
|
with the library after making changes to the library and recompiling
|
||||||
|
it. And you must show them these terms so they know their rights.
|
||||||
|
|
||||||
|
We protect your rights with a two-step method: (1) we copyright the
|
||||||
|
library, and (2) we offer you this license, which gives you legal
|
||||||
|
permission to copy, distribute and/or modify the library.
|
||||||
|
|
||||||
|
To protect each distributor, we want to make it very clear that there
|
||||||
|
is no warranty for the free library. Also, if the library is modified
|
||||||
|
by someone else and passed on, the recipients should know that what
|
||||||
|
they have is not the original version, so that the original author's
|
||||||
|
reputation will not be affected by problems that might be introduced
|
||||||
|
by others.
|
||||||
|
|
||||||
|
Finally, software patents pose a constant threat to the existence of
|
||||||
|
any free program. We wish to make sure that a company cannot
|
||||||
|
effectively restrict the users of a free program by obtaining a
|
||||||
|
restrictive license from a patent holder. Therefore, we insist that
|
||||||
|
any patent license obtained for a version of the library must be
|
||||||
|
consistent with the full freedom of use specified in this license.
|
||||||
|
|
||||||
|
Most GNU software, including some libraries, is covered by the
|
||||||
|
ordinary GNU General Public License. This license, the GNU Lesser
|
||||||
|
General Public License, applies to certain designated libraries, and
|
||||||
|
is quite different from the ordinary General Public License. We use
|
||||||
|
this license for certain libraries in order to permit linking those
|
||||||
|
libraries into non-free programs.
|
||||||
|
|
||||||
|
When a program is linked with a library, whether statically or using a
|
||||||
|
shared library, the combination of the two is legally speaking a
|
||||||
|
combined work, a derivative of the original library. The ordinary
|
||||||
|
General Public License therefore permits such linking only if the
|
||||||
|
entire combination fits its criteria of freedom. The Lesser General
|
||||||
|
Public License permits more lax criteria for linking other code with
|
||||||
|
the library.
|
||||||
|
|
||||||
|
We call this license the "Lesser" General Public License because it
|
||||||
|
does Less to protect the user's freedom than the ordinary General
|
||||||
|
Public License. It also provides other free software developers Less
|
||||||
|
of an advantage over competing non-free programs. These disadvantages
|
||||||
|
are the reason we use the ordinary General Public License for many
|
||||||
|
libraries. However, the Lesser license provides advantages in certain
|
||||||
|
special circumstances.
|
||||||
|
|
||||||
|
For example, on rare occasions, there may be a special need to
|
||||||
|
encourage the widest possible use of a certain library, so that it
|
||||||
|
becomes a de-facto standard. To achieve this, non-free programs must
|
||||||
|
be allowed to use the library. A more frequent case is that a free
|
||||||
|
library does the same job as widely used non-free libraries. In this
|
||||||
|
case, there is little to gain by limiting the free library to free
|
||||||
|
software only, so we use the Lesser General Public License.
|
||||||
|
|
||||||
|
In other cases, permission to use a particular library in non-free
|
||||||
|
programs enables a greater number of people to use a large body of
|
||||||
|
free software. For example, permission to use the GNU C Library in
|
||||||
|
non-free programs enables many more people to use the whole GNU
|
||||||
|
operating system, as well as its variant, the GNU/Linux operating
|
||||||
|
system.
|
||||||
|
|
||||||
|
Although the Lesser General Public License is Less protective of the
|
||||||
|
users' freedom, it does ensure that the user of a program that is
|
||||||
|
linked with the Library has the freedom and the wherewithal to run
|
||||||
|
that program using a modified version of the Library.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow. Pay close attention to the difference between a
|
||||||
|
"work based on the library" and a "work that uses the library". The
|
||||||
|
former contains code derived from the library, whereas the latter must
|
||||||
|
be combined with the library in order to run.
|
||||||
|
|
||||||
|
### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
**0.** This License Agreement applies to any software library or other
|
||||||
|
program which contains a notice placed by the copyright holder or
|
||||||
|
other authorized party saying it may be distributed under the terms of
|
||||||
|
this Lesser General Public License (also called "this License"). Each
|
||||||
|
licensee is addressed as "you".
|
||||||
|
|
||||||
|
A "library" means a collection of software functions and/or data
|
||||||
|
prepared so as to be conveniently linked with application programs
|
||||||
|
(which use some of those functions and data) to form executables.
|
||||||
|
|
||||||
|
The "Library", below, refers to any such software library or work
|
||||||
|
which has been distributed under these terms. A "work based on the
|
||||||
|
Library" means either the Library or any derivative work under
|
||||||
|
copyright law: that is to say, a work containing the Library or a
|
||||||
|
portion of it, either verbatim or with modifications and/or translated
|
||||||
|
straightforwardly into another language. (Hereinafter, translation is
|
||||||
|
included without limitation in the term "modification".)
|
||||||
|
|
||||||
|
"Source code" for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For a library, complete source code means
|
||||||
|
all the source code for all modules it contains, plus any associated
|
||||||
|
interface definition files, plus the scripts used to control
|
||||||
|
compilation and installation of the library.
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running a program using the Library is not restricted, and output from
|
||||||
|
such a program is covered only if its contents constitute a work based
|
||||||
|
on the Library (independent of the use of the Library in a tool for
|
||||||
|
writing it). Whether that is true depends on what the Library does and
|
||||||
|
what the program that uses the Library does.
|
||||||
|
|
||||||
|
**1.** You may copy and distribute verbatim copies of the Library's
|
||||||
|
complete source code as you receive it, in any medium, provided that
|
||||||
|
you conspicuously and appropriately publish on each copy an
|
||||||
|
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||||
|
all the notices that refer to this License and to the absence of any
|
||||||
|
warranty; and distribute a copy of this License along with the
|
||||||
|
Library.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and
|
||||||
|
you may at your option offer warranty protection in exchange for a
|
||||||
|
fee.
|
||||||
|
|
||||||
|
**2.** You may modify your copy or copies of the Library or any
|
||||||
|
portion of it, thus forming a work based on the Library, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
- **a)** The modified work must itself be a software library.
|
||||||
|
- **b)** You must cause the files modified to carry prominent
|
||||||
|
notices stating that you changed the files and the date of
|
||||||
|
any change.
|
||||||
|
- **c)** You must cause the whole of the work to be licensed at no
|
||||||
|
charge to all third parties under the terms of this License.
|
||||||
|
- **d)** If a facility in the modified Library refers to a function
|
||||||
|
or a table of data to be supplied by an application program that
|
||||||
|
uses the facility, other than as an argument passed when the
|
||||||
|
facility is invoked, then you must make a good faith effort to
|
||||||
|
ensure that, in the event an application does not supply such
|
||||||
|
function or table, the facility still operates, and performs
|
||||||
|
whatever part of its purpose remains meaningful.
|
||||||
|
|
||||||
|
(For example, a function in a library to compute square roots has
|
||||||
|
a purpose that is entirely well-defined independent of
|
||||||
|
the application. Therefore, Subsection 2d requires that any
|
||||||
|
application-supplied function or table used by this function must
|
||||||
|
be optional: if the application does not supply it, the square
|
||||||
|
root function must still compute square roots.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Library,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Library, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote
|
||||||
|
it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Library.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Library
|
||||||
|
with the Library (or with a work based on the Library) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
**3.** You may opt to apply the terms of the ordinary GNU General
|
||||||
|
Public License instead of this License to a given copy of the Library.
|
||||||
|
To do this, you must alter all the notices that refer to this License,
|
||||||
|
so that they refer to the ordinary GNU General Public License, version
|
||||||
|
2, instead of to this License. (If a newer version than version 2 of
|
||||||
|
the ordinary GNU General Public License has appeared, then you can
|
||||||
|
specify that version instead if you wish.) Do not make any other
|
||||||
|
change in these notices.
|
||||||
|
|
||||||
|
Once this change is made in a given copy, it is irreversible for that
|
||||||
|
copy, so the ordinary GNU General Public License applies to all
|
||||||
|
subsequent copies and derivative works made from that copy.
|
||||||
|
|
||||||
|
This option is useful when you wish to copy part of the code of the
|
||||||
|
Library into a program that is not a library.
|
||||||
|
|
||||||
|
**4.** You may copy and distribute the Library (or a portion or
|
||||||
|
derivative of it, under Section 2) in object code or executable form
|
||||||
|
under the terms of Sections 1 and 2 above provided that you accompany
|
||||||
|
it with the complete corresponding machine-readable source code, which
|
||||||
|
must be distributed under the terms of Sections 1 and 2 above on a
|
||||||
|
medium customarily used for software interchange.
|
||||||
|
|
||||||
|
If distribution of object code is made by offering access to copy from
|
||||||
|
a designated place, then offering equivalent access to copy the source
|
||||||
|
code from the same place satisfies the requirement to distribute the
|
||||||
|
source code, even though third parties are not compelled to copy the
|
||||||
|
source along with the object code.
|
||||||
|
|
||||||
|
**5.** A program that contains no derivative of any portion of the
|
||||||
|
Library, but is designed to work with the Library by being compiled or
|
||||||
|
linked with it, is called a "work that uses the Library". Such a work,
|
||||||
|
in isolation, is not a derivative work of the Library, and therefore
|
||||||
|
falls outside the scope of this License.
|
||||||
|
|
||||||
|
However, linking a "work that uses the Library" with the Library
|
||||||
|
creates an executable that is a derivative of the Library (because it
|
||||||
|
contains portions of the Library), rather than a "work that uses the
|
||||||
|
library". The executable is therefore covered by this License. Section
|
||||||
|
6 states terms for distribution of such executables.
|
||||||
|
|
||||||
|
When a "work that uses the Library" uses material from a header file
|
||||||
|
that is part of the Library, the object code for the work may be a
|
||||||
|
derivative work of the Library even though the source code is not.
|
||||||
|
Whether this is true is especially significant if the work can be
|
||||||
|
linked without the Library, or if the work is itself a library. The
|
||||||
|
threshold for this to be true is not precisely defined by law.
|
||||||
|
|
||||||
|
If such an object file uses only numerical parameters, data structure
|
||||||
|
layouts and accessors, and small macros and small inline functions
|
||||||
|
(ten lines or less in length), then the use of the object file is
|
||||||
|
unrestricted, regardless of whether it is legally a derivative work.
|
||||||
|
(Executables containing this object code plus portions of the Library
|
||||||
|
will still fall under Section 6.)
|
||||||
|
|
||||||
|
Otherwise, if the work is a derivative of the Library, you may
|
||||||
|
distribute the object code for the work under the terms of Section 6.
|
||||||
|
Any executables containing that work also fall under Section 6,
|
||||||
|
whether or not they are linked directly with the Library itself.
|
||||||
|
|
||||||
|
**6.** As an exception to the Sections above, you may also combine or
|
||||||
|
link a "work that uses the Library" with the Library to produce a work
|
||||||
|
containing portions of the Library, and distribute that work under
|
||||||
|
terms of your choice, provided that the terms permit modification of
|
||||||
|
the work for the customer's own use and reverse engineering for
|
||||||
|
debugging such modifications.
|
||||||
|
|
||||||
|
You must give prominent notice with each copy of the work that the
|
||||||
|
Library is used in it and that the Library and its use are covered by
|
||||||
|
this License. You must supply a copy of this License. If the work
|
||||||
|
during execution displays copyright notices, you must include the
|
||||||
|
copyright notice for the Library among them, as well as a reference
|
||||||
|
directing the user to the copy of this License. Also, you must do one
|
||||||
|
of these things:
|
||||||
|
|
||||||
|
- **a)** Accompany the work with the complete corresponding
|
||||||
|
machine-readable source code for the Library including whatever
|
||||||
|
changes were used in the work (which must be distributed under
|
||||||
|
Sections 1 and 2 above); and, if the work is an executable linked
|
||||||
|
with the Library, with the complete machine-readable "work that
|
||||||
|
uses the Library", as object code and/or source code, so that the
|
||||||
|
user can modify the Library and then relink to produce a modified
|
||||||
|
executable containing the modified Library. (It is understood that
|
||||||
|
the user who changes the contents of definitions files in the
|
||||||
|
Library will not necessarily be able to recompile the application
|
||||||
|
to use the modified definitions.)
|
||||||
|
- **b)** Use a suitable shared library mechanism for linking with
|
||||||
|
the Library. A suitable mechanism is one that (1) uses at run time
|
||||||
|
a copy of the library already present on the user's computer
|
||||||
|
system, rather than copying library functions into the executable,
|
||||||
|
and (2) will operate properly with a modified version of the
|
||||||
|
library, if the user installs one, as long as the modified version
|
||||||
|
is interface-compatible with the version that the work was
|
||||||
|
made with.
|
||||||
|
- **c)** Accompany the work with a written offer, valid for at least
|
||||||
|
three years, to give the same user the materials specified in
|
||||||
|
Subsection 6a, above, for a charge no more than the cost of
|
||||||
|
performing this distribution.
|
||||||
|
- **d)** If distribution of the work is made by offering access to
|
||||||
|
copy from a designated place, offer equivalent access to copy the
|
||||||
|
above specified materials from the same place.
|
||||||
|
- **e)** Verify that the user has already received a copy of these
|
||||||
|
materials or that you have already sent this user a copy.
|
||||||
|
|
||||||
|
For an executable, the required form of the "work that uses the
|
||||||
|
Library" must include any data and utility programs needed for
|
||||||
|
reproducing the executable from it. However, as a special exception,
|
||||||
|
the materials to be distributed need not include anything that is
|
||||||
|
normally distributed (in either source or binary form) with the major
|
||||||
|
components (compiler, kernel, and so on) of the operating system on
|
||||||
|
which the executable runs, unless that component itself accompanies
|
||||||
|
the executable.
|
||||||
|
|
||||||
|
It may happen that this requirement contradicts the license
|
||||||
|
restrictions of other proprietary libraries that do not normally
|
||||||
|
accompany the operating system. Such a contradiction means you cannot
|
||||||
|
use both them and the Library together in an executable that you
|
||||||
|
distribute.
|
||||||
|
|
||||||
|
**7.** You may place library facilities that are a work based on the
|
||||||
|
Library side-by-side in a single library together with other library
|
||||||
|
facilities not covered by this License, and distribute such a combined
|
||||||
|
library, provided that the separate distribution of the work based on
|
||||||
|
the Library and of the other library facilities is otherwise
|
||||||
|
permitted, and provided that you do these two things:
|
||||||
|
|
||||||
|
- **a)** Accompany the combined library with a copy of the same work
|
||||||
|
based on the Library, uncombined with any other
|
||||||
|
library facilities. This must be distributed under the terms of
|
||||||
|
the Sections above.
|
||||||
|
- **b)** Give prominent notice with the combined library of the fact
|
||||||
|
that part of it is a work based on the Library, and explaining
|
||||||
|
where to find the accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
**8.** You may not copy, modify, sublicense, link with, or distribute
|
||||||
|
the Library except as expressly provided under this License. Any
|
||||||
|
attempt otherwise to copy, modify, sublicense, link with, or
|
||||||
|
distribute the Library is void, and will automatically terminate your
|
||||||
|
rights under this License. However, parties who have received copies,
|
||||||
|
or rights, from you under this License will not have their licenses
|
||||||
|
terminated so long as such parties remain in full compliance.
|
||||||
|
|
||||||
|
**9.** You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Library or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Library (or any work based on the
|
||||||
|
Library), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Library or works based on it.
|
||||||
|
|
||||||
|
**10.** Each time you redistribute the Library (or any work based on
|
||||||
|
the Library), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute, link with or modify the Library
|
||||||
|
subject to these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties with
|
||||||
|
this License.
|
||||||
|
|
||||||
|
**11.** If, as a consequence of a court judgment or allegation of
|
||||||
|
patent infringement or for any other reason (not limited to patent
|
||||||
|
issues), conditions are imposed on you (whether by court order,
|
||||||
|
agreement or otherwise) that contradict the conditions of this
|
||||||
|
License, they do not excuse you from the conditions of this License.
|
||||||
|
If you cannot distribute so as to satisfy simultaneously your
|
||||||
|
obligations under this License and any other pertinent obligations,
|
||||||
|
then as a consequence you may not distribute the Library at all. For
|
||||||
|
example, if a patent license would not permit royalty-free
|
||||||
|
redistribution of the Library by all those who receive copies directly
|
||||||
|
or indirectly through you, then the only way you could satisfy both it
|
||||||
|
and this License would be to refrain entirely from distribution of the
|
||||||
|
Library.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under
|
||||||
|
any particular circumstance, the balance of the section is intended to
|
||||||
|
apply, and the section as a whole is intended to apply in other
|
||||||
|
circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
**12.** If the distribution and/or use of the Library is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Library under this License
|
||||||
|
may add an explicit geographical distribution limitation excluding
|
||||||
|
those countries, so that distribution is permitted only in or among
|
||||||
|
countries not thus excluded. In such case, this License incorporates
|
||||||
|
the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
**13.** The Free Software Foundation may publish revised and/or new
|
||||||
|
versions of the Lesser General Public License from time to time. Such
|
||||||
|
new versions will be similar in spirit to the present version, but may
|
||||||
|
differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Library
|
||||||
|
specifies a version number of this License which applies to it and
|
||||||
|
"any later version", you have the option of following the terms and
|
||||||
|
conditions either of that version or of any later version published by
|
||||||
|
the Free Software Foundation. If the Library does not specify a
|
||||||
|
license version number, you may choose any version ever published by
|
||||||
|
the Free Software Foundation.
|
||||||
|
|
||||||
|
**14.** If you wish to incorporate parts of the Library into other
|
||||||
|
free programs whose distribution conditions are incompatible with
|
||||||
|
these, write to the author to ask for permission. For software which
|
||||||
|
is copyrighted by the Free Software Foundation, write to the Free
|
||||||
|
Software Foundation; we sometimes make exceptions for this. Our
|
||||||
|
decision will be guided by the two goals of preserving the free status
|
||||||
|
of all derivatives of our free software and of promoting the sharing
|
||||||
|
and reuse of software generally.
|
||||||
|
|
||||||
|
**NO WARRANTY**
|
||||||
|
|
||||||
|
**15.** BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||||
|
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||||
|
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||||
|
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||||
|
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||||
|
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||||
|
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
**16.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||||
|
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||||
|
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||||
|
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||||
|
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||||
|
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||||
|
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||||
|
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||||
|
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||||
|
DAMAGES.
|
||||||
|
|
||||||
|
### END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
### How to Apply These Terms to Your New Libraries
|
||||||
|
|
||||||
|
If you develop a new library, and you want it to be of the greatest
|
||||||
|
possible use to the public, we recommend making it free software that
|
||||||
|
everyone can redistribute and change. You can do so by permitting
|
||||||
|
redistribution under these terms (or, alternatively, under the terms
|
||||||
|
of the ordinary General Public License).
|
||||||
|
|
||||||
|
To apply these terms, attach the following notices to the library. It
|
||||||
|
is safest to attach them to the start of each source file to most
|
||||||
|
effectively convey the exclusion of warranty; and each file should
|
||||||
|
have at least the "copyright" line and a pointer to where the full
|
||||||
|
notice is found.
|
||||||
|
|
||||||
|
one line to give the library's name and an idea of what it does.
|
||||||
|
Copyright (C) year name of author
|
||||||
|
|
||||||
|
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 Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper
|
||||||
|
mail.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or
|
||||||
|
your school, if any, to sign a "copyright disclaimer" for the library,
|
||||||
|
if necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in
|
||||||
|
the library `Frob' (a library for tweaking knobs) written
|
||||||
|
by James Random Hacker.
|
||||||
|
|
||||||
|
signature of Ty Coon, 1 April 1990
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
That's all there is to it!
|
58
README.md
Normal file
58
README.md
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# Arduino core for the ESP32, ESP32-S2, ESP32-S3 and ESP32-C3
|
||||||
|
|
||||||
|
![Build Status](https://github.com/espressif/arduino-esp32/workflows/ESP32%20Arduino%20CI/badge.svg) [![Documentation Status](https://readthedocs.com/projects/espressif-arduino-esp32/badge/?version=latest)](https://docs.espressif.com/projects/arduino-esp32/en/latest/?badge=latest)
|
||||||
|
|
||||||
|
### Need help or have a question? Join the chat at [![https://gitter.im/espressif/arduino-esp32](https://badges.gitter.im/espressif/arduino-esp32.svg)](https://gitter.im/espressif/arduino-esp32?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) or [open a new Discussion](https://github.com/espressif/arduino-esp32/discussions)
|
||||||
|
|
||||||
|
## Contents
|
||||||
|
|
||||||
|
- [Development Status](#development-status)
|
||||||
|
- [Development Planning](#development-planning)
|
||||||
|
- [Documentation](#documentation)
|
||||||
|
- [Supported Chips](#supported-chips)
|
||||||
|
- [Decoding exceptions](#decoding-exceptions)
|
||||||
|
- [Issue/Bug report template](#issuebug-report-template)
|
||||||
|
- [Contributing](#contributing)
|
||||||
|
|
||||||
|
### Development Status
|
||||||
|
|
||||||
|
Latest Stable Release [![Release Version](https://img.shields.io/github/release/espressif/arduino-esp32.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/latest/) [![Release Date](https://img.shields.io/github/release-date/espressif/arduino-esp32.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/latest/) [![Downloads](https://img.shields.io/github/downloads/espressif/arduino-esp32/latest/total.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/latest/)
|
||||||
|
|
||||||
|
Latest Development Release [![Release Version](https://img.shields.io/github/release/espressif/arduino-esp32/all.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/) [![Release Date](https://img.shields.io/github/release-date-pre/espressif/arduino-esp32.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/) [![Downloads](https://img.shields.io/github/downloads-pre/espressif/arduino-esp32/latest/total.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/)
|
||||||
|
|
||||||
|
### Development Planning
|
||||||
|
|
||||||
|
Our Development is fully tracked on this public **[Roadmap 🎉](https://github.com/orgs/espressif/projects/3)**
|
||||||
|
|
||||||
|
For even more information you can take a look at [Sprint Meeting notes](https://github.com/espressif/arduino-esp32/discussions/categories/sprints-meeting-notes) or join [Monthly Community Meetings 🔔](https://github.com/espressif/arduino-esp32/discussions/categories/monthly-community-meetings)
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
You can use the [Arduino-ESP32 Online Documentation](https://docs.espressif.com/projects/arduino-esp32/en/latest/) to get all information about this project.
|
||||||
|
|
||||||
|
* [Getting Started](https://docs.espressif.com/projects/arduino-esp32/en/latest/getting_started.html)
|
||||||
|
* [Installing (Windows, Linux and macOS)](https://docs.espressif.com/projects/arduino-esp32/en/latest/installing.html)
|
||||||
|
* [Libraries](https://docs.espressif.com/projects/arduino-esp32/en/latest/libraries.html)
|
||||||
|
* [ESP-IDF as Component](https://docs.espressif.com/projects/arduino-esp32/en/latest/esp-idf_component.html)
|
||||||
|
* [FAQ](https://docs.espressif.com/projects/arduino-esp32/en/latest/faq.html)
|
||||||
|
* [Troubleshooting](https://docs.espressif.com/projects/arduino-esp32/en/latest/troubleshooting.html)
|
||||||
|
|
||||||
|
### Supported Chips
|
||||||
|
|
||||||
|
Visit the [supported chips](https://docs.espressif.com/projects/arduino-esp32/en/latest/getting_started.html#supported-soc-s) documentation to see the list of current supported ESP32 SoCs.
|
||||||
|
|
||||||
|
### Decoding exceptions
|
||||||
|
|
||||||
|
You can use [EspExceptionDecoder](https://github.com/me-no-dev/EspExceptionDecoder) to get meaningful call trace.
|
||||||
|
|
||||||
|
### Issue/Bug report template
|
||||||
|
|
||||||
|
Before reporting an issue, make sure you've searched for similar one that was already created. Also make sure to go through all the issues labelled as [Type: For reference](https://github.com/espressif/arduino-esp32/issues?q=is%3Aissue+label%3A%22Type%3A+For+reference%22+).
|
||||||
|
|
||||||
|
Finally, if you are sure no one else had the issue, follow the **Issue template** or **Feature request template** while reporting any [new Issue](https://github.com/espressif/arduino-esp32/issues/new/choose).
|
||||||
|
|
||||||
|
### Contributing
|
||||||
|
|
||||||
|
We welcome contributions to the Arduino ESP32 project!
|
||||||
|
|
||||||
|
See [contributing](https://docs.espressif.com/projects/arduino-esp32/en/latest/contributing.html) in the documentation for more information on how to contribute to the project.
|
19017
boards.txt
Normal file
19017
boards.txt
Normal file
File diff suppressed because it is too large
Load Diff
216
cores/esp32/Arduino.h
Normal file
216
cores/esp32/Arduino.h
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
/*
|
||||||
|
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 "esp32-hal.h"
|
||||||
|
#include "esp8266-compat.h"
|
||||||
|
#include "soc/gpio_reg.h"
|
||||||
|
|
||||||
|
#include "stdlib_noniso.h"
|
||||||
|
#include "binary.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 << (pin))
|
||||||
|
#define portOutputRegister(port) ((volatile uint32_t*)GPIO_OUT_REG)
|
||||||
|
#define portInputRegister(port) ((volatile uint32_t*)GPIO_IN_REG)
|
||||||
|
#define portModeRegister(port) ((volatile uint32_t*)GPIO_ENABLE_REG)
|
||||||
|
#elif SOC_GPIO_PIN_COUNT <= 64
|
||||||
|
#define digitalPinToPort(pin) (((pin)>31)?1:0)
|
||||||
|
#define digitalPinToBitMask(pin) (1UL << (((pin)>31)?((pin)-32):(pin)))
|
||||||
|
#define 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
|
||||||
|
|
||||||
|
typedef bool boolean;
|
||||||
|
typedef uint8_t byte;
|
||||||
|
typedef unsigned int word;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
void setup(void);
|
||||||
|
void loop(void);
|
||||||
|
|
||||||
|
long random(long, long);
|
||||||
|
#endif
|
||||||
|
void randomSeed(unsigned long);
|
||||||
|
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);
|
||||||
|
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 "esp32/spiram.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;}
|
||||||
|
|
||||||
|
// allows user to bypass esp_spiram_test()
|
||||||
|
#define BYPASS_SPIRAM_TEST(bypass) bool testSPIRAM(void) { if (bypass) return true; else return esp_spiram_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);
|
||||||
|
|
||||||
|
// WMath prototypes
|
||||||
|
long random(long);
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
#include "pins_arduino.h"
|
||||||
|
|
||||||
|
#endif /* _ESP32_CORE_ARDUINO_H_ */
|
48
cores/esp32/Client.h
Normal file
48
cores/esp32/Client.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
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
|
442
cores/esp32/Esp.cpp
Normal file
442
cores/esp32/Esp.cpp
Normal file
@ -0,0 +1,442 @@
|
|||||||
|
/*
|
||||||
|
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 "esp_spi_flash.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"
|
||||||
|
#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_ESP32C3
|
||||||
|
#include "esp32c3/rom/spi_flash.h"
|
||||||
|
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32c3 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
|
||||||
|
#ifndef REG_SPI_BASE
|
||||||
|
#define REG_SPI_BASE(i) (DR_REG_SPI1_BASE + (((i)>1) ? (((i)* 0x1000) + 0x20000) : (((~(i)) & 1)* 0x1000 )))
|
||||||
|
#endif // REG_SPI_BASE
|
||||||
|
#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(uint32_t time_us)
|
||||||
|
{
|
||||||
|
esp_deep_sleep(time_us);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EspClass::restart(void)
|
||||||
|
{
|
||||||
|
esp_restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t EspClass::getHeapSize(void)
|
||||||
|
{
|
||||||
|
multi_heap_info_t info;
|
||||||
|
heap_caps_get_info(&info, MALLOC_CAP_INTERNAL);
|
||||||
|
return info.total_free_bytes + info.total_allocated_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
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()){
|
||||||
|
multi_heap_info_t info;
|
||||||
|
heap_caps_get_info(&info, MALLOC_CAP_SPIRAM);
|
||||||
|
return info.total_free_bytes + info.total_allocated_bytes;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
std::unique_ptr<uint8_t[]> buf(new uint8_t[bufSize]);
|
||||||
|
uint32_t offset = 0;
|
||||||
|
if(!buf.get()) {
|
||||||
|
log_e("Not enough memory to allocate buffer");
|
||||||
|
|
||||||
|
return String();
|
||||||
|
}
|
||||||
|
MD5Builder md5;
|
||||||
|
md5.begin();
|
||||||
|
while( lengthLeft > 0) {
|
||||||
|
size_t readBytes = (lengthLeft < bufSize) ? lengthLeft : bufSize;
|
||||||
|
if (!ESP.flashRead(running->address + offset, reinterpret_cast<uint32_t*>(buf.get()), (readBytes + 3) & ~3)) {
|
||||||
|
log_e("Could not read buffer from flash");
|
||||||
|
|
||||||
|
return String();
|
||||||
|
}
|
||||||
|
md5.add(buf.get(), readBytes);
|
||||||
|
lengthLeft -= readBytes;
|
||||||
|
offset += readBytes;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_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_VER_PKG);
|
||||||
|
uint32_t pkg_ver = chip_ver & 0x7;
|
||||||
|
switch (pkg_ver) {
|
||||||
|
case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ6 :
|
||||||
|
return "ESP32-D0WDQ6";
|
||||||
|
case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ5 :
|
||||||
|
return "ESP32-D0WDQ5";
|
||||||
|
case EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5 :
|
||||||
|
return "ESP32-D2WDQ5";
|
||||||
|
case EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2 :
|
||||||
|
return "ESP32-PICO-D2";
|
||||||
|
case EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4 :
|
||||||
|
return "ESP32-PICO-D4";
|
||||||
|
case EFUSE_RD_CHIP_VER_PKG_ESP32PICOV302 :
|
||||||
|
return "ESP32-PICO-V3-02";
|
||||||
|
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)";
|
||||||
|
}
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
return "ESP32-S3";
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||||
|
return "ESP32-C3";
|
||||||
|
#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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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(flashRead(ESP_FLASH_IMAGE_BASE, (uint32_t*)&fhdr, 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
|
||||||
|
uint32_t spi_ctrl = REG_READ(PERIPHS_SPI_FLASH_CTRL);
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
return (FM_DOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t EspClass::magicFlashChipSize(uint8_t byte)
|
||||||
|
{
|
||||||
|
switch(byte & 0x0F) {
|
||||||
|
case 0x0: // 8 MBit (1MB)
|
||||||
|
return (1_MB);
|
||||||
|
case 0x1: // 16 MBit (2MB)
|
||||||
|
return (2_MB);
|
||||||
|
case 0x2: // 32 MBit (4MB)
|
||||||
|
return (4_MB);
|
||||||
|
case 0x3: // 64 MBit (8MB)
|
||||||
|
return (8_MB);
|
||||||
|
case 0x4: // 128 MBit (16MB)
|
||||||
|
return (16_MB);
|
||||||
|
default: // fail?
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t EspClass::magicFlashChipSpeed(uint8_t byte)
|
||||||
|
{
|
||||||
|
switch(byte & 0x0F) {
|
||||||
|
case 0x0: // 40 MHz
|
||||||
|
return (40_MHz);
|
||||||
|
case 0x1: // 26 MHz
|
||||||
|
return (26_MHz);
|
||||||
|
case 0x2: // 20 MHz
|
||||||
|
return (20_MHz);
|
||||||
|
case 0xf: // 80 MHz
|
||||||
|
return (80_MHz);
|
||||||
|
default: // fail?
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlashMode_t EspClass::magicFlashChipMode(uint8_t byte)
|
||||||
|
{
|
||||||
|
FlashMode_t mode = (FlashMode_t) byte;
|
||||||
|
if(mode > FM_SLOW_READ) {
|
||||||
|
mode = FM_UNKNOWN;
|
||||||
|
}
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EspClass::flashEraseSector(uint32_t sector)
|
||||||
|
{
|
||||||
|
return spi_flash_erase_sector(sector) == ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warning: These functions do not work with encrypted flash
|
||||||
|
bool EspClass::flashWrite(uint32_t offset, uint32_t *data, size_t size)
|
||||||
|
{
|
||||||
|
return spi_flash_write(offset, (uint32_t*) data, size) == ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EspClass::flashRead(uint32_t offset, uint32_t *data, size_t size)
|
||||||
|
{
|
||||||
|
return spi_flash_read(offset, (uint32_t*) data, 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;
|
||||||
|
}
|
119
cores/esp32/Esp.h
Normal file
119
cores/esp32/Esp.h
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
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>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AVR macros for WDT managment
|
||||||
|
*/
|
||||||
|
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();
|
||||||
|
|
||||||
|
uint8_t getChipRevision();
|
||||||
|
const char * getChipModel();
|
||||||
|
uint8_t getChipCores();
|
||||||
|
uint32_t getCpuFreqMHz(){ return getCpuFrequencyMhz(); }
|
||||||
|
inline uint32_t getCycleCount() __attribute__((always_inline));
|
||||||
|
const char * getSdkVersion();
|
||||||
|
|
||||||
|
void deepSleep(uint32_t time_us);
|
||||||
|
|
||||||
|
uint32_t getFlashChipSize();
|
||||||
|
uint32_t getFlashChipSpeed();
|
||||||
|
FlashMode_t getFlashChipMode();
|
||||||
|
|
||||||
|
uint32_t magicFlashChipSize(uint8_t byte);
|
||||||
|
uint32_t magicFlashChipSpeed(uint8_t byte);
|
||||||
|
FlashMode_t magicFlashChipMode(uint8_t byte);
|
||||||
|
|
||||||
|
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 cpu_hal_get_cycle_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
extern EspClass ESP;
|
||||||
|
|
||||||
|
#endif //ESP_H
|
424
cores/esp32/FirmwareMSC.cpp
Normal file
424
cores/esp32/FirmwareMSC.cpp
Normal file
@ -0,0 +1,424 @@
|
|||||||
|
// 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 "esp32-hal.h"
|
||||||
|
#include "pins_arduino.h"
|
||||||
|
#include "firmware_msc_fat.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
cores/esp32/FirmwareMSC.h
Normal file
70
cores/esp32/FirmwareMSC.h
Normal 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 */
|
44
cores/esp32/FunctionalInterrupt.cpp
Normal file
44
cores/esp32/FunctionalInterrupt.cpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
20
cores/esp32/FunctionalInterrupt.h
Normal file
20
cores/esp32/FunctionalInterrupt.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* FunctionalInterrupt.h
|
||||||
|
*
|
||||||
|
* Created on: 8 jul. 2018
|
||||||
|
* Author: Herman
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CORE_CORE_FUNCTIONALINTERRUPT_H_
|
||||||
|
#define CORE_CORE_FUNCTIONALINTERRUPT_H_
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
struct InterruptArgStructure {
|
||||||
|
std::function<void(void)> interruptFunction;
|
||||||
|
};
|
||||||
|
|
||||||
|
void attachInterrupt(uint8_t pin, std::function<void(void)> intRoutine, int mode);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* CORE_CORE_FUNCTIONALINTERRUPT_H_ */
|
392
cores/esp32/HWCDC.cpp
Normal file
392
cores/esp32/HWCDC.cpp
Normal file
@ -0,0 +1,392 @@
|
|||||||
|
// 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 CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
|
||||||
|
#include "esp32-hal.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 "hal/usb_serial_jtag_ll.h"
|
||||||
|
|
||||||
|
ESP_EVENT_DEFINE_BASE(ARDUINO_HW_CDC_EVENTS);
|
||||||
|
|
||||||
|
static RingbufHandle_t tx_ring_buf = NULL;
|
||||||
|
static xQueueHandle rx_queue = NULL;
|
||||||
|
static uint8_t rx_data_buf[64];
|
||||||
|
static intr_handle_t intr_handle = NULL;
|
||||||
|
static volatile bool initial_empty = false;
|
||||||
|
static xSemaphoreHandle tx_lock = NULL;
|
||||||
|
static uint32_t tx_timeout_ms = 200;
|
||||||
|
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 = 5,
|
||||||
|
.task_stack_size = 2048,
|
||||||
|
.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 (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);
|
||||||
|
|
||||||
|
if(!initial_empty){
|
||||||
|
initial_empty = true;
|
||||||
|
//send event?
|
||||||
|
//ets_printf("CONNECTED\n");
|
||||||
|
arduino_hw_cdc_event_post(ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_CONNECTED_EVENT, &event, sizeof(arduino_hw_cdc_event_data_t), &xTaskWoken);
|
||||||
|
}
|
||||||
|
size_t queued_size;
|
||||||
|
uint8_t *queued_buff = (uint8_t *)xRingbufferReceiveUpToFromISR(tx_ring_buf, &queued_size, 64);
|
||||||
|
// If the hardware fifo is avaliable, 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);
|
||||||
|
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 avaliable 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//send event?
|
||||||
|
//ets_printf("RX:%u/%u\n", i, rx_fifo_len);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_BUS_RESET) {
|
||||||
|
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_BUS_RESET);
|
||||||
|
initial_empty = false;
|
||||||
|
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
|
||||||
|
//ets_printf("BUS_RESET\n");
|
||||||
|
arduino_hw_cdc_event_post(ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_BUS_RESET_EVENT, &event, sizeof(arduino_hw_cdc_event_data_t), &xTaskWoken);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xTaskWoken == pdTRUE) {
|
||||||
|
portYIELD_FROM_ISR();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ARDUINO_ISR_ATTR cdc0_write_char(char c) {
|
||||||
|
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_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
HWCDC::HWCDC() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
HWCDC::~HWCDC(){
|
||||||
|
end();
|
||||||
|
}
|
||||||
|
|
||||||
|
HWCDC::operator bool() const
|
||||||
|
{
|
||||||
|
return initial_empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HWCDC::begin(unsigned long baud)
|
||||||
|
{
|
||||||
|
if(tx_lock == NULL) {
|
||||||
|
tx_lock = xSemaphoreCreateMutex();
|
||||||
|
}
|
||||||
|
setRxBufferSize(256);//default if not preset
|
||||||
|
setTxBufferSize(256);//default if not preset
|
||||||
|
|
||||||
|
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_SERIAL_IN_EMPTY | USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT | USB_SERIAL_JTAG_INTR_BUS_RESET);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
usb_serial_jtag_ll_txfifo_flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HWCDC::end()
|
||||||
|
{
|
||||||
|
//Disable tx/rx interrupt.
|
||||||
|
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_LL_INTR_MASK);
|
||||||
|
esp_intr_free(intr_handle);
|
||||||
|
intr_handle = NULL;
|
||||||
|
if(tx_lock != NULL) {
|
||||||
|
vSemaphoreDelete(tx_lock);
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HWCDC::setTxTimeoutMs(uint32_t timeout){
|
||||||
|
tx_timeout_ms = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WRITING
|
||||||
|
*/
|
||||||
|
|
||||||
|
size_t HWCDC::setTxBufferSize(size_t tx_queue_len){
|
||||||
|
if(tx_ring_buf){
|
||||||
|
if(!tx_queue_len){
|
||||||
|
vRingbufferDelete(tx_ring_buf);
|
||||||
|
tx_ring_buf = NULL;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
size_t max_size = xRingbufferGetMaxItemSize(tx_ring_buf);
|
||||||
|
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(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_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
|
||||||
|
|
||||||
|
while(to_send){
|
||||||
|
if(max_size > to_send){
|
||||||
|
max_size = to_send;
|
||||||
|
}
|
||||||
|
// Blocking method, Sending data to ringbuffer, and handle the data in ISR.
|
||||||
|
if(xRingbufferSend(tx_ring_buf, (void*) (buffer+so_far), max_size, tx_timeout_ms / portTICK_PERIOD_MS) != pdTRUE){
|
||||||
|
size = so_far;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
so_far += max_size;
|
||||||
|
to_send -= max_size;
|
||||||
|
// Now trigger the ISR to read data from the ring buffer.
|
||||||
|
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
|
||||||
|
}
|
||||||
|
while(uxItemsWaiting){
|
||||||
|
delay(5);
|
||||||
|
vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting);
|
||||||
|
}
|
||||||
|
xSemaphoreGive(tx_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* READING
|
||||||
|
*/
|
||||||
|
|
||||||
|
size_t HWCDC::setRxBufferSize(size_t rx_queue_len){
|
||||||
|
if(rx_queue){
|
||||||
|
if(!rx_queue_len){
|
||||||
|
vQueueDelete(rx_queue);
|
||||||
|
rx_queue = NULL;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
rx_queue = xQueueCreate(rx_queue_len, sizeof(uint8_t));
|
||||||
|
if(!rx_queue){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(!tx_ring_buf){
|
||||||
|
tx_ring_buf = xRingbufferCreate(rx_queue_len, RINGBUF_TYPE_BYTEBUF);
|
||||||
|
}
|
||||||
|
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_putc1((void (*)(char)) &cdc0_write_char);
|
||||||
|
} else {
|
||||||
|
ets_install_putc1(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if ARDUINO_USB_MODE
|
||||||
|
#if ARDUINO_USB_CDC_ON_BOOT//Serial used for USB CDC
|
||||||
|
HWCDC Serial;
|
||||||
|
#else
|
||||||
|
HWCDC USBSerial;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* CONFIG_TINYUSB_CDC_ENABLED */
|
109
cores/esp32/HWCDC.h
Normal file
109
cores/esp32/HWCDC.h
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
// 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 "sdkconfig.h"
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include "esp_event.h"
|
||||||
|
#include "Stream.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
|
||||||
|
{
|
||||||
|
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 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
|
||||||
|
#if ARDUINO_USB_CDC_ON_BOOT//Serial used for USB CDC
|
||||||
|
extern HWCDC Serial;
|
||||||
|
#else
|
||||||
|
extern HWCDC USBSerial;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* CONFIG_IDF_TARGET_ESP32C3 */
|
552
cores/esp32/HardwareSerial.cpp
Normal file
552
cores/esp32/HardwareSerial.cpp
Normal file
@ -0,0 +1,552 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#include "pins_arduino.h"
|
||||||
|
#include "HardwareSerial.h"
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
|
#include "driver/uart.h"
|
||||||
|
#include "freertos/queue.h"
|
||||||
|
|
||||||
|
#ifndef ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE
|
||||||
|
#define ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE 2048
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ARDUINO_SERIAL_EVENT_TASK_PRIORITY
|
||||||
|
#define ARDUINO_SERIAL_EVENT_TASK_PRIORITY (configMAX_PRIORITIES-1)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE
|
||||||
|
#define ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE -1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SOC_RX0
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
#define SOC_RX0 3
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
#define SOC_RX0 44
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||||
|
#define SOC_RX0 20
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SOC_TX0
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
#define SOC_TX0 1
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
#define SOC_TX0 43
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||||
|
#define SOC_TX0 21
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void serialEvent(void) __attribute__((weak));
|
||||||
|
void serialEvent(void) {}
|
||||||
|
|
||||||
|
#if SOC_UART_NUM > 1
|
||||||
|
|
||||||
|
#ifndef RX1
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
#define RX1 9
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||||
|
#define RX1 18
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||||
|
#define RX1 18
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
#define RX1 15
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TX1
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
#define TX1 10
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||||
|
#define TX1 17
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||||
|
#define TX1 19
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
#define TX1 16
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void serialEvent1(void) __attribute__((weak));
|
||||||
|
void serialEvent1(void) {}
|
||||||
|
#endif /* SOC_UART_NUM > 1 */
|
||||||
|
|
||||||
|
#if SOC_UART_NUM > 2
|
||||||
|
#ifndef RX2
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
#define RX2 16
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
#define RX2 19
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TX2
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
#define TX2 17
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
#define TX2 20
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void serialEvent2(void) __attribute__((weak));
|
||||||
|
void serialEvent2(void) {}
|
||||||
|
#endif /* SOC_UART_NUM > 2 */
|
||||||
|
|
||||||
|
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL)
|
||||||
|
#if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC
|
||||||
|
HardwareSerial Serial0(0);
|
||||||
|
#else
|
||||||
|
HardwareSerial Serial(0);
|
||||||
|
#endif
|
||||||
|
#if SOC_UART_NUM > 1
|
||||||
|
HardwareSerial Serial1(1);
|
||||||
|
#endif
|
||||||
|
#if SOC_UART_NUM > 2
|
||||||
|
HardwareSerial Serial2(2);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void serialEventRun(void)
|
||||||
|
{
|
||||||
|
#if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC
|
||||||
|
if(Serial0.available()) serialEvent();
|
||||||
|
#else
|
||||||
|
if(Serial.available()) serialEvent();
|
||||||
|
#endif
|
||||||
|
#if SOC_UART_NUM > 1
|
||||||
|
if(Serial1.available()) serialEvent1();
|
||||||
|
#endif
|
||||||
|
#if SOC_UART_NUM > 2
|
||||||
|
if(Serial2.available()) serialEvent2();
|
||||||
|
#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(int uart_nr) :
|
||||||
|
_uart_nr(uart_nr),
|
||||||
|
_uart(NULL),
|
||||||
|
_rxBufferSize(256),
|
||||||
|
_txBufferSize(0),
|
||||||
|
_onReceiveCB(NULL),
|
||||||
|
_onReceiveErrorCB(NULL),
|
||||||
|
_onReceiveTimeout(true),
|
||||||
|
_rxTimeout(2),
|
||||||
|
_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();
|
||||||
|
#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;
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// this can be called after Serial.begin(), therefore it shall create the event task
|
||||||
|
if (function != NULL && _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
|
||||||
|
void HardwareSerial::setRxFIFOFull(uint8_t fifoBytes)
|
||||||
|
{
|
||||||
|
HSERIAL_MUTEX_LOCK();
|
||||||
|
uartSetRxFIFOFull(_uart, fifoBytes); // Set new timeout
|
||||||
|
HSERIAL_MUTEX_UNLOCK();
|
||||||
|
}
|
||||||
|
|
||||||
|
// timout is calculates in time to receive UART symbols at the UART baudrate.
|
||||||
|
// the estimation is about 11 bits per symbol (SERIAL_8N1)
|
||||||
|
void 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
|
||||||
|
|
||||||
|
uartSetRxTimeout(_uart, _rxTimeout); // Set new timeout
|
||||||
|
|
||||||
|
HSERIAL_MUTEX_UNLOCK();
|
||||||
|
}
|
||||||
|
|
||||||
|
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, (portTickType)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_w("UART%d RX break.", uart->_uart_nr);
|
||||||
|
currentErr = UART_BREAK_ERROR;
|
||||||
|
break;
|
||||||
|
case UART_PARITY_ERR:
|
||||||
|
log_w("UART%d parity error.", uart->_uart_nr);
|
||||||
|
currentErr = UART_PARITY_ERROR;
|
||||||
|
break;
|
||||||
|
case UART_FRAME_ERR:
|
||||||
|
log_w("UART%d frame error.", uart->_uart_nr);
|
||||||
|
currentErr = UART_FRAME_ERROR;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log_w("UART%d unknown event type %d.", uart->_uart_nr, event.type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (currentErr != UART_NO_ERROR) {
|
||||||
|
if(uart->_onReceiveErrorCB) uart->_onReceiveErrorCB(currentErr);
|
||||||
|
if(uart->_onReceiveCB && uart->available() > 0) uart->_onReceiveCB(); // forces User Callback too
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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(0 > _uart_nr || _uart_nr >= SOC_UART_NUM) {
|
||||||
|
log_e("Serial number is invalid, please use numers 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
|
||||||
|
|
||||||
|
HSERIAL_MUTEX_LOCK();
|
||||||
|
// First Time or after end() --> set default Pins
|
||||||
|
if (!uartIsDriverInstalled(_uart)) {
|
||||||
|
switch (_uart_nr) {
|
||||||
|
case UART_NUM_0:
|
||||||
|
if (rxPin < 0 && txPin < 0) {
|
||||||
|
rxPin = SOC_RX0;
|
||||||
|
txPin = SOC_TX0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#if SOC_UART_NUM > 1 // may save some flash bytes...
|
||||||
|
case UART_NUM_1:
|
||||||
|
if (rxPin < 0 && txPin < 0) {
|
||||||
|
rxPin = RX1;
|
||||||
|
txPin = TX1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
#if SOC_UART_NUM > 2 // may save some flash bytes...
|
||||||
|
case UART_NUM_2:
|
||||||
|
if (rxPin < 0 && txPin < 0) {
|
||||||
|
rxPin = RX2;
|
||||||
|
txPin = TX2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
log_e("Bad UART Number");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_uart) {
|
||||||
|
// in this case it is a begin() over a previous begin() - maybe to change baud rate
|
||||||
|
// thus do not disable debug output
|
||||||
|
end(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDF UART driver keeps Pin setting on restarting. Negative Pin number will keep it unmodified.
|
||||||
|
_uart = uartBegin(_uart_nr, baud ? baud : 9600, config, rxPin, txPin, _rxBufferSize, _txBufferSize, invert, rxfifo_full_thrhd);
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
end(false);
|
||||||
|
|
||||||
|
if(detectedBaudRate) {
|
||||||
|
delay(100); // Give some time...
|
||||||
|
_uart = uartBegin(_uart_nr, detectedBaudRate, config, rxPin, txPin, _rxBufferSize, _txBufferSize, invert, rxfifo_full_thrhd);
|
||||||
|
} 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);
|
||||||
|
|
||||||
|
HSERIAL_MUTEX_UNLOCK();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HardwareSerial::updateBaudRate(unsigned long baud)
|
||||||
|
{
|
||||||
|
uartSetBaudRate(_uart, baud);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HardwareSerial::end(bool fullyTerminate)
|
||||||
|
{
|
||||||
|
// default Serial.end() will completely disable HardwareSerial,
|
||||||
|
// including any tasks or debug message channel (log_x()) - but not for IDF log messages!
|
||||||
|
if(fullyTerminate) {
|
||||||
|
_onReceiveCB = NULL;
|
||||||
|
_onReceiveErrorCB = NULL;
|
||||||
|
if (uartGetDebug() == _uart_nr) {
|
||||||
|
uartSetDebug(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delay(10);
|
||||||
|
uartEnd(_uart);
|
||||||
|
_uart = 0;
|
||||||
|
_destroyEventTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HardwareSerial::setDebugOutput(bool en)
|
||||||
|
{
|
||||||
|
if(_uart == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if(available()) {
|
||||||
|
return uartRead(_uart);
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
size_t avail = available();
|
||||||
|
if (size < avail) {
|
||||||
|
avail = size;
|
||||||
|
}
|
||||||
|
size_t count = 0;
|
||||||
|
while(count < avail) {
|
||||||
|
*buffer++ = uartRead(_uart);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HardwareSerial::setRxInvert(bool invert)
|
||||||
|
{
|
||||||
|
uartSetRxInvert(_uart, invert);
|
||||||
|
}
|
||||||
|
|
||||||
|
// negative Pin value will keep it unmodified
|
||||||
|
void HardwareSerial::setPins(int8_t rxPin, int8_t txPin, int8_t ctsPin, int8_t rtsPin)
|
||||||
|
{
|
||||||
|
if(_uart == NULL) {
|
||||||
|
log_e("setPins() shall be called after begin() - nothing done");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uartSetPins(_uart, rxPin, txPin, ctsPin, rtsPin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enables or disables Hardware Flow Control using RTS and/or CTS pins (must use setAllPins() before)
|
||||||
|
void HardwareSerial::setHwFlowCtrlMode(uint8_t mode, uint8_t threshold)
|
||||||
|
{
|
||||||
|
uartSetHwFlowCtrlMode(_uart, mode, threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t HardwareSerial::setRxBufferSize(size_t new_size) {
|
||||||
|
|
||||||
|
if (_uart) {
|
||||||
|
log_e("RX Buffer can't be resized when Serial is already running.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_size <= SOC_UART_FIFO_LEN) {
|
||||||
|
log_e("RX Buffer must be higher than %d.\n", SOC_UART_FIFO_LEN); // ESP32, S2, S3 and C3 means higher than 128
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_rxBufferSize = new_size;
|
||||||
|
return _rxBufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t HardwareSerial::setTxBufferSize(size_t new_size) {
|
||||||
|
|
||||||
|
if (_uart) {
|
||||||
|
log_e("TX Buffer can't be resized when Serial is already running.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_size <= SOC_UART_FIFO_LEN) {
|
||||||
|
log_e("TX Buffer must be higher than %d.\n", SOC_UART_FIFO_LEN); // ESP32, S2, S3 and C3 means higher than 128
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_txBufferSize = new_size;
|
||||||
|
return _txBufferSize;
|
||||||
|
}
|
208
cores/esp32/HardwareSerial.h
Normal file
208
cores/esp32/HardwareSerial.h
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
/*
|
||||||
|
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 "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
|
|
||||||
|
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 std::function<void(void)> OnReceiveCb;
|
||||||
|
typedef std::function<void(hardwareSerial_error_t)> OnReceiveErrorCb;
|
||||||
|
|
||||||
|
class HardwareSerial: public Stream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HardwareSerial(int 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
|
||||||
|
void 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.
|
||||||
|
void 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 symbos 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 usefull in some use cases
|
||||||
|
void eventQueueReset();
|
||||||
|
|
||||||
|
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 = 112);
|
||||||
|
void end(bool fullyTerminate = true);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
|
||||||
|
void setRxInvert(bool);
|
||||||
|
|
||||||
|
// Negative Pin Number will keep it unmodified, thus this function can set individual pins
|
||||||
|
// SetPins shall be called after Serial begin()
|
||||||
|
void 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)
|
||||||
|
void setHwFlowCtrlMode(uint8_t mode = HW_FLOWCTRL_CTS_RTS, uint8_t threshold = 64); // 64 is half FIFO Length
|
||||||
|
|
||||||
|
size_t setRxBufferSize(size_t new_size);
|
||||||
|
size_t setTxBufferSize(size_t new_size);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int _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;
|
||||||
|
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 for USB CDC
|
||||||
|
#if !ARDUINO_USB_MODE
|
||||||
|
#include "USB.h"
|
||||||
|
#include "USBCDC.h"
|
||||||
|
#endif
|
||||||
|
extern HardwareSerial Serial0;
|
||||||
|
#else
|
||||||
|
extern HardwareSerial Serial;
|
||||||
|
#endif
|
||||||
|
#if SOC_UART_NUM > 1
|
||||||
|
extern HardwareSerial Serial1;
|
||||||
|
#endif
|
||||||
|
#if SOC_UART_NUM > 2
|
||||||
|
extern HardwareSerial Serial2;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // HardwareSerial_h
|
125
cores/esp32/IPAddress.cpp
Normal file
125
cores/esp32/IPAddress.cpp
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
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 <Arduino.h>
|
||||||
|
#include <IPAddress.h>
|
||||||
|
#include <Print.h>
|
||||||
|
|
||||||
|
IPAddress::IPAddress()
|
||||||
|
{
|
||||||
|
_address.dword = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet)
|
||||||
|
{
|
||||||
|
_address.bytes[0] = first_octet;
|
||||||
|
_address.bytes[1] = second_octet;
|
||||||
|
_address.bytes[2] = third_octet;
|
||||||
|
_address.bytes[3] = fourth_octet;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPAddress::IPAddress(uint32_t address)
|
||||||
|
{
|
||||||
|
_address.dword = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPAddress::IPAddress(const uint8_t *address)
|
||||||
|
{
|
||||||
|
memcpy(_address.bytes, address, sizeof(_address.bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
IPAddress& IPAddress::operator=(const uint8_t *address)
|
||||||
|
{
|
||||||
|
memcpy(_address.bytes, address, sizeof(_address.bytes));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPAddress& IPAddress::operator=(uint32_t address)
|
||||||
|
{
|
||||||
|
_address.dword = address;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPAddress::operator==(const uint8_t* addr) const
|
||||||
|
{
|
||||||
|
return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t IPAddress::printTo(Print& p) const
|
||||||
|
{
|
||||||
|
size_t n = 0;
|
||||||
|
for(int i = 0; i < 3; i++) {
|
||||||
|
n += p.print(_address.bytes[i], DEC);
|
||||||
|
n += p.print('.');
|
||||||
|
}
|
||||||
|
n += p.print(_address.bytes[3], DEC);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
String IPAddress::toString() const
|
||||||
|
{
|
||||||
|
char szRet[16];
|
||||||
|
sprintf(szRet,"%u.%u.%u.%u", _address.bytes[0], _address.bytes[1], _address.bytes[2], _address.bytes[3]);
|
||||||
|
return String(szRet);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPAddress::fromString(const char *address)
|
||||||
|
{
|
||||||
|
// TODO: add support for "a", "a.b", "a.b.c" formats
|
||||||
|
|
||||||
|
uint16_t acc = 0; // Accumulator
|
||||||
|
uint8_t dots = 0;
|
||||||
|
|
||||||
|
while (*address)
|
||||||
|
{
|
||||||
|
char c = *address++;
|
||||||
|
if (c >= '0' && c <= '9')
|
||||||
|
{
|
||||||
|
acc = acc * 10 + (c - '0');
|
||||||
|
if (acc > 255) {
|
||||||
|
// Value out of [0..255] range
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (c == '.')
|
||||||
|
{
|
||||||
|
if (dots == 3) {
|
||||||
|
// Too much dots (there must be 3 dots)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_address.bytes[dots++] = acc;
|
||||||
|
acc = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Invalid char
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dots != 3) {
|
||||||
|
// Too few dots (there must be 3 dots)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_address.bytes[3] = acc;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// declared one time - as external in IPAddress.h
|
||||||
|
IPAddress INADDR_NONE(0, 0, 0, 0);
|
96
cores/esp32/IPAddress.h
Normal file
96
cores/esp32/IPAddress.h
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef IPAddress_h
|
||||||
|
#define IPAddress_h
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <WString.h>
|
||||||
|
#include <Printable.h>
|
||||||
|
|
||||||
|
// A class to make it easier to handle and pass around IP addresses
|
||||||
|
|
||||||
|
class IPAddress: public Printable
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
union {
|
||||||
|
uint8_t bytes[4]; // IPv4 address
|
||||||
|
uint32_t dword;
|
||||||
|
} _address;
|
||||||
|
|
||||||
|
// 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 _address.bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Constructors
|
||||||
|
IPAddress();
|
||||||
|
IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
|
||||||
|
IPAddress(uint32_t address);
|
||||||
|
IPAddress(const uint8_t *address);
|
||||||
|
virtual ~IPAddress() {}
|
||||||
|
|
||||||
|
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 pointer
|
||||||
|
// to a four-byte uint8_t array is expected
|
||||||
|
operator uint32_t() const
|
||||||
|
{
|
||||||
|
return _address.dword;
|
||||||
|
}
|
||||||
|
bool operator==(const IPAddress& addr) const
|
||||||
|
{
|
||||||
|
return _address.dword == addr._address.dword;
|
||||||
|
}
|
||||||
|
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
|
||||||
|
{
|
||||||
|
return _address.bytes[index];
|
||||||
|
}
|
||||||
|
uint8_t& operator[](int index)
|
||||||
|
{
|
||||||
|
return _address.bytes[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overloaded copy operators to allow initialisation of IPAddress objects from other types
|
||||||
|
IPAddress& operator=(const uint8_t *address);
|
||||||
|
IPAddress& operator=(uint32_t address);
|
||||||
|
|
||||||
|
virtual size_t printTo(Print& p) const;
|
||||||
|
String toString() const;
|
||||||
|
|
||||||
|
friend class EthernetClass;
|
||||||
|
friend class UDP;
|
||||||
|
friend class Client;
|
||||||
|
friend class Server;
|
||||||
|
friend class DhcpClass;
|
||||||
|
friend class DNSClient;
|
||||||
|
};
|
||||||
|
|
||||||
|
// changed to extern because const declaration creates copies in BSS of INADDR_NONE for each CPP unit that includes it
|
||||||
|
extern IPAddress INADDR_NONE;
|
||||||
|
#endif
|
90
cores/esp32/IPv6Address.cpp
Normal file
90
cores/esp32/IPv6Address.cpp
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
IPv6Address.cpp - Base class that provides IPv6Address
|
||||||
|
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 <Arduino.h>
|
||||||
|
#include <IPv6Address.h>
|
||||||
|
#include <Print.h>
|
||||||
|
|
||||||
|
IPv6Address::IPv6Address()
|
||||||
|
{
|
||||||
|
memset(_address.bytes, 0, sizeof(_address.bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
IPv6Address::IPv6Address(const uint8_t *address)
|
||||||
|
{
|
||||||
|
memcpy(_address.bytes, address, sizeof(_address.bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
IPv6Address::IPv6Address(const uint32_t *address)
|
||||||
|
{
|
||||||
|
memcpy(_address.bytes, (const uint8_t *)address, sizeof(_address.bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
IPv6Address& IPv6Address::operator=(const uint8_t *address)
|
||||||
|
{
|
||||||
|
memcpy(_address.bytes, address, sizeof(_address.bytes));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPv6Address::operator==(const uint8_t* addr) const
|
||||||
|
{
|
||||||
|
return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t IPv6Address::printTo(Print& p) const
|
||||||
|
{
|
||||||
|
size_t n = 0;
|
||||||
|
for(int i = 0; i < 16; i+=2) {
|
||||||
|
if(i){
|
||||||
|
n += p.print(':');
|
||||||
|
}
|
||||||
|
n += p.printf("%02x", _address.bytes[i]);
|
||||||
|
n += p.printf("%02x", _address.bytes[i+1]);
|
||||||
|
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
String IPv6Address::toString() const
|
||||||
|
{
|
||||||
|
char szRet[40];
|
||||||
|
sprintf(szRet,"%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
|
||||||
|
_address.bytes[0], _address.bytes[1], _address.bytes[2], _address.bytes[3],
|
||||||
|
_address.bytes[4], _address.bytes[5], _address.bytes[6], _address.bytes[7],
|
||||||
|
_address.bytes[8], _address.bytes[9], _address.bytes[10], _address.bytes[11],
|
||||||
|
_address.bytes[12], _address.bytes[13], _address.bytes[14], _address.bytes[15]);
|
||||||
|
return String(szRet);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPv6Address::fromString(const char *address)
|
||||||
|
{
|
||||||
|
//format 0011:2233:4455:6677:8899:aabb:ccdd:eeff
|
||||||
|
if(strlen(address) != 39){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
char * pos = (char *)address;
|
||||||
|
size_t i = 0;
|
||||||
|
for(i = 0; i < 16; i+=2) {
|
||||||
|
if(!sscanf(pos, "%2hhx", &_address.bytes[i]) || !sscanf(pos+2, "%2hhx", &_address.bytes[i+1])){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pos += 5;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
94
cores/esp32/IPv6Address.h
Normal file
94
cores/esp32/IPv6Address.h
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
IPv6Address.h - Base class that provides IPv6Address
|
||||||
|
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 IPv6Address_h
|
||||||
|
#define IPv6Address_h
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <WString.h>
|
||||||
|
#include <Printable.h>
|
||||||
|
|
||||||
|
// A class to make it easier to handle and pass around IP addresses
|
||||||
|
|
||||||
|
class IPv6Address: public Printable
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
union {
|
||||||
|
uint8_t bytes[16]; // IPv4 address
|
||||||
|
uint32_t dword[4];
|
||||||
|
} _address;
|
||||||
|
|
||||||
|
// 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 _address.bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Constructors
|
||||||
|
IPv6Address();
|
||||||
|
IPv6Address(const uint8_t *address);
|
||||||
|
IPv6Address(const uint32_t *address);
|
||||||
|
virtual ~IPv6Address() {}
|
||||||
|
|
||||||
|
bool fromString(const char *address);
|
||||||
|
bool fromString(const String &address) { return fromString(address.c_str()); }
|
||||||
|
|
||||||
|
operator const uint8_t*() const
|
||||||
|
{
|
||||||
|
return _address.bytes;
|
||||||
|
}
|
||||||
|
operator const uint32_t*() const
|
||||||
|
{
|
||||||
|
return _address.dword;
|
||||||
|
}
|
||||||
|
bool operator==(const IPv6Address& addr) const
|
||||||
|
{
|
||||||
|
return (_address.dword[0] == addr._address.dword[0])
|
||||||
|
&& (_address.dword[1] == addr._address.dword[1])
|
||||||
|
&& (_address.dword[2] == addr._address.dword[2])
|
||||||
|
&& (_address.dword[3] == addr._address.dword[3]);
|
||||||
|
}
|
||||||
|
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
|
||||||
|
{
|
||||||
|
return _address.bytes[index];
|
||||||
|
}
|
||||||
|
uint8_t& operator[](int index)
|
||||||
|
{
|
||||||
|
return _address.bytes[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overloaded copy operators to allow initialisation of IPv6Address objects from other types
|
||||||
|
IPv6Address& operator=(const uint8_t *address);
|
||||||
|
|
||||||
|
virtual size_t printTo(Print& p) const;
|
||||||
|
String toString() const;
|
||||||
|
|
||||||
|
friend class UDP;
|
||||||
|
friend class Client;
|
||||||
|
friend class Server;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
117
cores/esp32/MD5Builder.cpp
Normal file
117
cores/esp32/MD5Builder.cpp
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
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 <Arduino.h>
|
||||||
|
#include <MD5Builder.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') : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MD5Builder::begin(void)
|
||||||
|
{
|
||||||
|
memset(_buf, 0x00, ESP_ROM_MD5_DIGEST_LEN);
|
||||||
|
esp_rom_md5_init(&_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MD5Builder::add(uint8_t * data, uint16_t len)
|
||||||
|
{
|
||||||
|
esp_rom_md5_update(&_ctx, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MD5Builder::addHexString(const char * data)
|
||||||
|
{
|
||||||
|
uint16_t i, len = strlen(data);
|
||||||
|
uint8_t * tmp = (uint8_t*)malloc(len/2);
|
||||||
|
if(tmp == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for(i=0; i<len; i+=2) {
|
||||||
|
uint8_t high = hex_char_to_byte(data[i]);
|
||||||
|
uint8_t low = hex_char_to_byte(data[i+1]);
|
||||||
|
tmp[i/2] = (high & 0x0F) << 4 | (low & 0x0F);
|
||||||
|
}
|
||||||
|
add(tmp, len/2);
|
||||||
|
free(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
for(uint8_t i = 0; i < ESP_ROM_MD5_DIGEST_LEN; i++) {
|
||||||
|
sprintf(output + (i * 2), "%02x", _buf[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String MD5Builder::toString(void)
|
||||||
|
{
|
||||||
|
char out[(ESP_ROM_MD5_DIGEST_LEN * 2) + 1];
|
||||||
|
getChars(out);
|
||||||
|
return String(out);
|
||||||
|
}
|
65
cores/esp32/MD5Builder.h
Normal file
65
cores/esp32/MD5Builder.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
#ifndef __ESP8266_MD5_BUILDER__
|
||||||
|
#define __ESP8266_MD5_BUILDER__
|
||||||
|
|
||||||
|
#include <WString.h>
|
||||||
|
#include <Stream.h>
|
||||||
|
|
||||||
|
#include "esp_system.h"
|
||||||
|
#include "esp_rom_md5.h"
|
||||||
|
|
||||||
|
class MD5Builder
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
md5_context_t _ctx;
|
||||||
|
uint8_t _buf[ESP_ROM_MD5_DIGEST_LEN];
|
||||||
|
public:
|
||||||
|
void begin(void);
|
||||||
|
void add(uint8_t * data, uint16_t len);
|
||||||
|
void add(const char * data)
|
||||||
|
{
|
||||||
|
add((uint8_t*)data, strlen(data));
|
||||||
|
}
|
||||||
|
void add(char * data)
|
||||||
|
{
|
||||||
|
add((const char*)data);
|
||||||
|
}
|
||||||
|
void add(String data)
|
||||||
|
{
|
||||||
|
add(data.c_str());
|
||||||
|
}
|
||||||
|
void addHexString(const char * data);
|
||||||
|
void addHexString(char * data)
|
||||||
|
{
|
||||||
|
addHexString((const char*)data);
|
||||||
|
}
|
||||||
|
void addHexString(String data)
|
||||||
|
{
|
||||||
|
addHexString(data.c_str());
|
||||||
|
}
|
||||||
|
bool addStream(Stream & stream, const size_t maxLen);
|
||||||
|
void calculate(void);
|
||||||
|
void getBytes(uint8_t * output);
|
||||||
|
void getChars(char * output);
|
||||||
|
String toString(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
374
cores/esp32/Print.cpp
Normal file
374
cores/esp32/Print.cpp
Normal file
@ -0,0 +1,374 @@
|
|||||||
|
/*
|
||||||
|
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::printf(const char *format, ...)
|
||||||
|
{
|
||||||
|
char loc_buf[64];
|
||||||
|
char * temp = loc_buf;
|
||||||
|
va_list arg;
|
||||||
|
va_list copy;
|
||||||
|
va_start(arg, format);
|
||||||
|
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 >= sizeof(loc_buf)){
|
||||||
|
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::print(const __FlashStringHelper *ifsh)
|
||||||
|
{
|
||||||
|
return print(reinterpret_cast<const char *>(ifsh));
|
||||||
|
}
|
||||||
|
|
||||||
|
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::println(const __FlashStringHelper *ifsh)
|
||||||
|
{
|
||||||
|
size_t n = print(ifsh);
|
||||||
|
n += println();
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
116
cores/esp32/Print.h
Normal file
116
cores/esp32/Print.h
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
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 <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 printf(const char * format, ...) __attribute__ ((format (printf, 2, 3)));
|
||||||
|
|
||||||
|
// add availableForWrite to make compatible with Arduino Print.h
|
||||||
|
// default to zero, meaning "a single write may block"
|
||||||
|
// should be overriden by subclasses with buffering
|
||||||
|
virtual int availableForWrite() { return 0; }
|
||||||
|
size_t print(const __FlashStringHelper *);
|
||||||
|
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 *);
|
||||||
|
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
|
41
cores/esp32/Printable.h
Normal file
41
cores/esp32/Printable.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
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
|
||||||
|
|
31
cores/esp32/Server.h
Normal file
31
cores/esp32/Server.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
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(uint16_t port=0) =0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
337
cores/esp32/Stream.cpp
Normal file
337
cores/esp32/Stream.cpp
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
/*
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "Stream.h"
|
||||||
|
#include "esp32-hal.h"
|
||||||
|
|
||||||
|
#define PARSE_TIMEOUT 1000 // default number of milli-seconds to wait
|
||||||
|
#define NO_SKIP_CHAR 1 // a magic char not found in a valid ASCII numeric field
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
while(1) {
|
||||||
|
c = timedPeek();
|
||||||
|
if(c < 0) {
|
||||||
|
return c; // timeout
|
||||||
|
}
|
||||||
|
if(c == '-') {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if(c >= '0' && c <= '9') {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
read(); // discard non-numeric
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public Methods
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void Stream::setTimeout(unsigned long timeout) // sets the maximum number of milliseconds to wait
|
||||||
|
{
|
||||||
|
_timeout = timeout;
|
||||||
|
}
|
||||||
|
unsigned long Stream::getTimeout(void) {
|
||||||
|
return _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 ? true : false;
|
||||||
|
} else {
|
||||||
|
MultiTarget t[2] = {{target, targetLen, 0}, {terminator, termLen, 0}};
|
||||||
|
return findMulti(t, 2) == 0 ? true : false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (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 (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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the first valid (long) integer value from the current position.
|
||||||
|
// initial characters that are not digits (or the minus sign) are skipped
|
||||||
|
// function is terminated by the first character that is not a digit.
|
||||||
|
long Stream::parseInt()
|
||||||
|
{
|
||||||
|
return parseInt(NO_SKIP_CHAR); // terminate on first non-digit character (or timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// as above but a given skipChar is ignored
|
||||||
|
// this allows format characters (typically commas) in values to be ignored
|
||||||
|
long Stream::parseInt(char skipChar)
|
||||||
|
{
|
||||||
|
boolean isNegative = false;
|
||||||
|
long value = 0;
|
||||||
|
int c;
|
||||||
|
|
||||||
|
c = peekNextDigit();
|
||||||
|
// ignore non numeric leading characters
|
||||||
|
if(c < 0) {
|
||||||
|
return 0; // zero returned if timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
if(c == skipChar) {
|
||||||
|
} // ignore this charactor
|
||||||
|
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') || c == skipChar);
|
||||||
|
|
||||||
|
if(isNegative) {
|
||||||
|
value = -value;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// as parseInt but returns a floating point value
|
||||||
|
float Stream::parseFloat()
|
||||||
|
{
|
||||||
|
return parseFloat(NO_SKIP_CHAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// as above but the given skipChar is ignored
|
||||||
|
// this allows format characters (typically commas) in values to be ignored
|
||||||
|
float Stream::parseFloat(char skipChar)
|
||||||
|
{
|
||||||
|
boolean isNegative = false;
|
||||||
|
boolean isFraction = false;
|
||||||
|
long value = 0;
|
||||||
|
int c;
|
||||||
|
float fraction = 1.0;
|
||||||
|
|
||||||
|
c = peekNextDigit();
|
||||||
|
// ignore non numeric leading characters
|
||||||
|
if(c < 0) {
|
||||||
|
return 0; // zero returned if timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
if(c == skipChar) {
|
||||||
|
} // ignore
|
||||||
|
else if(c == '-') {
|
||||||
|
isNegative = true;
|
||||||
|
} else if(c == '.') {
|
||||||
|
isFraction = true;
|
||||||
|
} else if(c >= '0' && c <= '9') { // is c a digit?
|
||||||
|
value = value * 10 + c - '0';
|
||||||
|
if(isFraction) {
|
||||||
|
fraction *= 0.1f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
read(); // consume the character we got with peek
|
||||||
|
c = timedPeek();
|
||||||
|
} while((c >= '0' && c <= '9') || c == '.' || c == skipChar);
|
||||||
|
|
||||||
|
if(isNegative) {
|
||||||
|
value = -value;
|
||||||
|
}
|
||||||
|
if(isFraction) {
|
||||||
|
return value * fraction;
|
||||||
|
} else {
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if(length < 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
size_t index = 0;
|
||||||
|
while(index < length) {
|
||||||
|
int c = timedRead();
|
||||||
|
if(c < 0 || 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 && c != terminator) {
|
||||||
|
ret += (char) c;
|
||||||
|
c = timedRead();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
139
cores/esp32/Stream.h
Normal file
139
cores/esp32/Stream.h
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef Stream_h
|
||||||
|
#define Stream_h
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include "Print.h"
|
||||||
|
|
||||||
|
// compatability macros for testing
|
||||||
|
/*
|
||||||
|
#define getInt() parseInt()
|
||||||
|
#define getInt(skipChar) parseInt(skipchar)
|
||||||
|
#define getFloat() parseFloat()
|
||||||
|
#define getFloat(skipChar) parseFloat(skipChar)
|
||||||
|
#define getString( pre_string, post_string, buffer, length)
|
||||||
|
readBytesBetween( pre_string, terminator, buffer, length)
|
||||||
|
*/
|
||||||
|
|
||||||
|
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(); // 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():_startMillis(0)
|
||||||
|
{
|
||||||
|
_timeout = 1000;
|
||||||
|
}
|
||||||
|
virtual ~Stream() {}
|
||||||
|
|
||||||
|
// parsing methods
|
||||||
|
|
||||||
|
void setTimeout(unsigned long timeout); // sets maximum milliseconds to wait for stream data, default is 1 second
|
||||||
|
unsigned long getTimeout(void);
|
||||||
|
|
||||||
|
bool find(const char *target); // reads data from the stream until the target string is found
|
||||||
|
bool find(uint8_t *target)
|
||||||
|
{
|
||||||
|
return find((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((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((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((char *) target, targetLen, terminate, termLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
long parseInt(); // returns the first valid (long) integer value from the current position.
|
||||||
|
// initial characters that are not digits (or the minus sign) are skipped
|
||||||
|
// integer is terminated by the first character that is not a digit.
|
||||||
|
|
||||||
|
float parseFloat(); // 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 skipChar); // as above but the given skipChar is ignored
|
||||||
|
// as above but the given skipChar is ignored
|
||||||
|
// this allows format characters (typically commas) in values to be ignored
|
||||||
|
|
||||||
|
float parseFloat(char skipChar); // as above but the given skipChar is ignored
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
67
cores/esp32/StreamString.cpp
Normal file
67
cores/esp32/StreamString.cpp
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
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() {
|
||||||
|
}
|
||||||
|
|
39
cores/esp32/StreamString.h
Normal file
39
cores/esp32/StreamString.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
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_
|
||||||
|
|
||||||
|
|
||||||
|
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_ */
|
135
cores/esp32/Tone.cpp
Normal file
135
cores/esp32/Tone.cpp
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
#include "esp32-hal-ledc.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/queue.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
|
|
||||||
|
static TaskHandle_t _tone_task = NULL;
|
||||||
|
static QueueHandle_t _tone_queue = NULL;
|
||||||
|
static uint8_t _channel = 0;
|
||||||
|
|
||||||
|
typedef enum{
|
||||||
|
TONE_START,
|
||||||
|
TONE_END,
|
||||||
|
TONE_SET_CHANNEL
|
||||||
|
} tone_cmd_t;
|
||||||
|
|
||||||
|
typedef struct{
|
||||||
|
tone_cmd_t tone_cmd;
|
||||||
|
uint8_t pin;
|
||||||
|
unsigned int frequency;
|
||||||
|
unsigned long duration;
|
||||||
|
uint8_t channel;
|
||||||
|
} tone_msg_t;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
log_d("Setup LED controll on channel %d", _channel);
|
||||||
|
ledcAttachPin(tone_msg.pin, _channel);
|
||||||
|
ledcWriteTone(_channel, tone_msg.frequency);
|
||||||
|
|
||||||
|
if(tone_msg.duration){
|
||||||
|
delay(tone_msg.duration);
|
||||||
|
ledcDetachPin(tone_msg.pin);
|
||||||
|
ledcWriteTone(_channel, 0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TONE_END:
|
||||||
|
log_d("Task received from queue TONE_END: pin=%d", tone_msg.pin);
|
||||||
|
ledcDetachPin(tone_msg.pin);
|
||||||
|
ledcWriteTone(_channel, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TONE_SET_CHANNEL:
|
||||||
|
log_d("Task received from queue TONE_SET_CHANNEL: channel=%d", tone_msg.channel);
|
||||||
|
_channel = tone_msg.channel;
|
||||||
|
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
|
||||||
|
1, // Priority of the 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 setToneChannel(uint8_t channel){
|
||||||
|
log_d("channel=%d", channel);
|
||||||
|
if(tone_init()){
|
||||||
|
tone_msg_t tone_msg = {
|
||||||
|
.tone_cmd = TONE_SET_CHANNEL,
|
||||||
|
.pin = 0, // Ignored
|
||||||
|
.frequency = 0, // Ignored
|
||||||
|
.duration = 0, // Ignored
|
||||||
|
.channel = channel
|
||||||
|
};
|
||||||
|
xQueueSend(_tone_queue, &tone_msg, portMAX_DELAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void noTone(uint8_t _pin){
|
||||||
|
log_d("noTone was called");
|
||||||
|
if(tone_init()){
|
||||||
|
tone_msg_t tone_msg = {
|
||||||
|
.tone_cmd = TONE_END,
|
||||||
|
.pin = _pin,
|
||||||
|
.frequency = 0, // Ignored
|
||||||
|
.duration = 0, // Ignored
|
||||||
|
.channel = 0 // Ignored
|
||||||
|
};
|
||||||
|
xQueueSend(_tone_queue, &tone_msg, portMAX_DELAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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(tone_init()){
|
||||||
|
tone_msg_t tone_msg = {
|
||||||
|
.tone_cmd = TONE_START,
|
||||||
|
.pin = _pin,
|
||||||
|
.frequency = frequency,
|
||||||
|
.duration = duration,
|
||||||
|
.channel = 0 // Ignored
|
||||||
|
};
|
||||||
|
xQueueSend(_tone_queue, &tone_msg, portMAX_DELAY);
|
||||||
|
}
|
||||||
|
}
|
357
cores/esp32/USB.cpp
Normal file
357
cores/esp32/USB.cpp
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
// 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 CONFIG_TINYUSB_ENABLED
|
||||||
|
|
||||||
|
#include "pins_arduino.h"
|
||||||
|
#include "esp32-hal.h"
|
||||||
|
#include "esp32-hal-tinyusb.h"
|
||||||
|
#include "common/tusb_common.h"
|
||||||
|
#include "StreamString.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://espressif.github.io/arduino-esp32/webusb.html"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 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;
|
||||||
|
}
|
||||||
|
// 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;
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
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_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_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 */
|
119
cores/esp32/USB.h
Normal file
119
cores/esp32/USB.h
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
// 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 "sdkconfig.h"
|
||||||
|
|
||||||
|
#if CONFIG_TINYUSB_ENABLED
|
||||||
|
|
||||||
|
#include "esp_event.h"
|
||||||
|
#include "USBCDC.h"
|
||||||
|
|
||||||
|
#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=2048, uint8_t event_task_priority=5);
|
||||||
|
~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 */
|
453
cores/esp32/USBCDC.cpp
Normal file
453
cores/esp32/USBCDC.cpp
Normal file
@ -0,0 +1,453 @@
|
|||||||
|
// 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 CONFIG_TINYUSB_CDC_ENABLED
|
||||||
|
|
||||||
|
#include "USBCDC.h"
|
||||||
|
#include "esp32-hal-tinyusb.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);
|
||||||
|
|
||||||
|
#define MAX_USB_CDC_DEVICES 2
|
||||||
|
USBCDC * devices[MAX_USB_CDC_DEVICES] = {NULL, NULL};
|
||||||
|
|
||||||
|
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, 64, 0x03, 0x84, 64)
|
||||||
|
};
|
||||||
|
*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)
|
||||||
|
{
|
||||||
|
if(itf < MAX_USB_CDC_DEVICES && 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)
|
||||||
|
{
|
||||||
|
if(itf < MAX_USB_CDC_DEVICES && 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)
|
||||||
|
{
|
||||||
|
if(itf < MAX_USB_CDC_DEVICES && 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 < MAX_USB_CDC_DEVICES && devices[itf] != NULL){
|
||||||
|
devices[itf]->_onTX();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ARDUINO_ISR_ATTR cdc0_write_char(char c){
|
||||||
|
if(devices[0] != NULL){
|
||||||
|
devices[0]->write(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)
|
||||||
|
{
|
||||||
|
tinyusb_enable_interface(USB_INTERFACE_CDC, TUD_CDC_DESC_LEN, load_cdc_descriptor);
|
||||||
|
if(itf < MAX_USB_CDC_DEVICES){
|
||||||
|
arduino_usb_event_handler_register_with(ARDUINO_USB_EVENTS, ARDUINO_USB_STOPPED_EVENT, usb_unplugged_cb, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
xQueueHandle 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(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()
|
||||||
|
{
|
||||||
|
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 {
|
||||||
|
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 {
|
||||||
|
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 >= MAX_USB_CDC_DEVICES || rx_queue == NULL){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return uxQueueMessagesWaiting(rx_queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
int USBCDC::peek(void)
|
||||||
|
{
|
||||||
|
if(itf >= MAX_USB_CDC_DEVICES || rx_queue == NULL){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
uint8_t c;
|
||||||
|
if(xQueuePeek(rx_queue, &c, 0)) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int USBCDC::read(void)
|
||||||
|
{
|
||||||
|
if(itf >= MAX_USB_CDC_DEVICES || 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 >= MAX_USB_CDC_DEVICES || 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 >= MAX_USB_CDC_DEVICES || 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 >= MAX_USB_CDC_DEVICES || 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 >= MAX_USB_CDC_DEVICES || 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;
|
||||||
|
while(to_send){
|
||||||
|
if(!tud_cdc_n_connected(itf)){
|
||||||
|
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(en) {
|
||||||
|
uartSetDebug(NULL);
|
||||||
|
ets_install_putc1((void (*)(char)) &cdc0_write_char);
|
||||||
|
} else {
|
||||||
|
ets_install_putc1(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
USBCDC::operator bool() const
|
||||||
|
{
|
||||||
|
if(itf >= MAX_USB_CDC_DEVICES){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if ARDUINO_USB_CDC_ON_BOOT && !ARDUINO_USB_MODE //Serial used for USB CDC
|
||||||
|
USBCDC Serial(0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* CONFIG_TINYUSB_CDC_ENABLED */
|
145
cores/esp32/USBCDC.h
Normal file
145
cores/esp32/USBCDC.h
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
// 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 "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;
|
||||||
|
xQueueHandle rx_queue;
|
||||||
|
xSemaphoreHandle tx_lock;
|
||||||
|
uint32_t tx_timeout_ms;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#if ARDUINO_USB_CDC_ON_BOOT && !ARDUINO_USB_MODE //Serial used for USB CDC
|
||||||
|
extern USBCDC Serial;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* CONFIG_TINYUSB_CDC_ENABLED */
|
260
cores/esp32/USBMSC.cpp
Normal file
260
cores/esp32/USBMSC.cpp
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
// 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 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), 64)
|
||||||
|
};
|
||||||
|
*itf+=1;
|
||||||
|
memcpy(dst, descriptor, TUD_MSC_DESC_LEN);
|
||||||
|
return TUD_MSC_DESC_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool media_present;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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].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].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::mediaPresent(bool media_present){
|
||||||
|
msc_luns[_lun].media_present = media_present;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_TINYUSB_MSC_ENABLED */
|
51
cores/esp32/USBMSC.h
Normal file
51
cores/esp32/USBMSC.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// 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 <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 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 */
|
93
cores/esp32/Udp.h
Normal file
93
cores/esp32/Udp.h
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
154
cores/esp32/WCharacter.h
Normal file
154
cores/esp32/WCharacter.h
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
/*
|
||||||
|
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
|
88
cores/esp32/WMath.cpp
Normal file
88
cores/esp32/WMath.cpp
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
/* -*- 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"
|
||||||
|
|
||||||
|
void randomSeed(unsigned long seed)
|
||||||
|
{
|
||||||
|
if(seed != 0) {
|
||||||
|
srand(seed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long random(long howbig)
|
||||||
|
{
|
||||||
|
uint32_t x = esp_random();
|
||||||
|
uint64_t m = uint64_t(x) * uint64_t(howbig);
|
||||||
|
uint32_t l = uint32_t(m);
|
||||||
|
if (l < howbig) {
|
||||||
|
uint32_t t = -howbig;
|
||||||
|
if (t >= howbig) {
|
||||||
|
t -= howbig;
|
||||||
|
if (t >= howbig)
|
||||||
|
t %= howbig;
|
||||||
|
}
|
||||||
|
while (l < t) {
|
||||||
|
x = esp_random();
|
||||||
|
m = uint64_t(x) * uint64_t(howbig);
|
||||||
|
l = uint32_t(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m >> 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
918
cores/esp32/WString.cpp
Normal file
918
cores/esp32/WString.cpp
Normal file
@ -0,0 +1,918 @@
|
|||||||
|
/*
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
String::String(const __FlashStringHelper *pstr) {
|
||||||
|
init();
|
||||||
|
*this = pstr; // see operator =
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __GXX_EXPERIMENTAL_CXX0X__
|
||||||
|
String::String(String &&rval) {
|
||||||
|
init();
|
||||||
|
move(rval);
|
||||||
|
}
|
||||||
|
|
||||||
|
String::String(StringSumHelper &&rval) {
|
||||||
|
init();
|
||||||
|
move(rval);
|
||||||
|
}
|
||||||
|
#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)];
|
||||||
|
if (base == 10) {
|
||||||
|
sprintf(buf, "%d", value);
|
||||||
|
} else {
|
||||||
|
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)];
|
||||||
|
if (base==10) {
|
||||||
|
sprintf(buf, "%ld", value);
|
||||||
|
} else {
|
||||||
|
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 enought 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 enought memory for the operation.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String::String(long long value, unsigned char base) {
|
||||||
|
init();
|
||||||
|
char buf[2 + 8 * sizeof(long long)];
|
||||||
|
if (base==10) {
|
||||||
|
sprintf(buf, "%lld", value); // NOT SURE - NewLib Nano ... does it support %lld?
|
||||||
|
} else {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::reserve(unsigned int size) {
|
||||||
|
if(buffer() && capacity() >= size)
|
||||||
|
return 1;
|
||||||
|
if(changeBuffer(size)) {
|
||||||
|
if(len() == 0)
|
||||||
|
wbuffer()[0] = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char 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
|
||||||
|
uint16_t oldLen = len();
|
||||||
|
setSSO(true);
|
||||||
|
setLen(oldLen);
|
||||||
|
return 1;
|
||||||
|
} else { // if bufptr && !isSSO()
|
||||||
|
// Using bufptr, need to shrink into sso.buff
|
||||||
|
char temp[sizeof(sso.buff)];
|
||||||
|
memcpy(temp, buffer(), maxStrLen);
|
||||||
|
free(wbuffer());
|
||||||
|
uint16_t oldLen = len();
|
||||||
|
setSSO(true);
|
||||||
|
memcpy(wbuffer(), temp, maxStrLen);
|
||||||
|
setLen(oldLen);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
uint16_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 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// /*********************************************/
|
||||||
|
// /* Copy and Move */
|
||||||
|
// /*********************************************/
|
||||||
|
|
||||||
|
String & String::copy(const char *cstr, unsigned int length) {
|
||||||
|
if(!reserve(length)) {
|
||||||
|
invalidate();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
memmove(wbuffer(), cstr, length + 1);
|
||||||
|
setLen(length);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String & String::copy(const __FlashStringHelper *pstr, unsigned int length) {
|
||||||
|
if (!reserve(length)) {
|
||||||
|
invalidate();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
memcpy_P(wbuffer(), (PGM_P)pstr, length + 1); // We know wbuffer() cannot ever be in PROGMEM, so memcpy safe here
|
||||||
|
setLen(length);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __GXX_EXPERIMENTAL_CXX0X__
|
||||||
|
void String::move(String &rhs) {
|
||||||
|
if(buffer()) {
|
||||||
|
if(capacity() >= rhs.len()) {
|
||||||
|
memmove(wbuffer(), rhs.buffer(), rhs.length() + 1);
|
||||||
|
setLen(rhs.len());
|
||||||
|
rhs.invalidate();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
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.setSSO(false);
|
||||||
|
rhs.setCapacity(0);
|
||||||
|
rhs.setBuffer(nullptr);
|
||||||
|
rhs.setLen(0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
String & String::operator =(const String &rhs) {
|
||||||
|
if(this == &rhs)
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
if(rhs.buffer())
|
||||||
|
copy(rhs.buffer(), rhs.len());
|
||||||
|
else
|
||||||
|
invalidate();
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#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) {
|
||||||
|
if(cstr)
|
||||||
|
copy(cstr, strlen(cstr));
|
||||||
|
else
|
||||||
|
invalidate();
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String & String::operator =(const __FlashStringHelper *pstr) {
|
||||||
|
if(pstr)
|
||||||
|
copy(pstr, strlen_P((PGM_P)pstr));
|
||||||
|
else
|
||||||
|
invalidate();
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// /*********************************************/
|
||||||
|
// /* concat */
|
||||||
|
// /*********************************************/
|
||||||
|
|
||||||
|
unsigned char 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) {
|
||||||
|
unsigned int newlen = 2 * len();
|
||||||
|
if (!s.buffer())
|
||||||
|
return 0;
|
||||||
|
if (s.len() == 0)
|
||||||
|
return 1;
|
||||||
|
if (!reserve(newlen))
|
||||||
|
return 0;
|
||||||
|
memmove(wbuffer() + len(), buffer(), len());
|
||||||
|
setLen(newlen);
|
||||||
|
wbuffer()[len()] = 0;
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return concat(s.buffer(), s.len());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::concat(const char *cstr, unsigned int length) {
|
||||||
|
unsigned int newlen = len() + length;
|
||||||
|
if(!cstr)
|
||||||
|
return 0;
|
||||||
|
if(length == 0)
|
||||||
|
return 1;
|
||||||
|
if(!reserve(newlen))
|
||||||
|
return 0;
|
||||||
|
if (cstr >= wbuffer() && cstr < wbuffer() + len())
|
||||||
|
// compatible with SSO in ram #6155 (case "x += x.c_str()")
|
||||||
|
memmove(wbuffer() + len(), cstr, length + 1);
|
||||||
|
else
|
||||||
|
// compatible with source in flash #6367
|
||||||
|
memcpy_P(wbuffer() + len(), cstr, length + 1);
|
||||||
|
setLen(newlen);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::concat(const char *cstr) {
|
||||||
|
if(!cstr)
|
||||||
|
return 0;
|
||||||
|
return concat(cstr, strlen(cstr));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::concat(char c) {
|
||||||
|
char buf[] = { c, '\0' };
|
||||||
|
return concat(buf, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::concat(unsigned char num) {
|
||||||
|
char buf[1 + 3 * sizeof(unsigned char)];
|
||||||
|
return concat(buf, sprintf(buf, "%d", num));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::concat(int num) {
|
||||||
|
char buf[2 + 3 * sizeof(int)];
|
||||||
|
return concat(buf, sprintf(buf, "%d", num));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::concat(unsigned int num) {
|
||||||
|
char buf[1 + 3 * sizeof(unsigned int)];
|
||||||
|
utoa(num, buf, 10);
|
||||||
|
return concat(buf, strlen(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::concat(long num) {
|
||||||
|
char buf[2 + 3 * sizeof(long)];
|
||||||
|
return concat(buf, sprintf(buf, "%ld", num));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::concat(unsigned long num) {
|
||||||
|
char buf[1 + 3 * sizeof(unsigned long)];
|
||||||
|
ultoa(num, buf, 10);
|
||||||
|
return concat(buf, strlen(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::concat(float num) {
|
||||||
|
char buf[20];
|
||||||
|
char* string = dtostrf(num, 4, 2, buf);
|
||||||
|
return concat(string, strlen(string));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::concat(double num) {
|
||||||
|
char buf[20];
|
||||||
|
char* string = dtostrf(num, 4, 2, buf);
|
||||||
|
return concat(string, strlen(string));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::concat(long long num) {
|
||||||
|
char buf[2 + 3 * sizeof(long long)];
|
||||||
|
return concat(buf, sprintf(buf, "%lld", num)); // NOT SURE - NewLib Nano ... does it support %lld?
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::concat(unsigned long long num) {
|
||||||
|
char buf[1 + 3 * sizeof(unsigned long long)];
|
||||||
|
ulltoa(num, buf, 10);
|
||||||
|
return concat(buf, strlen(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::concat(const __FlashStringHelper * str) {
|
||||||
|
if (!str) return 0;
|
||||||
|
int length = strlen_P((PGM_P)str);
|
||||||
|
if (length == 0) return 1;
|
||||||
|
unsigned int newlen = len() + length;
|
||||||
|
if (!reserve(newlen)) return 0;
|
||||||
|
memcpy_P(wbuffer() + len(), (PGM_P)str, length + 1);
|
||||||
|
setLen(newlen);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************/
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringSumHelper & operator + (const StringSumHelper &lhs, const __FlashStringHelper *rhs)
|
||||||
|
{
|
||||||
|
StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
|
||||||
|
if (!a.concat(rhs))
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::equals(const String &s2) const {
|
||||||
|
return (len() == s2.len() && compareTo(s2) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::operator<(const String &rhs) const {
|
||||||
|
return compareTo(rhs) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::operator>(const String &rhs) const {
|
||||||
|
return compareTo(rhs) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::operator<=(const String &rhs) const {
|
||||||
|
return compareTo(rhs) <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::operator>=(const String &rhs) const {
|
||||||
|
return compareTo(rhs) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::equalsIgnoreCase(const String &s2) const {
|
||||||
|
if(this == &s2)
|
||||||
|
return 1;
|
||||||
|
if(len() != s2.len())
|
||||||
|
return 0;
|
||||||
|
if(len() == 0)
|
||||||
|
return 1;
|
||||||
|
const char *p1 = buffer();
|
||||||
|
const char *p2 = s2.buffer();
|
||||||
|
while(*p1) {
|
||||||
|
if(tolower(*p1++) != tolower(*p2++))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::startsWith(const String &s2) const {
|
||||||
|
if(len() < s2.len())
|
||||||
|
return 0;
|
||||||
|
return startsWith(s2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::startsWith(const String &s2, unsigned int offset) const {
|
||||||
|
if(offset > (unsigned)(len() - s2.len()) || !buffer() || !s2.buffer())
|
||||||
|
return 0;
|
||||||
|
return strncmp(&buffer()[offset], s2.buffer(), s2.len()) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char String::endsWith(const String &s2) const {
|
||||||
|
if(len() < s2.len() || !buffer() || !s2.buffer())
|
||||||
|
return 0;
|
||||||
|
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;
|
||||||
|
return temp - buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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;
|
401
cores/esp32/WString.h
Normal file
401
cores/esp32/WString.h
Normal file
@ -0,0 +1,401 @@
|
|||||||
|
/*
|
||||||
|
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 <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <pgmspace.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// An inherited class for holding the result of a concatenation. These
|
||||||
|
// result objects are assumed to be writable by subsequent concatenations.
|
||||||
|
class StringSumHelper;
|
||||||
|
|
||||||
|
// an abstract class used as a means to proide a unique pointer type
|
||||||
|
// but really has no body
|
||||||
|
class __FlashStringHelper;
|
||||||
|
#define FPSTR(pstr_pointer) (reinterpret_cast<const __FlashStringHelper *>(pstr_pointer))
|
||||||
|
#define F(string_literal) (FPSTR(PSTR(string_literal)))
|
||||||
|
|
||||||
|
// 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((const char*)cstr, length) {}
|
||||||
|
#endif
|
||||||
|
String(const String &str);
|
||||||
|
String(const __FlashStringHelper *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)
|
||||||
|
unsigned char 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);
|
||||||
|
#ifdef __GXX_EXPERIMENTAL_CXX0X__
|
||||||
|
String & operator =(String &&rval);
|
||||||
|
String & operator =(StringSumHelper &&rval);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// concatenate (works w/ built-in types)
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
unsigned char concat(const String &str);
|
||||||
|
unsigned char concat(const char *cstr);
|
||||||
|
unsigned char concat(const char *cstr, unsigned int length);
|
||||||
|
unsigned char concat(const uint8_t *cstr, unsigned int length) {return concat((const char*)cstr, length);}
|
||||||
|
unsigned char concat(char c);
|
||||||
|
unsigned char concat(unsigned char c);
|
||||||
|
unsigned char concat(int num);
|
||||||
|
unsigned char concat(unsigned int num);
|
||||||
|
unsigned char concat(long num);
|
||||||
|
unsigned char concat(unsigned long num);
|
||||||
|
unsigned char concat(float num);
|
||||||
|
unsigned char concat(double num);
|
||||||
|
unsigned char concat(long long num);
|
||||||
|
unsigned char concat(unsigned long long num);
|
||||||
|
unsigned char concat(const __FlashStringHelper * str);
|
||||||
|
|
||||||
|
// if there's not enough memory for the concatenated value, the string
|
||||||
|
// will be left unchanged (but this isn't signalled 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){
|
||||||
|
concat(str);
|
||||||
|
return (*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
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, const __FlashStringHelper *rhs);
|
||||||
|
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;
|
||||||
|
unsigned char equals(const String &s) const;
|
||||||
|
unsigned char equals(const char *cstr) const;
|
||||||
|
unsigned char operator ==(const String &rhs) const {
|
||||||
|
return equals(rhs);
|
||||||
|
}
|
||||||
|
unsigned char operator ==(const char *cstr) const {
|
||||||
|
return equals(cstr);
|
||||||
|
}
|
||||||
|
unsigned char operator !=(const String &rhs) const {
|
||||||
|
return !equals(rhs);
|
||||||
|
}
|
||||||
|
unsigned char operator !=(const char *cstr) const {
|
||||||
|
return !equals(cstr);
|
||||||
|
}
|
||||||
|
unsigned char operator <(const String &rhs) const;
|
||||||
|
unsigned char operator >(const String &rhs) const;
|
||||||
|
unsigned char operator <=(const String &rhs) const;
|
||||||
|
unsigned char operator >=(const String &rhs) const;
|
||||||
|
unsigned char equalsIgnoreCase(const String &s) const;
|
||||||
|
unsigned char equalsConstantTime(const String &s) const;
|
||||||
|
unsigned char startsWith(const String &prefix) const;
|
||||||
|
unsigned char startsWith(const char *prefix) const {
|
||||||
|
return this->startsWith(String(prefix));
|
||||||
|
}
|
||||||
|
unsigned char startsWith(const __FlashStringHelper *prefix) const {
|
||||||
|
return this->startsWith(String(prefix));
|
||||||
|
}
|
||||||
|
unsigned char startsWith(const String &prefix, unsigned int offset) const;
|
||||||
|
unsigned char endsWith(const String &suffix) const;
|
||||||
|
unsigned char endsWith(const char *suffix) const {
|
||||||
|
return this->endsWith(String(suffix));
|
||||||
|
}
|
||||||
|
unsigned char endsWith(const __FlashStringHelper * suffix) const {
|
||||||
|
return this->endsWith(String(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(String(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(String(find), String(replace));
|
||||||
|
}
|
||||||
|
void replace(const __FlashStringHelper *find, const __FlashStringHelper *replace) {
|
||||||
|
this->replace(String(find), String(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 (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);
|
||||||
|
unsigned char changeBuffer(unsigned int maxStrLen);
|
||||||
|
|
||||||
|
// copy and move
|
||||||
|
String & copy(const char *cstr, unsigned int length);
|
||||||
|
String & copy(const __FlashStringHelper *pstr, unsigned int 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) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const String emptyString;
|
||||||
|
|
||||||
|
#endif // __cplusplus
|
||||||
|
#endif // String_class_h
|
1
cores/esp32/apps/sntp/sntp.h
Normal file
1
cores/esp32/apps/sntp/sntp.h
Normal file
@ -0,0 +1 @@
|
|||||||
|
#include "lwip/apps/sntp.h"
|
64
cores/esp32/base64.cpp
Normal file
64
cores/esp32/base64.cpp
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* 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());
|
||||||
|
}
|
||||||
|
|
13
cores/esp32/base64.h
Normal file
13
cores/esp32/base64.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#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_ */
|
534
cores/esp32/binary.h
Normal file
534
cores/esp32/binary.h
Normal file
@ -0,0 +1,534 @@
|
|||||||
|
/*
|
||||||
|
binary.h - Definitions for binary constants
|
||||||
|
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
|
||||||
|
|
||||||
|
#define B0 0
|
||||||
|
#define B00 0
|
||||||
|
#define B000 0
|
||||||
|
#define B0000 0
|
||||||
|
#define B00000 0
|
||||||
|
#define B000000 0
|
||||||
|
#define B0000000 0
|
||||||
|
#define B00000000 0
|
||||||
|
#define B1 1
|
||||||
|
#define B01 1
|
||||||
|
#define B001 1
|
||||||
|
#define B0001 1
|
||||||
|
#define B00001 1
|
||||||
|
#define B000001 1
|
||||||
|
#define B0000001 1
|
||||||
|
#define B00000001 1
|
||||||
|
#define B10 2
|
||||||
|
#define B010 2
|
||||||
|
#define B0010 2
|
||||||
|
#define B00010 2
|
||||||
|
#define B000010 2
|
||||||
|
#define B0000010 2
|
||||||
|
#define B00000010 2
|
||||||
|
#define B11 3
|
||||||
|
#define B011 3
|
||||||
|
#define B0011 3
|
||||||
|
#define B00011 3
|
||||||
|
#define B000011 3
|
||||||
|
#define B0000011 3
|
||||||
|
#define B00000011 3
|
||||||
|
#define B100 4
|
||||||
|
#define B0100 4
|
||||||
|
#define B00100 4
|
||||||
|
#define B000100 4
|
||||||
|
#define B0000100 4
|
||||||
|
#define B00000100 4
|
||||||
|
#define B101 5
|
||||||
|
#define B0101 5
|
||||||
|
#define B00101 5
|
||||||
|
#define B000101 5
|
||||||
|
#define B0000101 5
|
||||||
|
#define B00000101 5
|
||||||
|
#define B110 6
|
||||||
|
#define B0110 6
|
||||||
|
#define B00110 6
|
||||||
|
#define B000110 6
|
||||||
|
#define B0000110 6
|
||||||
|
#define B00000110 6
|
||||||
|
#define B111 7
|
||||||
|
#define B0111 7
|
||||||
|
#define B00111 7
|
||||||
|
#define B000111 7
|
||||||
|
#define B0000111 7
|
||||||
|
#define B00000111 7
|
||||||
|
#define B1000 8
|
||||||
|
#define B01000 8
|
||||||
|
#define B001000 8
|
||||||
|
#define B0001000 8
|
||||||
|
#define B00001000 8
|
||||||
|
#define B1001 9
|
||||||
|
#define B01001 9
|
||||||
|
#define B001001 9
|
||||||
|
#define B0001001 9
|
||||||
|
#define B00001001 9
|
||||||
|
#define B1010 10
|
||||||
|
#define B01010 10
|
||||||
|
#define B001010 10
|
||||||
|
#define B0001010 10
|
||||||
|
#define B00001010 10
|
||||||
|
#define B1011 11
|
||||||
|
#define B01011 11
|
||||||
|
#define B001011 11
|
||||||
|
#define B0001011 11
|
||||||
|
#define B00001011 11
|
||||||
|
#define B1100 12
|
||||||
|
#define B01100 12
|
||||||
|
#define B001100 12
|
||||||
|
#define B0001100 12
|
||||||
|
#define B00001100 12
|
||||||
|
#define B1101 13
|
||||||
|
#define B01101 13
|
||||||
|
#define B001101 13
|
||||||
|
#define B0001101 13
|
||||||
|
#define B00001101 13
|
||||||
|
#define B1110 14
|
||||||
|
#define B01110 14
|
||||||
|
#define B001110 14
|
||||||
|
#define B0001110 14
|
||||||
|
#define B00001110 14
|
||||||
|
#define B1111 15
|
||||||
|
#define B01111 15
|
||||||
|
#define B001111 15
|
||||||
|
#define B0001111 15
|
||||||
|
#define B00001111 15
|
||||||
|
#define B10000 16
|
||||||
|
#define B010000 16
|
||||||
|
#define B0010000 16
|
||||||
|
#define B00010000 16
|
||||||
|
#define B10001 17
|
||||||
|
#define B010001 17
|
||||||
|
#define B0010001 17
|
||||||
|
#define B00010001 17
|
||||||
|
#define B10010 18
|
||||||
|
#define B010010 18
|
||||||
|
#define B0010010 18
|
||||||
|
#define B00010010 18
|
||||||
|
#define B10011 19
|
||||||
|
#define B010011 19
|
||||||
|
#define B0010011 19
|
||||||
|
#define B00010011 19
|
||||||
|
#define B10100 20
|
||||||
|
#define B010100 20
|
||||||
|
#define B0010100 20
|
||||||
|
#define B00010100 20
|
||||||
|
#define B10101 21
|
||||||
|
#define B010101 21
|
||||||
|
#define B0010101 21
|
||||||
|
#define B00010101 21
|
||||||
|
#define B10110 22
|
||||||
|
#define B010110 22
|
||||||
|
#define B0010110 22
|
||||||
|
#define B00010110 22
|
||||||
|
#define B10111 23
|
||||||
|
#define B010111 23
|
||||||
|
#define B0010111 23
|
||||||
|
#define B00010111 23
|
||||||
|
#define B11000 24
|
||||||
|
#define B011000 24
|
||||||
|
#define B0011000 24
|
||||||
|
#define B00011000 24
|
||||||
|
#define B11001 25
|
||||||
|
#define B011001 25
|
||||||
|
#define B0011001 25
|
||||||
|
#define B00011001 25
|
||||||
|
#define B11010 26
|
||||||
|
#define B011010 26
|
||||||
|
#define B0011010 26
|
||||||
|
#define B00011010 26
|
||||||
|
#define B11011 27
|
||||||
|
#define B011011 27
|
||||||
|
#define B0011011 27
|
||||||
|
#define B00011011 27
|
||||||
|
#define B11100 28
|
||||||
|
#define B011100 28
|
||||||
|
#define B0011100 28
|
||||||
|
#define B00011100 28
|
||||||
|
#define B11101 29
|
||||||
|
#define B011101 29
|
||||||
|
#define B0011101 29
|
||||||
|
#define B00011101 29
|
||||||
|
#define B11110 30
|
||||||
|
#define B011110 30
|
||||||
|
#define B0011110 30
|
||||||
|
#define B00011110 30
|
||||||
|
#define B11111 31
|
||||||
|
#define B011111 31
|
||||||
|
#define B0011111 31
|
||||||
|
#define B00011111 31
|
||||||
|
#define B100000 32
|
||||||
|
#define B0100000 32
|
||||||
|
#define B00100000 32
|
||||||
|
#define B100001 33
|
||||||
|
#define B0100001 33
|
||||||
|
#define B00100001 33
|
||||||
|
#define B100010 34
|
||||||
|
#define B0100010 34
|
||||||
|
#define B00100010 34
|
||||||
|
#define B100011 35
|
||||||
|
#define B0100011 35
|
||||||
|
#define B00100011 35
|
||||||
|
#define B100100 36
|
||||||
|
#define B0100100 36
|
||||||
|
#define B00100100 36
|
||||||
|
#define B100101 37
|
||||||
|
#define B0100101 37
|
||||||
|
#define B00100101 37
|
||||||
|
#define B100110 38
|
||||||
|
#define B0100110 38
|
||||||
|
#define B00100110 38
|
||||||
|
#define B100111 39
|
||||||
|
#define B0100111 39
|
||||||
|
#define B00100111 39
|
||||||
|
#define B101000 40
|
||||||
|
#define B0101000 40
|
||||||
|
#define B00101000 40
|
||||||
|
#define B101001 41
|
||||||
|
#define B0101001 41
|
||||||
|
#define B00101001 41
|
||||||
|
#define B101010 42
|
||||||
|
#define B0101010 42
|
||||||
|
#define B00101010 42
|
||||||
|
#define B101011 43
|
||||||
|
#define B0101011 43
|
||||||
|
#define B00101011 43
|
||||||
|
#define B101100 44
|
||||||
|
#define B0101100 44
|
||||||
|
#define B00101100 44
|
||||||
|
#define B101101 45
|
||||||
|
#define B0101101 45
|
||||||
|
#define B00101101 45
|
||||||
|
#define B101110 46
|
||||||
|
#define B0101110 46
|
||||||
|
#define B00101110 46
|
||||||
|
#define B101111 47
|
||||||
|
#define B0101111 47
|
||||||
|
#define B00101111 47
|
||||||
|
#define B110000 48
|
||||||
|
#define B0110000 48
|
||||||
|
#define B00110000 48
|
||||||
|
#define B110001 49
|
||||||
|
#define B0110001 49
|
||||||
|
#define B00110001 49
|
||||||
|
#define B110010 50
|
||||||
|
#define B0110010 50
|
||||||
|
#define B00110010 50
|
||||||
|
#define B110011 51
|
||||||
|
#define B0110011 51
|
||||||
|
#define B00110011 51
|
||||||
|
#define B110100 52
|
||||||
|
#define B0110100 52
|
||||||
|
#define B00110100 52
|
||||||
|
#define B110101 53
|
||||||
|
#define B0110101 53
|
||||||
|
#define B00110101 53
|
||||||
|
#define B110110 54
|
||||||
|
#define B0110110 54
|
||||||
|
#define B00110110 54
|
||||||
|
#define B110111 55
|
||||||
|
#define B0110111 55
|
||||||
|
#define B00110111 55
|
||||||
|
#define B111000 56
|
||||||
|
#define B0111000 56
|
||||||
|
#define B00111000 56
|
||||||
|
#define B111001 57
|
||||||
|
#define B0111001 57
|
||||||
|
#define B00111001 57
|
||||||
|
#define B111010 58
|
||||||
|
#define B0111010 58
|
||||||
|
#define B00111010 58
|
||||||
|
#define B111011 59
|
||||||
|
#define B0111011 59
|
||||||
|
#define B00111011 59
|
||||||
|
#define B111100 60
|
||||||
|
#define B0111100 60
|
||||||
|
#define B00111100 60
|
||||||
|
#define B111101 61
|
||||||
|
#define B0111101 61
|
||||||
|
#define B00111101 61
|
||||||
|
#define B111110 62
|
||||||
|
#define B0111110 62
|
||||||
|
#define B00111110 62
|
||||||
|
#define B111111 63
|
||||||
|
#define B0111111 63
|
||||||
|
#define B00111111 63
|
||||||
|
#define B1000000 64
|
||||||
|
#define B01000000 64
|
||||||
|
#define B1000001 65
|
||||||
|
#define B01000001 65
|
||||||
|
#define B1000010 66
|
||||||
|
#define B01000010 66
|
||||||
|
#define B1000011 67
|
||||||
|
#define B01000011 67
|
||||||
|
#define B1000100 68
|
||||||
|
#define B01000100 68
|
||||||
|
#define B1000101 69
|
||||||
|
#define B01000101 69
|
||||||
|
#define B1000110 70
|
||||||
|
#define B01000110 70
|
||||||
|
#define B1000111 71
|
||||||
|
#define B01000111 71
|
||||||
|
#define B1001000 72
|
||||||
|
#define B01001000 72
|
||||||
|
#define B1001001 73
|
||||||
|
#define B01001001 73
|
||||||
|
#define B1001010 74
|
||||||
|
#define B01001010 74
|
||||||
|
#define B1001011 75
|
||||||
|
#define B01001011 75
|
||||||
|
#define B1001100 76
|
||||||
|
#define B01001100 76
|
||||||
|
#define B1001101 77
|
||||||
|
#define B01001101 77
|
||||||
|
#define B1001110 78
|
||||||
|
#define B01001110 78
|
||||||
|
#define B1001111 79
|
||||||
|
#define B01001111 79
|
||||||
|
#define B1010000 80
|
||||||
|
#define B01010000 80
|
||||||
|
#define B1010001 81
|
||||||
|
#define B01010001 81
|
||||||
|
#define B1010010 82
|
||||||
|
#define B01010010 82
|
||||||
|
#define B1010011 83
|
||||||
|
#define B01010011 83
|
||||||
|
#define B1010100 84
|
||||||
|
#define B01010100 84
|
||||||
|
#define B1010101 85
|
||||||
|
#define B01010101 85
|
||||||
|
#define B1010110 86
|
||||||
|
#define B01010110 86
|
||||||
|
#define B1010111 87
|
||||||
|
#define B01010111 87
|
||||||
|
#define B1011000 88
|
||||||
|
#define B01011000 88
|
||||||
|
#define B1011001 89
|
||||||
|
#define B01011001 89
|
||||||
|
#define B1011010 90
|
||||||
|
#define B01011010 90
|
||||||
|
#define B1011011 91
|
||||||
|
#define B01011011 91
|
||||||
|
#define B1011100 92
|
||||||
|
#define B01011100 92
|
||||||
|
#define B1011101 93
|
||||||
|
#define B01011101 93
|
||||||
|
#define B1011110 94
|
||||||
|
#define B01011110 94
|
||||||
|
#define B1011111 95
|
||||||
|
#define B01011111 95
|
||||||
|
#define B1100000 96
|
||||||
|
#define B01100000 96
|
||||||
|
#define B1100001 97
|
||||||
|
#define B01100001 97
|
||||||
|
#define B1100010 98
|
||||||
|
#define B01100010 98
|
||||||
|
#define B1100011 99
|
||||||
|
#define B01100011 99
|
||||||
|
#define B1100100 100
|
||||||
|
#define B01100100 100
|
||||||
|
#define B1100101 101
|
||||||
|
#define B01100101 101
|
||||||
|
#define B1100110 102
|
||||||
|
#define B01100110 102
|
||||||
|
#define B1100111 103
|
||||||
|
#define B01100111 103
|
||||||
|
#define B1101000 104
|
||||||
|
#define B01101000 104
|
||||||
|
#define B1101001 105
|
||||||
|
#define B01101001 105
|
||||||
|
#define B1101010 106
|
||||||
|
#define B01101010 106
|
||||||
|
#define B1101011 107
|
||||||
|
#define B01101011 107
|
||||||
|
#define B1101100 108
|
||||||
|
#define B01101100 108
|
||||||
|
#define B1101101 109
|
||||||
|
#define B01101101 109
|
||||||
|
#define B1101110 110
|
||||||
|
#define B01101110 110
|
||||||
|
#define B1101111 111
|
||||||
|
#define B01101111 111
|
||||||
|
#define B1110000 112
|
||||||
|
#define B01110000 112
|
||||||
|
#define B1110001 113
|
||||||
|
#define B01110001 113
|
||||||
|
#define B1110010 114
|
||||||
|
#define B01110010 114
|
||||||
|
#define B1110011 115
|
||||||
|
#define B01110011 115
|
||||||
|
#define B1110100 116
|
||||||
|
#define B01110100 116
|
||||||
|
#define B1110101 117
|
||||||
|
#define B01110101 117
|
||||||
|
#define B1110110 118
|
||||||
|
#define B01110110 118
|
||||||
|
#define B1110111 119
|
||||||
|
#define B01110111 119
|
||||||
|
#define B1111000 120
|
||||||
|
#define B01111000 120
|
||||||
|
#define B1111001 121
|
||||||
|
#define B01111001 121
|
||||||
|
#define B1111010 122
|
||||||
|
#define B01111010 122
|
||||||
|
#define B1111011 123
|
||||||
|
#define B01111011 123
|
||||||
|
#define B1111100 124
|
||||||
|
#define B01111100 124
|
||||||
|
#define B1111101 125
|
||||||
|
#define B01111101 125
|
||||||
|
#define B1111110 126
|
||||||
|
#define B01111110 126
|
||||||
|
#define B1111111 127
|
||||||
|
#define B01111111 127
|
||||||
|
#define B10000000 128
|
||||||
|
#define B10000001 129
|
||||||
|
#define B10000010 130
|
||||||
|
#define B10000011 131
|
||||||
|
#define B10000100 132
|
||||||
|
#define B10000101 133
|
||||||
|
#define B10000110 134
|
||||||
|
#define B10000111 135
|
||||||
|
#define B10001000 136
|
||||||
|
#define B10001001 137
|
||||||
|
#define B10001010 138
|
||||||
|
#define B10001011 139
|
||||||
|
#define B10001100 140
|
||||||
|
#define B10001101 141
|
||||||
|
#define B10001110 142
|
||||||
|
#define B10001111 143
|
||||||
|
#define B10010000 144
|
||||||
|
#define B10010001 145
|
||||||
|
#define B10010010 146
|
||||||
|
#define B10010011 147
|
||||||
|
#define B10010100 148
|
||||||
|
#define B10010101 149
|
||||||
|
#define B10010110 150
|
||||||
|
#define B10010111 151
|
||||||
|
#define B10011000 152
|
||||||
|
#define B10011001 153
|
||||||
|
#define B10011010 154
|
||||||
|
#define B10011011 155
|
||||||
|
#define B10011100 156
|
||||||
|
#define B10011101 157
|
||||||
|
#define B10011110 158
|
||||||
|
#define B10011111 159
|
||||||
|
#define B10100000 160
|
||||||
|
#define B10100001 161
|
||||||
|
#define B10100010 162
|
||||||
|
#define B10100011 163
|
||||||
|
#define B10100100 164
|
||||||
|
#define B10100101 165
|
||||||
|
#define B10100110 166
|
||||||
|
#define B10100111 167
|
||||||
|
#define B10101000 168
|
||||||
|
#define B10101001 169
|
||||||
|
#define B10101010 170
|
||||||
|
#define B10101011 171
|
||||||
|
#define B10101100 172
|
||||||
|
#define B10101101 173
|
||||||
|
#define B10101110 174
|
||||||
|
#define B10101111 175
|
||||||
|
#define B10110000 176
|
||||||
|
#define B10110001 177
|
||||||
|
#define B10110010 178
|
||||||
|
#define B10110011 179
|
||||||
|
#define B10110100 180
|
||||||
|
#define B10110101 181
|
||||||
|
#define B10110110 182
|
||||||
|
#define B10110111 183
|
||||||
|
#define B10111000 184
|
||||||
|
#define B10111001 185
|
||||||
|
#define B10111010 186
|
||||||
|
#define B10111011 187
|
||||||
|
#define B10111100 188
|
||||||
|
#define B10111101 189
|
||||||
|
#define B10111110 190
|
||||||
|
#define B10111111 191
|
||||||
|
#define B11000000 192
|
||||||
|
#define B11000001 193
|
||||||
|
#define B11000010 194
|
||||||
|
#define B11000011 195
|
||||||
|
#define B11000100 196
|
||||||
|
#define B11000101 197
|
||||||
|
#define B11000110 198
|
||||||
|
#define B11000111 199
|
||||||
|
#define B11001000 200
|
||||||
|
#define B11001001 201
|
||||||
|
#define B11001010 202
|
||||||
|
#define B11001011 203
|
||||||
|
#define B11001100 204
|
||||||
|
#define B11001101 205
|
||||||
|
#define B11001110 206
|
||||||
|
#define B11001111 207
|
||||||
|
#define B11010000 208
|
||||||
|
#define B11010001 209
|
||||||
|
#define B11010010 210
|
||||||
|
#define B11010011 211
|
||||||
|
#define B11010100 212
|
||||||
|
#define B11010101 213
|
||||||
|
#define B11010110 214
|
||||||
|
#define B11010111 215
|
||||||
|
#define B11011000 216
|
||||||
|
#define B11011001 217
|
||||||
|
#define B11011010 218
|
||||||
|
#define B11011011 219
|
||||||
|
#define B11011100 220
|
||||||
|
#define B11011101 221
|
||||||
|
#define B11011110 222
|
||||||
|
#define B11011111 223
|
||||||
|
#define B11100000 224
|
||||||
|
#define B11100001 225
|
||||||
|
#define B11100010 226
|
||||||
|
#define B11100011 227
|
||||||
|
#define B11100100 228
|
||||||
|
#define B11100101 229
|
||||||
|
#define B11100110 230
|
||||||
|
#define B11100111 231
|
||||||
|
#define B11101000 232
|
||||||
|
#define B11101001 233
|
||||||
|
#define B11101010 234
|
||||||
|
#define B11101011 235
|
||||||
|
#define B11101100 236
|
||||||
|
#define B11101101 237
|
||||||
|
#define B11101110 238
|
||||||
|
#define B11101111 239
|
||||||
|
#define B11110000 240
|
||||||
|
#define B11110001 241
|
||||||
|
#define B11110010 242
|
||||||
|
#define B11110011 243
|
||||||
|
#define B11110100 244
|
||||||
|
#define B11110101 245
|
||||||
|
#define B11110110 246
|
||||||
|
#define B11110111 247
|
||||||
|
#define B11111000 248
|
||||||
|
#define B11111001 249
|
||||||
|
#define B11111010 250
|
||||||
|
#define B11111011 251
|
||||||
|
#define B11111100 252
|
||||||
|
#define B11111101 253
|
||||||
|
#define B11111110 254
|
||||||
|
#define B11111111 255
|
||||||
|
|
||||||
|
#endif
|
196
cores/esp32/cbuf.cpp
Normal file
196
cores/esp32/cbuf.cpp
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
/*
|
||||||
|
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"
|
||||||
|
|
||||||
|
cbuf::cbuf(size_t size) :
|
||||||
|
next(NULL), _size(size+1), _buf(new char[size+1]), _bufend(_buf + size + 1), _begin(_buf), _end(_begin)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
cbuf::~cbuf()
|
||||||
|
{
|
||||||
|
delete[] _buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cbuf::resizeAdd(size_t addSize)
|
||||||
|
{
|
||||||
|
return resize(_size + addSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cbuf::resize(size_t newSize)
|
||||||
|
{
|
||||||
|
|
||||||
|
size_t bytes_available = available();
|
||||||
|
newSize += 1;
|
||||||
|
// not lose any data
|
||||||
|
// if data can be lost use remove or flush before resize
|
||||||
|
if((newSize < bytes_available) || (newSize == _size)) {
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *newbuf = new char[newSize];
|
||||||
|
char *oldbuf = _buf;
|
||||||
|
|
||||||
|
if(!newbuf) {
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_buf) {
|
||||||
|
read(newbuf, bytes_available);
|
||||||
|
memset((newbuf + bytes_available), 0x00, (newSize - bytes_available));
|
||||||
|
}
|
||||||
|
|
||||||
|
_begin = newbuf;
|
||||||
|
_end = newbuf + bytes_available;
|
||||||
|
_bufend = newbuf + newSize;
|
||||||
|
_size = newSize;
|
||||||
|
|
||||||
|
_buf = newbuf;
|
||||||
|
delete[] oldbuf;
|
||||||
|
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cbuf::available() const
|
||||||
|
{
|
||||||
|
if(_end >= _begin) {
|
||||||
|
return _end - _begin;
|
||||||
|
}
|
||||||
|
return _size - (_begin - _end);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cbuf::size()
|
||||||
|
{
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cbuf::room() const
|
||||||
|
{
|
||||||
|
if(_end >= _begin) {
|
||||||
|
return _size - (_end - _begin) - 1;
|
||||||
|
}
|
||||||
|
return _begin - _end - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cbuf::peek()
|
||||||
|
{
|
||||||
|
if(empty()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<int>(*_begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cbuf::peek(char *dst, size_t size)
|
||||||
|
{
|
||||||
|
size_t bytes_available = available();
|
||||||
|
size_t size_to_read = (size < bytes_available) ? size : bytes_available;
|
||||||
|
size_t size_read = size_to_read;
|
||||||
|
char * begin = _begin;
|
||||||
|
if(_end < _begin && size_to_read > (size_t) (_bufend - _begin)) {
|
||||||
|
size_t top_size = _bufend - _begin;
|
||||||
|
memcpy(dst, _begin, top_size);
|
||||||
|
begin = _buf;
|
||||||
|
size_to_read -= top_size;
|
||||||
|
dst += top_size;
|
||||||
|
}
|
||||||
|
memcpy(dst, begin, size_to_read);
|
||||||
|
return size_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cbuf::read()
|
||||||
|
{
|
||||||
|
if(empty()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char result = *_begin;
|
||||||
|
_begin = wrap_if_bufend(_begin + 1);
|
||||||
|
return static_cast<int>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cbuf::read(char* dst, size_t size)
|
||||||
|
{
|
||||||
|
size_t bytes_available = available();
|
||||||
|
size_t size_to_read = (size < bytes_available) ? size : bytes_available;
|
||||||
|
size_t size_read = size_to_read;
|
||||||
|
if(_end < _begin && size_to_read > (size_t) (_bufend - _begin)) {
|
||||||
|
size_t top_size = _bufend - _begin;
|
||||||
|
memcpy(dst, _begin, top_size);
|
||||||
|
_begin = _buf;
|
||||||
|
size_to_read -= top_size;
|
||||||
|
dst += top_size;
|
||||||
|
}
|
||||||
|
memcpy(dst, _begin, size_to_read);
|
||||||
|
_begin = wrap_if_bufend(_begin + size_to_read);
|
||||||
|
return size_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cbuf::write(char c)
|
||||||
|
{
|
||||||
|
if(full()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*_end = c;
|
||||||
|
_end = wrap_if_bufend(_end + 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cbuf::write(const char* src, size_t size)
|
||||||
|
{
|
||||||
|
size_t bytes_available = room();
|
||||||
|
size_t size_to_write = (size < bytes_available) ? size : bytes_available;
|
||||||
|
size_t size_written = size_to_write;
|
||||||
|
if(_end >= _begin && size_to_write > (size_t) (_bufend - _end)) {
|
||||||
|
size_t top_size = _bufend - _end;
|
||||||
|
memcpy(_end, src, top_size);
|
||||||
|
_end = _buf;
|
||||||
|
size_to_write -= top_size;
|
||||||
|
src += top_size;
|
||||||
|
}
|
||||||
|
memcpy(_end, src, size_to_write);
|
||||||
|
_end = wrap_if_bufend(_end + size_to_write);
|
||||||
|
return size_written;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cbuf::flush()
|
||||||
|
{
|
||||||
|
_begin = _buf;
|
||||||
|
_end = _buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cbuf::remove(size_t size)
|
||||||
|
{
|
||||||
|
size_t bytes_available = available();
|
||||||
|
if(size >= bytes_available) {
|
||||||
|
flush();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
size_t size_to_remove = (size < bytes_available) ? size : bytes_available;
|
||||||
|
if(_end < _begin && size_to_remove > (size_t) (_bufend - _begin)) {
|
||||||
|
size_t top_size = _bufend - _begin;
|
||||||
|
_begin = _buf;
|
||||||
|
size_to_remove -= top_size;
|
||||||
|
}
|
||||||
|
_begin = wrap_if_bufend(_begin + size_to_remove);
|
||||||
|
return available();
|
||||||
|
}
|
79
cores/esp32/cbuf.h
Normal file
79
cores/esp32/cbuf.h
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __cbuf_h
|
||||||
|
#define __cbuf_h
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.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;
|
||||||
|
|
||||||
|
inline bool empty() const
|
||||||
|
{
|
||||||
|
return _begin == _end;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool full() const
|
||||||
|
{
|
||||||
|
return wrap_if_bufend(_end + 1) == _begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
int peek();
|
||||||
|
size_t peek(char *dst, size_t size);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
inline char* wrap_if_bufend(char* ptr) const
|
||||||
|
{
|
||||||
|
return (ptr == _bufend) ? _buf : ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t _size;
|
||||||
|
char* _buf;
|
||||||
|
const char* _bufend;
|
||||||
|
char* _begin;
|
||||||
|
char* _end;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif//__cbuf_h
|
281
cores/esp32/esp32-hal-adc.c
Normal file
281
cores/esp32/esp32-hal-adc.c
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
// 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-adc.h"
|
||||||
|
#include "driver/adc.h"
|
||||||
|
#include "esp_adc_cal.h"
|
||||||
|
|
||||||
|
#if SOC_DAC_SUPPORTED //ESP32, ESP32S2
|
||||||
|
#include "soc/dac_channel.h"
|
||||||
|
#include "soc/sens_reg.h"
|
||||||
|
#include "soc/rtc_io_reg.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define DEFAULT_VREF 1100
|
||||||
|
|
||||||
|
static uint8_t __analogAttenuation = 3;//11db
|
||||||
|
static uint8_t __analogWidth = ADC_WIDTH_MAX - 1; //3 for ESP32/ESP32C3; 4 for ESP32S2
|
||||||
|
static uint8_t __analogReturnedWidth = SOC_ADC_MAX_BITWIDTH; //12 for ESP32/ESP32C3; 13 for ESP32S2
|
||||||
|
static uint8_t __analogClockDiv = 1;
|
||||||
|
static adc_attenuation_t __pin_attenuation[SOC_GPIO_PIN_COUNT];
|
||||||
|
|
||||||
|
static uint16_t __analogVRef = 0;
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
static uint8_t __analogVRefPin = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline uint16_t mapResolution(uint16_t value)
|
||||||
|
{
|
||||||
|
uint8_t from = __analogWidth + 9;
|
||||||
|
if (from == __analogReturnedWidth) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
if (from > __analogReturnedWidth) {
|
||||||
|
return value >> (from - __analogReturnedWidth);
|
||||||
|
}
|
||||||
|
return value << (__analogReturnedWidth - from);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __analogSetClockDiv(uint8_t clockDiv){
|
||||||
|
if(!clockDiv){
|
||||||
|
clockDiv = 1;
|
||||||
|
}
|
||||||
|
__analogClockDiv = clockDiv;
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
|
||||||
|
adc_set_clk_div(__analogClockDiv);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void __analogSetAttenuation(adc_attenuation_t attenuation)
|
||||||
|
{
|
||||||
|
__analogAttenuation = attenuation & 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
void __analogSetWidth(uint8_t bits){
|
||||||
|
if(bits < 9){
|
||||||
|
bits = 9;
|
||||||
|
} else if(bits > 12){
|
||||||
|
bits = 12;
|
||||||
|
}
|
||||||
|
__analogWidth = bits - 9;
|
||||||
|
adc1_config_width(__analogWidth);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void __analogInit(){
|
||||||
|
static bool initialized = false;
|
||||||
|
if(initialized){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
initialized = true;
|
||||||
|
__analogSetClockDiv(__analogClockDiv);
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
__analogSetWidth(__analogWidth + 9);//in bits
|
||||||
|
#endif
|
||||||
|
for(int i=0; i<SOC_GPIO_PIN_COUNT; i++){
|
||||||
|
__pin_attenuation[i] = ADC_ATTENDB_MAX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __analogSetPinAttenuation(uint8_t pin, adc_attenuation_t attenuation)
|
||||||
|
{
|
||||||
|
int8_t channel = digitalPinToAnalogChannel(pin);
|
||||||
|
if(channel < 0 || attenuation > 3){
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
if(channel > (SOC_ADC_MAX_CHANNEL_NUM - 1)){
|
||||||
|
adc2_config_channel_atten(channel - SOC_ADC_MAX_CHANNEL_NUM, attenuation);
|
||||||
|
} else {
|
||||||
|
adc1_config_channel_atten(channel, attenuation);
|
||||||
|
}
|
||||||
|
__analogInit();
|
||||||
|
if((__pin_attenuation[pin] != ADC_ATTENDB_MAX) || (attenuation != __analogAttenuation)){
|
||||||
|
__pin_attenuation[pin] = attenuation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool __adcAttachPin(uint8_t pin){
|
||||||
|
int8_t channel = digitalPinToAnalogChannel(pin);
|
||||||
|
if(channel < 0){
|
||||||
|
log_e("Pin %u is not ADC pin!", pin);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
__analogInit();
|
||||||
|
int8_t pad = digitalPinToTouchChannel(pin);
|
||||||
|
if(pad >= 0){
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
uint32_t touch = READ_PERI_REG(SENS_SAR_TOUCH_ENABLE_REG);
|
||||||
|
if(touch & (1 << pad)){
|
||||||
|
touch &= ~((1 << (pad + SENS_TOUCH_PAD_OUTEN2_S))
|
||||||
|
| (1 << (pad + SENS_TOUCH_PAD_OUTEN1_S))
|
||||||
|
| (1 << (pad + SENS_TOUCH_PAD_WORKEN_S)));
|
||||||
|
WRITE_PERI_REG(SENS_SAR_TOUCH_ENABLE_REG, touch);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#if SOC_DAC_SUPPORTED
|
||||||
|
else if(pin == DAC_CHANNEL_1_GPIO_NUM){
|
||||||
|
CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_XPD_DAC | RTC_IO_PDAC1_DAC_XPD_FORCE);//stop dac1
|
||||||
|
} else if(pin == DAC_CHANNEL_2_GPIO_NUM){
|
||||||
|
CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_XPD_DAC | RTC_IO_PDAC2_DAC_XPD_FORCE);//stop dac2
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pinMode(pin, ANALOG);
|
||||||
|
__analogSetPinAttenuation(pin, (__pin_attenuation[pin] != ADC_ATTENDB_MAX)?__pin_attenuation[pin]:__analogAttenuation);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void __analogReadResolution(uint8_t bits)
|
||||||
|
{
|
||||||
|
if(!bits || bits > 16){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
__analogReturnedWidth = bits;
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
__analogSetWidth(bits); // hadware from 9 to 12
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t __analogRead(uint8_t pin)
|
||||||
|
{
|
||||||
|
int8_t channel = digitalPinToAnalogChannel(pin);
|
||||||
|
int value = 0;
|
||||||
|
esp_err_t r = ESP_OK;
|
||||||
|
if(channel < 0){
|
||||||
|
log_e("Pin %u is not ADC pin!", pin);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
__adcAttachPin(pin);
|
||||||
|
if(channel > (SOC_ADC_MAX_CHANNEL_NUM - 1)){
|
||||||
|
channel -= SOC_ADC_MAX_CHANNEL_NUM;
|
||||||
|
r = adc2_get_raw( channel, __analogWidth, &value);
|
||||||
|
if ( r == ESP_OK ) {
|
||||||
|
return mapResolution(value);
|
||||||
|
} else if ( r == ESP_ERR_INVALID_STATE ) {
|
||||||
|
log_e("GPIO%u: %s: ADC2 not initialized yet.", pin, esp_err_to_name(r));
|
||||||
|
} else if ( r == ESP_ERR_TIMEOUT ) {
|
||||||
|
log_e("GPIO%u: %s: ADC2 is in use by Wi-Fi. Please see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/adc.html#adc-limitations for more info", pin, esp_err_to_name(r));
|
||||||
|
} else {
|
||||||
|
log_e("GPIO%u: %s", pin, esp_err_to_name(r));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value = adc1_get_raw(channel);
|
||||||
|
return mapResolution(value);
|
||||||
|
}
|
||||||
|
return mapResolution(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t __analogReadMilliVolts(uint8_t pin){
|
||||||
|
int8_t channel = digitalPinToAnalogChannel(pin);
|
||||||
|
if(channel < 0){
|
||||||
|
log_e("Pin %u is not ADC pin!", pin);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!__analogVRef){
|
||||||
|
if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP) == ESP_OK) {
|
||||||
|
log_d("eFuse Two Point: Supported");
|
||||||
|
__analogVRef = DEFAULT_VREF;
|
||||||
|
}
|
||||||
|
if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_VREF) == ESP_OK) {
|
||||||
|
log_d("eFuse Vref: Supported");
|
||||||
|
__analogVRef = DEFAULT_VREF;
|
||||||
|
}
|
||||||
|
if(!__analogVRef){
|
||||||
|
__analogVRef = DEFAULT_VREF;
|
||||||
|
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
if(__analogVRefPin){
|
||||||
|
esp_adc_cal_characteristics_t chars;
|
||||||
|
if(adc_vref_to_gpio(ADC_UNIT_2, __analogVRefPin) == ESP_OK){
|
||||||
|
__analogVRef = __analogRead(__analogVRefPin);
|
||||||
|
esp_adc_cal_characterize(1, __analogAttenuation, __analogWidth, DEFAULT_VREF, &chars);
|
||||||
|
__analogVRef = esp_adc_cal_raw_to_voltage(__analogVRef, &chars);
|
||||||
|
log_d("Vref to GPIO%u: %u", __analogVRefPin, __analogVRef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint8_t unit = 1;
|
||||||
|
if(channel > (SOC_ADC_MAX_CHANNEL_NUM - 1)){
|
||||||
|
unit = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t adc_reading = __analogRead(pin);
|
||||||
|
|
||||||
|
uint8_t atten = __analogAttenuation;
|
||||||
|
if (__pin_attenuation[pin] != ADC_ATTENDB_MAX){
|
||||||
|
atten = __pin_attenuation[pin];
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_adc_cal_characteristics_t chars = {};
|
||||||
|
esp_adc_cal_value_t val_type = esp_adc_cal_characterize(unit, atten, __analogWidth, __analogVRef, &chars);
|
||||||
|
|
||||||
|
static bool print_chars_info = true;
|
||||||
|
if(print_chars_info)
|
||||||
|
{
|
||||||
|
if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) {
|
||||||
|
log_i("ADC%u: Characterized using Two Point Value: %u\n", unit, chars.vref);
|
||||||
|
}
|
||||||
|
else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) {
|
||||||
|
log_i("ADC%u: Characterized using eFuse Vref: %u\n", unit, chars.vref);
|
||||||
|
}
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
else if(__analogVRef != DEFAULT_VREF){
|
||||||
|
log_i("ADC%u: Characterized using Vref to GPIO%u: %u\n", unit, __analogVRefPin, chars.vref);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else {
|
||||||
|
log_i("ADC%u: Characterized using Default Vref: %u\n", unit, chars.vref);
|
||||||
|
}
|
||||||
|
print_chars_info = false;
|
||||||
|
}
|
||||||
|
return esp_adc_cal_raw_to_voltage((uint32_t)adc_reading, &chars);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
|
||||||
|
void __analogSetVRefPin(uint8_t pin){
|
||||||
|
if(pin <25 || pin > 27){
|
||||||
|
pin = 0;
|
||||||
|
}
|
||||||
|
__analogVRefPin = pin;
|
||||||
|
}
|
||||||
|
|
||||||
|
int __hallRead() //hall sensor using idf read
|
||||||
|
{
|
||||||
|
pinMode(36, ANALOG);
|
||||||
|
pinMode(39, ANALOG);
|
||||||
|
__analogSetWidth(12);
|
||||||
|
return hall_sensor_read();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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 analogSetClockDiv(uint8_t clockDiv) __attribute__ ((weak, alias("__analogSetClockDiv")));
|
||||||
|
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")));
|
||||||
|
|
||||||
|
extern bool adcAttachPin(uint8_t pin) __attribute__ ((weak, alias("__adcAttachPin")));
|
||||||
|
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
extern void analogSetVRefPin(uint8_t pin) __attribute__ ((weak, alias("__analogSetVRefPin")));
|
||||||
|
extern void analogSetWidth(uint8_t bits) __attribute__ ((weak, alias("__analogSetWidth")));
|
||||||
|
extern int hallRead() __attribute__ ((weak, alias("__hallRead")));
|
||||||
|
#endif
|
104
cores/esp32/esp32-hal-adc.h
Normal file
104
cores/esp32/esp32-hal-adc.h
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
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_ADC_H_
|
||||||
|
#define MAIN_ESP32_HAL_ADC_H_
|
||||||
|
|
||||||
|
#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 divider for the ADC clock.
|
||||||
|
* Default is 1
|
||||||
|
* Range is 1 - 255
|
||||||
|
* */
|
||||||
|
void analogSetClockDiv(uint8_t clockDiv);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Attach pin to ADC (will also clear any other analog mode that could be on)
|
||||||
|
* */
|
||||||
|
bool adcAttachPin(uint8_t pin);
|
||||||
|
|
||||||
|
#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);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set pin to use for ADC calibration if the esp is not already calibrated (25, 26 or 27)
|
||||||
|
* */
|
||||||
|
void analogSetVRefPin(uint8_t pin);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get value for HALL sensor (without LNA)
|
||||||
|
* connected to pins 36(SVP) and 39(SVN)
|
||||||
|
* */
|
||||||
|
int hallRead();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* MAIN_ESP32_HAL_ADC_H_ */
|
100
cores/esp32/esp32-hal-bt.c
Normal file
100
cores/esp32/esp32-hal-bt.c
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
// 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"
|
||||||
|
|
||||||
|
#ifdef CONFIG_BT_ENABLED
|
||||||
|
|
||||||
|
bool btInUse(){ return true; }
|
||||||
|
|
||||||
|
#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(){
|
||||||
|
esp_bt_controller_config_t cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
||||||
|
if(esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE){
|
||||||
|
esp_bt_controller_init(&cfg);
|
||||||
|
while(esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE){}
|
||||||
|
}
|
||||||
|
if(esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED){
|
||||||
|
if (esp_bt_controller_enable(BT_MODE)) {
|
||||||
|
log_e("BT Enable failed");
|
||||||
|
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 // CONFIG_BT_ENABLED
|
||||||
|
bool btStarted()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool btStart()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool btStop()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONFIG_BT_ENABLED
|
||||||
|
|
32
cores/esp32/esp32-hal-bt.h
Normal file
32
cores/esp32/esp32-hal-bt.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// 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 "esp32-hal.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool btStarted();
|
||||||
|
bool btStart();
|
||||||
|
bool btStop();
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _ESP32_ESP32_HAL_BT_H_ */
|
262
cores/esp32/esp32-hal-cpu.c
Normal file
262
cores/esp32/esp32-hal-cpu.c
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
// 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"
|
||||||
|
#include "soc/rtc_cntl_reg.h"
|
||||||
|
#include "soc/apb_ctrl_reg.h"
|
||||||
|
#include "soc/efuse_reg.h"
|
||||||
|
#include "esp32-hal.h"
|
||||||
|
#include "esp32-hal-cpu.h"
|
||||||
|
|
||||||
|
#include "esp_system.h"
|
||||||
|
#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||||
|
#include "freertos/xtensa_timer.h"
|
||||||
|
#include "esp32/rom/rtc.h"
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||||
|
#include "freertos/xtensa_timer.h"
|
||||||
|
#include "esp32s2/rom/rtc.h"
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
#include "freertos/xtensa_timer.h"
|
||||||
|
#include "esp32s3/rom/rtc.h"
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||||
|
#include "esp32c3/rom/rtc.h"
|
||||||
|
#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 xSemaphoreHandle 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_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
return APB_CLK_FREQ;
|
||||||
|
#else
|
||||||
|
if(conf->freq_mhz >= 80){
|
||||||
|
return 80 * MHZ;
|
||||||
|
}
|
||||||
|
return (conf->source_freq_mhz * MHZ) / conf->div;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us); //private in IDF
|
||||||
|
|
||||||
|
bool setCpuFrequencyMhz(uint32_t cpu_freq_mhz){
|
||||||
|
rtc_cpu_freq_config_t conf, cconf;
|
||||||
|
uint32_t capb, apb;
|
||||||
|
//Get XTAL Frequency and calculate min CPU MHz
|
||||||
|
rtc_xtal_freq_t xtal = rtc_clk_xtal_freq_get();
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
if(xtal > RTC_XTAL_FREQ_AUTO){
|
||||||
|
if(xtal < RTC_XTAL_FREQ_40M) {
|
||||||
|
if(cpu_freq_mhz <= xtal && cpu_freq_mhz != xtal && cpu_freq_mhz != (xtal/2)){
|
||||||
|
log_e("Bad frequency: %u MHz! Options are: 240, 160, 80, %u and %u MHz", cpu_freq_mhz, xtal, xtal/2);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if(cpu_freq_mhz <= xtal && cpu_freq_mhz != xtal && cpu_freq_mhz != (xtal/2) && cpu_freq_mhz != (xtal/4)){
|
||||||
|
log_e("Bad frequency: %u MHz! Options are: 240, 160, 80, %u, %u and %u MHz", cpu_freq_mhz, xtal, xtal/2, xtal/4);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if(cpu_freq_mhz > xtal && cpu_freq_mhz != 240 && cpu_freq_mhz != 160 && cpu_freq_mhz != 80){
|
||||||
|
if(xtal >= RTC_XTAL_FREQ_40M){
|
||||||
|
log_e("Bad frequency: %u MHz! Options are: 240, 160, 80, %u, %u and %u MHz", cpu_freq_mhz, xtal, xtal/2, xtal/4);
|
||||||
|
} else {
|
||||||
|
log_e("Bad frequency: %u MHz! Options are: 240, 160, 80, %u and %u MHz", cpu_freq_mhz, xtal, xtal/2);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
//check if cpu supports the frequency
|
||||||
|
if(cpu_freq_mhz == 240){
|
||||||
|
//Check if ESP32 is rated for a CPU frequency of 160MHz only
|
||||||
|
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)) {
|
||||||
|
log_e("Can not switch to 240 MHz! Chip CPU frequency rated for 160MHz.");
|
||||||
|
cpu_freq_mhz = 160;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
//Get current CPU clock configuration
|
||||||
|
rtc_clk_cpu_freq_get_config(&cconf);
|
||||||
|
//return if frequency has not changed
|
||||||
|
if(cconf.freq_mhz == cpu_freq_mhz){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//Get configuration for the new CPU frequency
|
||||||
|
if(!rtc_clk_cpu_freq_mhz_to_config(cpu_freq_mhz, &conf)){
|
||||||
|
log_e("CPU clock could not be set to %u MHz", cpu_freq_mhz);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//Current APB
|
||||||
|
capb = calculateApb(&cconf);
|
||||||
|
//New APB
|
||||||
|
apb = calculateApb(&conf);
|
||||||
|
|
||||||
|
//Call peripheral functions before the APB change
|
||||||
|
if(apb_change_callbacks){
|
||||||
|
triggerApbChangeCallback(APB_BEFORE_CHANGE, capb, apb);
|
||||||
|
}
|
||||||
|
//Make the frequency change
|
||||||
|
rtc_clk_cpu_freq_set_config_fast(&conf);
|
||||||
|
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;
|
||||||
|
// }
|
||||||
|
//Update APB Freq REG
|
||||||
|
rtc_clk_apb_freq_update(apb);
|
||||||
|
//Update esp_timer divisor
|
||||||
|
esp_timer_impl_update_apb_freq(apb / MHZ);
|
||||||
|
}
|
||||||
|
//Update FreeRTOS Tick Divisor
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32C3
|
||||||
|
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
|
||||||
|
#else
|
||||||
|
uint32_t fcpu = (conf.freq_mhz >= 80)?(conf.freq_mhz * MHZ):(apb);
|
||||||
|
_xt_tick_divisor = fcpu / XT_TICK_PER_SEC;
|
||||||
|
#endif
|
||||||
|
//Call peripheral functions after the APB change
|
||||||
|
if(apb_change_callbacks){
|
||||||
|
triggerApbChangeCallback(APB_AFTER_CHANGE, capb, apb);
|
||||||
|
}
|
||||||
|
log_d("%s: %u / %u = %u Mhz, APB: %u Hz", (conf.source == RTC_CPU_FREQ_SRC_PLL)?"PLL":((conf.source == RTC_CPU_FREQ_SRC_APLL)?"APLL":((conf.source == RTC_CPU_FREQ_SRC_XTAL)?"XTAL":"8M")), 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);
|
||||||
|
}
|
48
cores/esp32/esp32-hal-cpu.h
Normal file
48
cores/esp32/esp32-hal-cpu.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// 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>
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
uint32_t getCpuFrequencyMhz(); // In MHz
|
||||||
|
uint32_t getXtalFrequencyMhz(); // In MHz
|
||||||
|
uint32_t getApbFrequency(); // In Hz
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _ESP32_HAL_CPU_H_ */
|
49
cores/esp32/esp32-hal-dac.c
Normal file
49
cores/esp32/esp32-hal-dac.c
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// 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 "soc/soc_caps.h"
|
||||||
|
|
||||||
|
#ifndef SOC_DAC_SUPPORTED
|
||||||
|
#define NODAC
|
||||||
|
#else
|
||||||
|
#include "soc/dac_channel.h"
|
||||||
|
#include "driver/dac_common.h"
|
||||||
|
|
||||||
|
void ARDUINO_ISR_ATTR __dacWrite(uint8_t pin, uint8_t value)
|
||||||
|
{
|
||||||
|
if(pin < DAC_CHANNEL_1_GPIO_NUM || pin > DAC_CHANNEL_2_GPIO_NUM){
|
||||||
|
return;//not dac pin
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t channel = pin - DAC_CHANNEL_1_GPIO_NUM;
|
||||||
|
dac_output_enable(channel);
|
||||||
|
dac_output_voltage(channel, value);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARDUINO_ISR_ATTR __dacDisable(uint8_t pin)
|
||||||
|
{
|
||||||
|
if(pin < DAC_CHANNEL_1_GPIO_NUM || pin > DAC_CHANNEL_2_GPIO_NUM){
|
||||||
|
return;//not dac pin
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t channel = pin - DAC_CHANNEL_1_GPIO_NUM;
|
||||||
|
dac_output_disable(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void dacWrite(uint8_t pin, uint8_t value) __attribute__ ((weak, alias("__dacWrite")));
|
||||||
|
extern void dacDisable(uint8_t pin) __attribute__ ((weak, alias("__dacDisable")));
|
||||||
|
|
||||||
|
#endif
|
37
cores/esp32/esp32-hal-dac.h
Normal file
37
cores/esp32/esp32-hal-dac.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
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_DAC_H_
|
||||||
|
#define MAIN_ESP32_HAL_DAC_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "esp32-hal.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
|
||||||
|
void dacWrite(uint8_t pin, uint8_t value);
|
||||||
|
void dacDisable(uint8_t pin);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* MAIN_ESP32_HAL_DAC_H_ */
|
233
cores/esp32/esp32-hal-gpio.c
Normal file
233
cores/esp32/esp32-hal-gpio.c
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
// 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 "hal/gpio_hal.h"
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
int8_t ret = -1;
|
||||||
|
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) {
|
||||||
|
ret = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// No Touch Sensor available
|
||||||
|
int8_t digitalPinToTouchChannel(uint8_t pin)
|
||||||
|
{
|
||||||
|
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 availible
|
||||||
|
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"
|
||||||
|
|
||||||
|
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 (!GPIO_IS_VALID_GPIO(pin)) {
|
||||||
|
log_e("Invalid pin selected");
|
||||||
|
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 */
|
||||||
|
.intr_type = gpiohal.dev->pin[pin].int_type /*!< GPIO interrupt type - previously set */
|
||||||
|
};
|
||||||
|
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("GPIO config failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
const uint8_t comm_val = val != 0 ? RGB_BRIGHTNESS : 0;
|
||||||
|
neopixelWrite(RGB_BUILTIN, comm_val, comm_val, comm_val);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
gpio_set_level((gpio_num_t)pin, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern int ARDUINO_ISR_ATTR __digitalRead(uint8_t 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;
|
||||||
|
|
||||||
|
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("GPIO ISR Service Failed To Start");
|
||||||
|
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 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")));
|
90
cores/esp32/esp32-hal-gpio.h
Normal file
90
cores/esp32/esp32-hal-gpio.h
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
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 "esp32-hal.h"
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
|
#include "pins_arduino.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 0x12
|
||||||
|
#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);
|
||||||
|
|
||||||
|
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_ */
|
841
cores/esp32/esp32-hal-i2c-slave.c
Normal file
841
cores/esp32/esp32-hal-i2c-slave.c
Normal file
@ -0,0 +1,841 @@
|
|||||||
|
// 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 <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 "driver/periph_ctrl.h"
|
||||||
|
#include "soc/i2c_reg.h"
|
||||||
|
#include "soc/i2c_struct.h"
|
||||||
|
#include "hal/i2c_ll.h"
|
||||||
|
#include "esp32-hal-log.h"
|
||||||
|
#include "esp32-hal-i2c-slave.h"
|
||||||
|
|
||||||
|
#define I2C_SLAVE_USE_RX_QUEUE 0 // 1: Queue, 0: RingBuffer
|
||||||
|
|
||||||
|
#if SOC_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
|
||||||
|
|
||||||
|
#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;
|
||||||
|
xQueueHandle event_queue;
|
||||||
|
#if I2C_SLAVE_USE_RX_QUEUE
|
||||||
|
xQueueHandle rx_queue;
|
||||||
|
#else
|
||||||
|
RingbufHandle_t rx_ring_buf;
|
||||||
|
#endif
|
||||||
|
xQueueHandle tx_queue;
|
||||||
|
uint32_t rx_data_count;
|
||||||
|
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||||
|
xSemaphoreHandle 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_I2C_NUM] = {
|
||||||
|
{ &I2C0, 0, -1, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0
|
||||||
|
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||||
|
, NULL
|
||||||
|
#endif
|
||||||
|
},
|
||||||
|
#if SOC_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_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
return hw->sr.slave_addressed;
|
||||||
|
#else
|
||||||
|
return hw->status_reg.slave_addressed;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool i2c_ll_slave_rw(i2c_dev_t *hw)//not exposed by hal_ll
|
||||||
|
{
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
return hw->sr.slave_rw;
|
||||||
|
#else
|
||||||
|
return hw->status_reg.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);
|
||||||
|
|
||||||
|
|
||||||
|
//=====================================================================================================================
|
||||||
|
//-------------------------------------- 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_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_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_i("Initialising 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 (i2c->num == 0) {
|
||||||
|
periph_module_enable(PERIPH_I2C0_MODULE);
|
||||||
|
#if SOC_I2C_NUM > 1
|
||||||
|
} else {
|
||||||
|
periph_module_enable(PERIPH_I2C1_MODULE);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_ll_slave_init(i2c->dev);
|
||||||
|
i2c_ll_set_fifo_mode(i2c->dev, true);
|
||||||
|
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_clr_intsts_mask(i2c->dev, I2C_LL_INTR_MASK);
|
||||||
|
i2c_ll_set_fifo_mode(i2c->dev, true);
|
||||||
|
|
||||||
|
if (!i2c->intr_handle) {
|
||||||
|
uint32_t flags = ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED;
|
||||||
|
if(i2c->num == 0) {
|
||||||
|
ret = esp_intr_alloc(ETS_I2C_EXT0_INTR_SOURCE, flags, &i2c_slave_isr_handler, i2c, &i2c->intr_handle);
|
||||||
|
#if SOC_I2C_NUM > 1
|
||||||
|
} else {
|
||||||
|
ret = esp_intr_alloc(ETS_I2C_EXT1_INTR_SOURCE, flags, &i2c_slave_isr_handler, i2c, &i2c->intr_handle);
|
||||||
|
#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);
|
||||||
|
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_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();
|
||||||
|
i2c_slave_free_resources(i2c);
|
||||||
|
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_I2C_NUM){
|
||||||
|
log_e("Invalid port num: %u", num);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
size_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);
|
||||||
|
if (i2c_ll_get_txfifo_len(i2c->dev) < SOC_I2C_FIFO_LEN) {
|
||||||
|
i2c_ll_txfifo_rst(i2c->dev);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
to_fifo = i2c_ll_get_txfifo_len(i2c->dev);
|
||||||
|
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_RATE_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_clr_intsts_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_clk_cal_t clk_cal;
|
||||||
|
#if SOC_I2C_SUPPORT_APB
|
||||||
|
i2c_ll_cal_bus_clk(APB_CLK_FREQ, clk_speed, &clk_cal);
|
||||||
|
i2c_ll_set_source_clk(i2c->dev, I2C_SCLK_APB); /*!< I2C source clock from APB, 80M*/
|
||||||
|
#elif SOC_I2C_SUPPORT_XTAL
|
||||||
|
i2c_ll_cal_bus_clk(XTAL_CLK_FREQ, clk_speed, &clk_cal);
|
||||||
|
i2c_ll_set_source_clk(i2c->dev, I2C_SCLK_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_set_bus_timing(i2c->dev, &clk_cal);
|
||||||
|
i2c_ll_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 a=0;a<9; a++) {
|
||||||
|
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 = i2c_ll_get_txfifo_len(i2c->dev);
|
||||||
|
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 = i2c_ll_get_intsts_mask(i2c->dev);
|
||||||
|
i2c_ll_clr_intsts_mask(i2c->dev, activeInt);
|
||||||
|
uint8_t rx_fifo_len = i2c_ll_get_rxfifo_cnt(i2c->dev);
|
||||||
|
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 dissapears 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);
|
||||||
|
}
|
35
cores/esp32/esp32-hal-i2c-slave.h
Normal file
35
cores/esp32/esp32-hal-i2c-slave.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
#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
|
343
cores/esp32/esp32-hal-i2c.c
Normal file
343
cores/esp32/esp32-hal-i2c.c
Normal file
@ -0,0 +1,343 @@
|
|||||||
|
// 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"
|
||||||
|
#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"
|
||||||
|
|
||||||
|
typedef volatile struct {
|
||||||
|
bool initialized;
|
||||||
|
uint32_t frequency;
|
||||||
|
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||||
|
SemaphoreHandle_t lock;
|
||||||
|
#endif
|
||||||
|
} i2c_bus_t;
|
||||||
|
|
||||||
|
static i2c_bus_t bus[SOC_I2C_NUM];
|
||||||
|
|
||||||
|
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){
|
||||||
|
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");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!frequency){
|
||||||
|
frequency = 100000UL;
|
||||||
|
} else if(frequency > 1000000UL){
|
||||||
|
frequency = 1000000UL;
|
||||||
|
}
|
||||||
|
log_i("Initialising 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 choosen
|
||||||
|
|
||||||
|
esp_err_t 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;
|
||||||
|
//Clock Stretching Timeout: 20b:esp32, 5b:esp32-c3, 24b:esp32-s2
|
||||||
|
i2c_set_timeout((i2c_port_t)i2c_num, I2C_LL_MAX_TIMEOUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#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_RATE_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_RATE_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_RATE_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_RATE_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;
|
||||||
|
}
|
||||||
|
// Freq limitation when using different clock sources
|
||||||
|
#define I2C_CLK_LIMIT_REF_TICK (1 * 1000 * 1000 / 20) /*!< Limited by REF_TICK, no more than REF_TICK/20*/
|
||||||
|
#define I2C_CLK_LIMIT_APB (80 * 1000 * 1000 / 20) /*!< Limited by APB, no more than APB/20*/
|
||||||
|
#define I2C_CLK_LIMIT_RTC (20 * 1000 * 1000 / 20) /*!< Limited by RTC, no more than RTC/20*/
|
||||||
|
#define I2C_CLK_LIMIT_XTAL (40 * 1000 * 1000 / 20) /*!< Limited by RTC, no more than XTAL/20*/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t character; /*!< I2C source clock characteristic */
|
||||||
|
uint32_t clk_freq; /*!< I2C source clock frequency */
|
||||||
|
} i2c_clk_alloc_t;
|
||||||
|
|
||||||
|
// i2c clock characteristic, The order is the same as i2c_sclk_t.
|
||||||
|
static i2c_clk_alloc_t i2c_clk_alloc[I2C_SCLK_MAX] = {
|
||||||
|
{0, 0},
|
||||||
|
#if SOC_I2C_SUPPORT_APB
|
||||||
|
{0, I2C_CLK_LIMIT_APB}, /*!< I2C APB clock characteristic*/
|
||||||
|
#endif
|
||||||
|
#if SOC_I2C_SUPPORT_XTAL
|
||||||
|
{0, I2C_CLK_LIMIT_XTAL}, /*!< I2C XTAL characteristic*/
|
||||||
|
#endif
|
||||||
|
#if SOC_I2C_SUPPORT_RTC
|
||||||
|
{I2C_SCLK_SRC_FLAG_LIGHT_SLEEP | I2C_SCLK_SRC_FLAG_AWARE_DFS, I2C_CLK_LIMIT_RTC}, /*!< I2C 20M RTC characteristic*/
|
||||||
|
#endif
|
||||||
|
#if SOC_I2C_SUPPORT_REF_TICK
|
||||||
|
{I2C_SCLK_SRC_FLAG_AWARE_DFS, I2C_CLK_LIMIT_REF_TICK}, /*!< 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 unaccessable now.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (frequency <= i2c_clk_alloc[clk].clk_freq) {
|
||||||
|
src_clk = clk;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(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);
|
||||||
|
i2c_hal_set_bus_timing(&(hal), frequency, src_clk);
|
||||||
|
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;
|
||||||
|
}
|
41
cores/esp32/esp32-hal-i2c.h
Normal file
41
cores/esp32/esp32-hal-i2c.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// 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_
|
||||||
|
|
||||||
|
#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);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _ESP32_HAL_I2C_H_ */
|
232
cores/esp32/esp32-hal-ledc.c
Normal file
232
cores/esp32/esp32-hal-ledc.c
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
// 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 "soc/soc_caps.h"
|
||||||
|
#include "driver/ledc.h"
|
||||||
|
|
||||||
|
#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_WIDE_NUM
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LEDC Chan to Group/Channel/Timer Mapping
|
||||||
|
** ledc: 0 => Group: 0, Channel: 0, Timer: 0
|
||||||
|
** ledc: 1 => Group: 0, Channel: 1, Timer: 0
|
||||||
|
** ledc: 2 => Group: 0, Channel: 2, Timer: 1
|
||||||
|
** ledc: 3 => Group: 0, Channel: 3, Timer: 1
|
||||||
|
** ledc: 4 => Group: 0, Channel: 4, Timer: 2
|
||||||
|
** ledc: 5 => Group: 0, Channel: 5, Timer: 2
|
||||||
|
** ledc: 6 => Group: 0, Channel: 6, Timer: 3
|
||||||
|
** ledc: 7 => Group: 0, Channel: 7, Timer: 3
|
||||||
|
** ledc: 8 => Group: 1, Channel: 0, Timer: 0
|
||||||
|
** ledc: 9 => Group: 1, Channel: 1, Timer: 0
|
||||||
|
** ledc: 10 => Group: 1, Channel: 2, Timer: 1
|
||||||
|
** ledc: 11 => Group: 1, Channel: 3, Timer: 1
|
||||||
|
** ledc: 12 => Group: 1, Channel: 4, Timer: 2
|
||||||
|
** ledc: 13 => Group: 1, Channel: 5, Timer: 2
|
||||||
|
** ledc: 14 => Group: 1, Channel: 6, Timer: 3
|
||||||
|
** ledc: 15 => Group: 1, Channel: 7, Timer: 3
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint8_t channels_resolution[LEDC_CHANNELS] = {0};
|
||||||
|
|
||||||
|
uint32_t ledcSetup(uint8_t chan, uint32_t freq, uint8_t bit_num)
|
||||||
|
{
|
||||||
|
if(chan >= LEDC_CHANNELS || bit_num > LEDC_MAX_BIT_WIDTH){
|
||||||
|
log_e("No more LEDC channels available! (maximum %u) or bit width too big (maximum %u)", LEDC_CHANNELS, LEDC_MAX_BIT_WIDTH);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t group=(chan/8), timer=((chan/2)%4);
|
||||||
|
|
||||||
|
ledc_timer_config_t ledc_timer = {
|
||||||
|
.speed_mode = group,
|
||||||
|
.timer_num = timer,
|
||||||
|
.duty_resolution = bit_num,
|
||||||
|
.freq_hz = freq,
|
||||||
|
.clk_cfg = LEDC_DEFAULT_CLK
|
||||||
|
};
|
||||||
|
if(ledc_timer_config(&ledc_timer) != ESP_OK)
|
||||||
|
{
|
||||||
|
log_e("ledc setup failed!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
channels_resolution[chan] = bit_num;
|
||||||
|
return ledc_get_freq(group,timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ledcWrite(uint8_t chan, uint32_t duty)
|
||||||
|
{
|
||||||
|
if(chan >= LEDC_CHANNELS){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint8_t group=(chan/8), channel=(chan%8);
|
||||||
|
|
||||||
|
//Fixing if all bits in resolution is set = LEDC FULL ON
|
||||||
|
uint32_t max_duty = (1 << channels_resolution[chan]) - 1;
|
||||||
|
|
||||||
|
if((duty == max_duty) && (max_duty != 1)){
|
||||||
|
duty = max_duty + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ledc_set_duty(group, channel, duty);
|
||||||
|
ledc_update_duty(group, channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ledcRead(uint8_t chan)
|
||||||
|
{
|
||||||
|
if(chan >= LEDC_CHANNELS){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
uint8_t group=(chan/8), channel=(chan%8);
|
||||||
|
return ledc_get_duty(group,channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ledcReadFreq(uint8_t chan)
|
||||||
|
{
|
||||||
|
if(!ledcRead(chan)){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
uint8_t group=(chan/8), timer=((chan/2)%4);
|
||||||
|
return ledc_get_freq(group,timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ledcWriteTone(uint8_t chan, uint32_t freq)
|
||||||
|
{
|
||||||
|
if(chan >= LEDC_CHANNELS){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(!freq){
|
||||||
|
ledcWrite(chan, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t group=(chan/8), timer=((chan/2)%4);
|
||||||
|
|
||||||
|
ledc_timer_config_t ledc_timer = {
|
||||||
|
.speed_mode = group,
|
||||||
|
.timer_num = timer,
|
||||||
|
.duty_resolution = 10,
|
||||||
|
.freq_hz = freq,
|
||||||
|
.clk_cfg = LEDC_DEFAULT_CLK
|
||||||
|
};
|
||||||
|
|
||||||
|
if(ledc_timer_config(&ledc_timer) != ESP_OK)
|
||||||
|
{
|
||||||
|
log_e("ledcSetup failed!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
channels_resolution[chan] = 10;
|
||||||
|
|
||||||
|
uint32_t res_freq = ledc_get_freq(group,timer);
|
||||||
|
ledcWrite(chan, 0x1FF);
|
||||||
|
return res_freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ledcWriteNote(uint8_t chan, 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(chan, noteFreq);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ledcAttachPin(uint8_t pin, uint8_t chan)
|
||||||
|
{
|
||||||
|
if(chan >= LEDC_CHANNELS){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint8_t group=(chan/8), channel=(chan%8), timer=((chan/2)%4);
|
||||||
|
|
||||||
|
ledc_channel_config_t ledc_channel = {
|
||||||
|
.speed_mode = group,
|
||||||
|
.channel = channel,
|
||||||
|
.timer_sel = timer,
|
||||||
|
.intr_type = LEDC_INTR_DISABLE,
|
||||||
|
.gpio_num = pin,
|
||||||
|
.duty = 0,
|
||||||
|
.hpoint = 0
|
||||||
|
};
|
||||||
|
ledc_channel_config(&ledc_channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ledcDetachPin(uint8_t pin)
|
||||||
|
{
|
||||||
|
pinMatrixOutDetach(pin, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ledcChangeFrequency(uint8_t chan, uint32_t freq, uint8_t bit_num)
|
||||||
|
{
|
||||||
|
if(chan >= LEDC_CHANNELS || bit_num > LEDC_MAX_BIT_WIDTH){
|
||||||
|
log_e("LEDC channel not available! (maximum %u) or bit width too big (maximum %u)", LEDC_CHANNELS, LEDC_MAX_BIT_WIDTH);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
uint8_t group=(chan/8), timer=((chan/2)%4);
|
||||||
|
|
||||||
|
ledc_timer_config_t ledc_timer = {
|
||||||
|
.speed_mode = group,
|
||||||
|
.timer_num = timer,
|
||||||
|
.duty_resolution = bit_num,
|
||||||
|
.freq_hz = freq,
|
||||||
|
.clk_cfg = LEDC_DEFAULT_CLK
|
||||||
|
};
|
||||||
|
|
||||||
|
if(ledc_timer_config(&ledc_timer) != ESP_OK)
|
||||||
|
{
|
||||||
|
log_e("ledcChangeFrequency failed!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
channels_resolution[chan] = bit_num;
|
||||||
|
return ledc_get_freq(group,timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int8_t pin_to_channel[SOC_GPIO_PIN_COUNT] = { 0 };
|
||||||
|
static int cnt_channel = LEDC_CHANNELS;
|
||||||
|
void analogWrite(uint8_t pin, int value) {
|
||||||
|
// Use ledc hardware for internal pins
|
||||||
|
if (pin < SOC_GPIO_PIN_COUNT) {
|
||||||
|
if (pin_to_channel[pin] == 0) {
|
||||||
|
if (!cnt_channel) {
|
||||||
|
log_e("No more analogWrite channels available! You can have maximum %u", LEDC_CHANNELS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pin_to_channel[pin] = cnt_channel--;
|
||||||
|
ledcAttachPin(pin, cnt_channel);
|
||||||
|
ledcSetup(cnt_channel, 1000, 8);
|
||||||
|
}
|
||||||
|
ledcWrite(pin_to_channel[pin] - 1, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t analogGetChannel(uint8_t pin) {
|
||||||
|
return pin_to_channel[pin] - 1;
|
||||||
|
}
|
45
cores/esp32/esp32-hal-ledc.h
Normal file
45
cores/esp32/esp32-hal-ledc.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// 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_LEDC_H_
|
||||||
|
#define _ESP32_HAL_LEDC_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.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;
|
||||||
|
|
||||||
|
//channel 0-15 resolution 1-16bits freq limits depend on resolution
|
||||||
|
uint32_t ledcSetup(uint8_t channel, uint32_t freq, uint8_t resolution_bits);
|
||||||
|
void ledcWrite(uint8_t channel, uint32_t duty);
|
||||||
|
uint32_t ledcWriteTone(uint8_t channel, uint32_t freq);
|
||||||
|
uint32_t ledcWriteNote(uint8_t channel, note_t note, uint8_t octave);
|
||||||
|
uint32_t ledcRead(uint8_t channel);
|
||||||
|
uint32_t ledcReadFreq(uint8_t channel);
|
||||||
|
void ledcAttachPin(uint8_t pin, uint8_t channel);
|
||||||
|
void ledcDetachPin(uint8_t pin);
|
||||||
|
uint32_t ledcChangeFrequency(uint8_t channel, uint32_t freq, uint8_t resolution_bits);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _ESP32_HAL_LEDC_H_ */
|
226
cores/esp32/esp32-hal-log.h
Normal file
226
cores/esp32/esp32-hal-log.h
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
// 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"
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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__
|
||||||
|
|
||||||
|
#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, TAG, format, ##__VA_ARGS__);}while(0)
|
||||||
|
#define isr_log_v(format, ...) do {ets_printf(LOG_FORMAT(V, format), esp_log_timestamp(), TAG, ##__VA_ARGS__);}while(0)
|
||||||
|
#define log_buf_v(b,l) do {ESP_LOG_BUFFER_HEXDUMP(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, TAG, format, ##__VA_ARGS__);}while(0)
|
||||||
|
#define isr_log_d(format, ...) do {ets_printf(LOG_FORMAT(D, format), esp_log_timestamp(), TAG, ##__VA_ARGS__);}while(0)
|
||||||
|
#define log_buf_d(b,l) do {ESP_LOG_BUFFER_HEXDUMP(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, TAG, format, ##__VA_ARGS__);}while(0)
|
||||||
|
#define isr_log_i(format, ...) do {ets_printf(LOG_FORMAT(I, format), esp_log_timestamp(), TAG, ##__VA_ARGS__);}while(0)
|
||||||
|
#define log_buf_i(b,l) do {ESP_LOG_BUFFER_HEXDUMP(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, TAG, format, ##__VA_ARGS__);}while(0)
|
||||||
|
#define isr_log_w(format, ...) do {ets_printf(LOG_FORMAT(W, format), esp_log_timestamp(), TAG, ##__VA_ARGS__);}while(0)
|
||||||
|
#define log_buf_w(b,l) do {ESP_LOG_BUFFER_HEXDUMP(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, TAG, format, ##__VA_ARGS__);}while(0)
|
||||||
|
#define isr_log_e(format, ...) do {ets_printf(LOG_FORMAT(E, format), esp_log_timestamp(), TAG, ##__VA_ARGS__);}while(0)
|
||||||
|
#define log_buf_e(b,l) do {ESP_LOG_BUFFER_HEXDUMP(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, TAG, format, ##__VA_ARGS__);}while(0)
|
||||||
|
#define isr_log_n(format, ...) do {ets_printf(LOG_FORMAT(E, format), esp_log_timestamp(), TAG, ##__VA_ARGS__);}while(0)
|
||||||
|
#define log_buf_n(b,l) do {ESP_LOG_BUFFER_HEXDUMP(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"
|
||||||
|
|
||||||
|
#ifdef USE_ESP_IDF_LOG
|
||||||
|
//#ifndef TAG
|
||||||
|
//#define TAG "ARDUINO"
|
||||||
|
//#endif
|
||||||
|
//#define log_n(format, ...) myLog(ESP_LOG_NONE, format, ##__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#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__ */
|
63
cores/esp32/esp32-hal-matrix.c
Normal file
63
cores/esp32/esp32-hal-matrix.c
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// 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+
|
||||||
|
#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_ESP32C3
|
||||||
|
#include "esp32c3/rom/gpio.h"
|
||||||
|
#else
|
||||||
|
#error Target CONFIG_IDF_TARGET is not supported
|
||||||
|
#endif
|
||||||
|
#else // ESP32 Before IDF 4.0
|
||||||
|
#include "rom/gpio.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MATRIX_DETACH_OUT_SIG 0x100
|
||||||
|
#define MATRIX_DETACH_IN_LOW_PIN 0x30
|
||||||
|
#define MATRIX_DETACH_IN_LOW_HIGH 0x38
|
||||||
|
|
||||||
|
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, MATRIX_DETACH_OUT_SIG, 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?MATRIX_DETACH_IN_LOW_HIGH:MATRIX_DETACH_IN_LOW_PIN, signal, inverted);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
void ARDUINO_ISR_ATTR intrMatrixAttach(uint32_t source, uint32_t inum){
|
||||||
|
intr_matrix_set(PRO_CPU_NUM, source, inum);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
35
cores/esp32/esp32-hal-matrix.h
Normal file
35
cores/esp32/esp32-hal-matrix.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// 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_ */
|
285
cores/esp32/esp32-hal-misc.c
Normal file
285
cores/esp32/esp32-hal-misc.c
Normal 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 "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
|
||||||
|
#ifdef CONFIG_BT_ENABLED
|
||||||
|
#include "esp_bt.h"
|
||||||
|
#endif //CONFIG_BT_ENABLED
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include "soc/rtc.h"
|
||||||
|
#include "soc/rtc_cntl_reg.h"
|
||||||
|
#include "soc/apb_ctrl_reg.h"
|
||||||
|
#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"
|
||||||
|
#include "driver/temp_sensor.h"
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
#include "esp32s3/rom/rtc.h"
|
||||||
|
#include "driver/temp_sensor.h"
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||||
|
#include "esp32c3/rom/rtc.h"
|
||||||
|
#include "driver/temp_sensor.h"
|
||||||
|
#else
|
||||||
|
#error Target CONFIG_IDF_TARGET is not supported
|
||||||
|
#endif
|
||||||
|
#else // ESP32 Before IDF 4.0
|
||||||
|
#include "rom/rtc.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//Undocumented!!! Get chip temperature in Farenheit
|
||||||
|
//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;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
float temperatureRead()
|
||||||
|
{
|
||||||
|
float result = NAN;
|
||||||
|
temp_sensor_config_t tsens = TSENS_CONFIG_DEFAULT();
|
||||||
|
temp_sensor_set_config(tsens);
|
||||||
|
temp_sensor_start();
|
||||||
|
temp_sensor_read_celsius(&result);
|
||||||
|
temp_sensor_stop();
|
||||||
|
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 = xTaskGetIdleTaskHandleForCPU(0);
|
||||||
|
if(idle_0 == NULL || esp_task_wdt_add(idle_0) != ESP_OK){
|
||||||
|
log_e("Failed to add Core 0 IDLE task to WDT");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void disableCore0WDT(){
|
||||||
|
TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCPU(0);
|
||||||
|
if(idle_0 == NULL || esp_task_wdt_delete(idle_0) != ESP_OK){
|
||||||
|
log_e("Failed to remove Core 0 IDLE task from WDT");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef CONFIG_FREERTOS_UNICORE
|
||||||
|
void enableCore1WDT(){
|
||||||
|
TaskHandle_t idle_1 = xTaskGetIdleTaskHandleForCPU(1);
|
||||||
|
if(idle_1 == NULL || esp_task_wdt_add(idle_1) != ESP_OK){
|
||||||
|
log_e("Failed to add Core 1 IDLE task to WDT");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void disableCore1WDT(){
|
||||||
|
TaskHandle_t idle_1 = xTaskGetIdleTaskHandleForCPU(1);
|
||||||
|
if(idle_1 == NULL || esp_task_wdt_delete(idle_1) != ESP_OK){
|
||||||
|
log_e("Failed to remove Core 1 IDLE task from WDT");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#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
|
||||||
|
bool verifyOta() __attribute__((weak));
|
||||||
|
bool verifyOta() { return true; }
|
||||||
|
|
||||||
|
bool verifyRollbackLater() __attribute__((weak));
|
||||||
|
bool verifyRollbackLater() { return false; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_BT_ENABLED
|
||||||
|
//overwritten in esp32-hal-bt.c
|
||||||
|
bool btInUse() __attribute__((weak));
|
||||||
|
bool btInUse(){ return false; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void initArduino()
|
||||||
|
{
|
||||||
|
#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
|
||||||
|
//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;
|
||||||
|
#ifdef F_CPU
|
||||||
|
setCpuFrequencyMhz(F_CPU/1000000);
|
||||||
|
#endif
|
||||||
|
#if CONFIG_SPIRAM_SUPPORT || CONFIG_SPIRAM
|
||||||
|
psramInit();
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
#ifdef CONFIG_BT_ENABLED
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
148
cores/esp32/esp32-hal-psram.c
Normal file
148
cores/esp32/esp32-hal-psram.c
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
// 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"
|
||||||
|
#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||||
|
#include "esp32/spiram.h"
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||||
|
#include "esp32s2/spiram.h"
|
||||||
|
#include "esp32s2/rom/cache.h"
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
#include "esp32s3/spiram.h"
|
||||||
|
#include "esp32s3/rom/cache.h"
|
||||||
|
#else
|
||||||
|
#error Target CONFIG_IDF_TARGET is not supported
|
||||||
|
#endif
|
||||||
|
#else // ESP32 Before IDF 4.0
|
||||||
|
#include "esp_spiram.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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_spiram_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_VER_PKG);
|
||||||
|
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;
|
||||||
|
log_w("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_spiram_init() != ESP_OK) {
|
||||||
|
spiramFailed = true;
|
||||||
|
log_w("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;
|
||||||
|
}
|
||||||
|
esp_spiram_init_cache();
|
||||||
|
//testSPIRAM() allows user to bypass SPI RAM test routine
|
||||||
|
if (!testSPIRAM()) {
|
||||||
|
spiramFailed = true;
|
||||||
|
log_e("PSRAM test failed!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (esp_spiram_add_to_heapalloc() != ESP_OK) {
|
||||||
|
spiramFailed = true;
|
||||||
|
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
|
||||||
|
#endif /* CONFIG_SPIRAM_BOOT_INIT */
|
||||||
|
log_i("PSRAM enabled");
|
||||||
|
spiramDetected = true;
|
||||||
|
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
|
44
cores/esp32/esp32-hal-psram.h
Normal file
44
cores/esp32/esp32-hal-psram.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// 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"
|
||||||
|
|
||||||
|
#ifndef BOARD_HAS_PSRAM
|
||||||
|
#ifdef CONFIG_SPIRAM_SUPPORT
|
||||||
|
#undef CONFIG_SPIRAM_SUPPORT
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_SPIRAM
|
||||||
|
#undef CONFIG_SPIRAM
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool psramInit();
|
||||||
|
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_ */
|
47
cores/esp32/esp32-hal-rgb-led.c
Normal file
47
cores/esp32/esp32-hal-rgb-led.c
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#include "esp32-hal-rgb-led.h"
|
||||||
|
|
||||||
|
|
||||||
|
void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val){
|
||||||
|
rmt_data_t led_data[24];
|
||||||
|
static rmt_obj_t* rmt_send = NULL;
|
||||||
|
static bool initialized = false;
|
||||||
|
|
||||||
|
uint8_t _pin = pin;
|
||||||
|
#ifdef RGB_BUILTIN
|
||||||
|
if(pin == RGB_BUILTIN){
|
||||||
|
_pin = RGB_BUILTIN-SOC_GPIO_PIN_COUNT;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(!initialized){
|
||||||
|
if((rmt_send = rmtInit(_pin, RMT_TX_MODE, RMT_MEM_64)) == NULL){
|
||||||
|
log_e("RGB LED driver initialization failed!");
|
||||||
|
rmt_send = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rmtSetTick(rmt_send, 100);
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int color[] = {green_val, red_val, blue_val}; // Color coding is in order GREEN, RED, BLUE
|
||||||
|
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(rmt_send, led_data, 24);
|
||||||
|
}
|
20
cores/esp32/esp32-hal-rgb-led.h
Normal file
20
cores/esp32/esp32-hal-rgb-led.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#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
|
||||||
|
|
||||||
|
void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* MAIN_ESP32_HAL_RGB_LED_H_ */
|
634
cores/esp32/esp32-hal-rmt.c
Normal file
634
cores/esp32/esp32-hal-rmt.c
Normal file
@ -0,0 +1,634 @@
|
|||||||
|
// Copyright 2018 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 "driver/rmt.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal macros
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define MAX_CHANNELS (SOC_RMT_GROUPS * SOC_RMT_CHANNELS_PER_GROUP)
|
||||||
|
|
||||||
|
#define RMT_TX_CH_START (0)
|
||||||
|
#define RMT_TX_CH_END (SOC_RMT_TX_CANDIDATES_PER_GROUP - 1)
|
||||||
|
#define RMT_RX_CH_START (SOC_RMT_CHANNELS_PER_GROUP - SOC_RMT_TX_CANDIDATES_PER_GROUP)
|
||||||
|
#define RMT_RX_CH_END (SOC_RMT_CHANNELS_PER_GROUP - 1)
|
||||||
|
|
||||||
|
#define _LIMIT(a,b) (a>b?b:a)
|
||||||
|
|
||||||
|
#if CONFIG_DISABLE_HAL_LOCKS
|
||||||
|
# define RMT_MUTEX_LOCK(channel)
|
||||||
|
# define RMT_MUTEX_UNLOCK(channel)
|
||||||
|
#else
|
||||||
|
# define RMT_MUTEX_LOCK(channel) do {} while (xSemaphoreTake(g_rmt_objlocks[channel], portMAX_DELAY) != pdPASS)
|
||||||
|
# define RMT_MUTEX_UNLOCK(channel) xSemaphoreGive(g_rmt_objlocks[channel])
|
||||||
|
#endif /* CONFIG_DISABLE_HAL_LOCKS */
|
||||||
|
|
||||||
|
//#define _RMT_INTERNAL_DEBUG
|
||||||
|
#ifdef _RMT_INTERNAL_DEBUG
|
||||||
|
# define DEBUG_INTERRUPT_START(pin) digitalWrite(pin, 1);
|
||||||
|
# define DEBUG_INTERRUPT_END(pin) digitalWrite(pin, 0);
|
||||||
|
#else
|
||||||
|
# define DEBUG_INTERRUPT_START(pin)
|
||||||
|
# define DEBUG_INTERRUPT_END(pin)
|
||||||
|
#endif /* _RMT_INTERNAL_DEBUG */
|
||||||
|
|
||||||
|
#define RMT_DEFAULT_ARD_CONFIG_TX(gpio, channel_id, buffers) \
|
||||||
|
{ \
|
||||||
|
.rmt_mode = RMT_MODE_TX, \
|
||||||
|
.channel = channel_id, \
|
||||||
|
.gpio_num = gpio, \
|
||||||
|
.clk_div = 1, \
|
||||||
|
.mem_block_num = buffers, \
|
||||||
|
.flags = 0, \
|
||||||
|
.tx_config = { \
|
||||||
|
.carrier_level = RMT_CARRIER_LEVEL_HIGH, \
|
||||||
|
.idle_level = RMT_IDLE_LEVEL_LOW, \
|
||||||
|
.carrier_duty_percent = 50, \
|
||||||
|
.carrier_en = false, \
|
||||||
|
.loop_en = false, \
|
||||||
|
.idle_output_en = true, \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define RMT_DEFAULT_ARD_CONFIG_RX(gpio, channel_id, buffers) \
|
||||||
|
{ \
|
||||||
|
.rmt_mode = RMT_MODE_RX, \
|
||||||
|
.channel = channel_id, \
|
||||||
|
.gpio_num = gpio, \
|
||||||
|
.clk_div = 1, \
|
||||||
|
.mem_block_num = buffers, \
|
||||||
|
.flags = 0, \
|
||||||
|
.rx_config = { \
|
||||||
|
.idle_threshold = 0x80, \
|
||||||
|
.filter_ticks_thresh = 100, \
|
||||||
|
.filter_en = false, \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Typedefs for internal stuctures, enums
|
||||||
|
*/
|
||||||
|
struct rmt_obj_s
|
||||||
|
{
|
||||||
|
bool allocated;
|
||||||
|
EventGroupHandle_t events;
|
||||||
|
int channel;
|
||||||
|
int buffers;
|
||||||
|
int data_size;
|
||||||
|
uint32_t* data_ptr;
|
||||||
|
rmt_rx_data_cb_t cb;
|
||||||
|
void * arg;
|
||||||
|
TaskHandle_t rxTaskHandle;
|
||||||
|
bool rx_completed;
|
||||||
|
bool tx_not_rx;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal variables for channel descriptors
|
||||||
|
*/
|
||||||
|
static xSemaphoreHandle g_rmt_objlocks[MAX_CHANNELS] = {
|
||||||
|
NULL, NULL, NULL, NULL,
|
||||||
|
#if MAX_CHANNELS > 4
|
||||||
|
NULL, NULL, NULL, NULL
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static rmt_obj_t g_rmt_objects[MAX_CHANNELS] = {
|
||||||
|
{ false, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, true, true},
|
||||||
|
{ false, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, true, true},
|
||||||
|
{ false, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, true, true},
|
||||||
|
{ false, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, true, true},
|
||||||
|
#if MAX_CHANNELS > 4
|
||||||
|
{ false, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, true, true},
|
||||||
|
{ false, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, true, true},
|
||||||
|
{ false, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, true, true},
|
||||||
|
{ false, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, true, true},
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal variables for driver data
|
||||||
|
*/
|
||||||
|
static xSemaphoreHandle g_rmt_block_lock = NULL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal method (private) declarations
|
||||||
|
*/
|
||||||
|
|
||||||
|
static rmt_obj_t* _rmtAllocate(int pin, int from, int size)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
// setup how many buffers shall we use
|
||||||
|
g_rmt_objects[from].buffers = size;
|
||||||
|
|
||||||
|
for (i=0; i<size; i++) {
|
||||||
|
// mark the block of channels as used
|
||||||
|
g_rmt_objects[i+from].allocated = true;
|
||||||
|
}
|
||||||
|
return &(g_rmt_objects[from]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _rmtDumpStatus(rmt_obj_t* rmt)
|
||||||
|
{
|
||||||
|
bool loop_en;
|
||||||
|
uint8_t div_cnt;
|
||||||
|
uint8_t memNum;
|
||||||
|
bool lowPowerMode;
|
||||||
|
rmt_mem_owner_t owner;
|
||||||
|
uint16_t idleThreshold;
|
||||||
|
uint32_t status;
|
||||||
|
rmt_source_clk_t srcClk;
|
||||||
|
rmt_channel_t channel = rmt->channel;
|
||||||
|
|
||||||
|
RMT_MUTEX_LOCK(channel);
|
||||||
|
rmt_get_tx_loop_mode(channel, &loop_en);
|
||||||
|
rmt_get_clk_div(channel, &div_cnt);
|
||||||
|
rmt_get_mem_block_num(channel, &memNum);
|
||||||
|
rmt_get_mem_pd(channel, &lowPowerMode);
|
||||||
|
rmt_get_memory_owner(channel, &owner);
|
||||||
|
rmt_get_rx_idle_thresh(channel, &idleThreshold);
|
||||||
|
rmt_get_status(channel, &status);
|
||||||
|
rmt_get_source_clk(channel, &srcClk);
|
||||||
|
|
||||||
|
log_d("Status for RMT channel %d", channel);
|
||||||
|
log_d("- Loop enabled: %d", loop_en);
|
||||||
|
log_d("- Clock divisor: %d", div_cnt);
|
||||||
|
log_d("- Number of memory blocks: %d", memNum);
|
||||||
|
log_d("- Low power mode: %d", lowPowerMode);
|
||||||
|
log_d("- Memory owner: %s", owner==RMT_MEM_OWNER_TX?"TX":"RX");
|
||||||
|
log_d("- Idle threshold: %d", idleThreshold);
|
||||||
|
log_d("- Status: %d", status);
|
||||||
|
log_d("- Source clock: %s", srcClk==RMT_BASECLK_APB?"APB (80MHz)":"1MHz");
|
||||||
|
RMT_MUTEX_UNLOCK(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _rmtRxTask(void *args) {
|
||||||
|
rmt_obj_t *rmt = (rmt_obj_t *) args;
|
||||||
|
RingbufHandle_t rb = NULL;
|
||||||
|
size_t rmt_len = 0;
|
||||||
|
rmt_item32_t *data = NULL;
|
||||||
|
|
||||||
|
if (!rmt) {
|
||||||
|
log_e(" -- Inavalid Argument");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int channel = rmt->channel;
|
||||||
|
rmt_get_ringbuf_handle(channel, &rb);
|
||||||
|
if (!rb) {
|
||||||
|
log_e(" -- Failed to get RMT ringbuffer handle");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
data = (rmt_item32_t *) xRingbufferReceive(rb, &rmt_len, portMAX_DELAY);
|
||||||
|
if (data) {
|
||||||
|
log_d(" -- Got %d bytes on RX Ringbuffer - CH %d", rmt_len, rmt->channel);
|
||||||
|
rmt->rx_completed = true; // used in rmtReceiveCompleted()
|
||||||
|
// callback
|
||||||
|
if (rmt->cb) {
|
||||||
|
(rmt->cb)((uint32_t *)data, rmt_len / sizeof(rmt_item32_t), rmt->arg);
|
||||||
|
} else {
|
||||||
|
// stop RX -- will force a correct call with a callback pointer / new rmtReadData() / rmtReadAsync()
|
||||||
|
rmt_rx_stop(channel);
|
||||||
|
}
|
||||||
|
// Async Read -- copy data to caller
|
||||||
|
if (rmt->data_ptr && rmt->data_size) {
|
||||||
|
uint32_t data_size = rmt->data_size;
|
||||||
|
uint32_t read_len = rmt_len / sizeof(rmt_item32_t);
|
||||||
|
if (read_len < rmt->data_size) data_size = read_len;
|
||||||
|
rmt_item32_t *p = (rmt_item32_t *)rmt->data_ptr;
|
||||||
|
for (uint32_t i = 0; i < data_size; i++) {
|
||||||
|
p[i] = data[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// set events
|
||||||
|
if (rmt->events) {
|
||||||
|
xEventGroupSetBits(rmt->events, RMT_FLAG_RX_DONE);
|
||||||
|
}
|
||||||
|
vRingbufferReturnItem(rb, (void *) data);
|
||||||
|
} // xRingbufferReceive
|
||||||
|
} // for(;;)
|
||||||
|
|
||||||
|
err:
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool _rmtCreateRxTask(rmt_obj_t* rmt)
|
||||||
|
{
|
||||||
|
if (!rmt) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (rmt->rxTaskHandle) { // Task already created
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
xTaskCreate(_rmtRxTask, "rmt_rx_task", 4096, rmt, 20, &rmt->rxTaskHandle);
|
||||||
|
|
||||||
|
if(rmt->rxTaskHandle == NULL){
|
||||||
|
log_e("RMT RX Task create failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to test if an RMT channel is correctly assigned to TX or RX, issuing an error message if necessary
|
||||||
|
// Also test RMT pointer for NULL and returns false in case it is NULL
|
||||||
|
// return true when it is correctly assigned, false otherwise
|
||||||
|
static bool _rmtCheckTXnotRX(rmt_obj_t* rmt, bool tx_not_rx)
|
||||||
|
{
|
||||||
|
if (!rmt) { // also returns false on NULL
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rmt->tx_not_rx == tx_not_rx) { // matches expected RX/TX channel
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tx_not_rx) { // expected TX channel
|
||||||
|
log_e("Can't write on a RX RMT Channel");
|
||||||
|
} else{ // expected RX channel
|
||||||
|
log_e("Can't read on a TX RMT Channel");
|
||||||
|
}
|
||||||
|
return false; // missmatched
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public method definitions
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool rmtSetCarrier(rmt_obj_t* rmt, bool carrier_en, bool carrier_level, uint32_t low, uint32_t high)
|
||||||
|
{
|
||||||
|
if (!_rmtCheckTXnotRX(rmt, RMT_TX_MODE) || low > 0xFFFF || high > 0xFFFF) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size_t channel = rmt->channel;
|
||||||
|
|
||||||
|
RMT_MUTEX_LOCK(channel);
|
||||||
|
rmt_set_tx_carrier(channel, carrier_en, high, low, carrier_level);
|
||||||
|
RMT_MUTEX_UNLOCK(channel);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rmtSetFilter(rmt_obj_t* rmt, bool filter_en, uint32_t filter_level)
|
||||||
|
{
|
||||||
|
if (!_rmtCheckTXnotRX(rmt, RMT_RX_MODE) || filter_level > 0xFF) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size_t channel = rmt->channel;
|
||||||
|
|
||||||
|
RMT_MUTEX_LOCK(channel);
|
||||||
|
rmt_set_rx_filter(channel, filter_en, filter_level);
|
||||||
|
RMT_MUTEX_UNLOCK(channel);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rmtSetRxThreshold(rmt_obj_t* rmt, uint32_t value)
|
||||||
|
{
|
||||||
|
if (!_rmtCheckTXnotRX(rmt, RMT_RX_MODE) || value > 0xFFFF) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size_t channel = rmt->channel;
|
||||||
|
|
||||||
|
RMT_MUTEX_LOCK(channel);
|
||||||
|
rmt_set_rx_idle_thresh(channel, value);
|
||||||
|
RMT_MUTEX_UNLOCK(channel);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool rmtDeinit(rmt_obj_t *rmt)
|
||||||
|
{
|
||||||
|
if (!rmt) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
if (rmt != &(g_rmt_objects[rmt->channel])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RMT_MUTEX_LOCK(rmt->channel);
|
||||||
|
// force stopping rmt processing
|
||||||
|
if (rmt->tx_not_rx) {
|
||||||
|
rmt_tx_stop(rmt->channel);
|
||||||
|
} else {
|
||||||
|
rmt_rx_stop(rmt->channel);
|
||||||
|
if(rmt->rxTaskHandle){
|
||||||
|
vTaskDelete(rmt->rxTaskHandle);
|
||||||
|
rmt->rxTaskHandle = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rmt_driver_uninstall(rmt->channel);
|
||||||
|
|
||||||
|
size_t from = rmt->channel;
|
||||||
|
size_t to = rmt->buffers + rmt->channel;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = from; i < to; i++) {
|
||||||
|
g_rmt_objects[i].allocated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_rmt_objects[from].channel = 0;
|
||||||
|
g_rmt_objects[from].buffers = 0;
|
||||||
|
RMT_MUTEX_UNLOCK(rmt->channel);
|
||||||
|
|
||||||
|
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||||
|
if(g_rmt_objlocks[from] != NULL) {
|
||||||
|
vSemaphoreDelete(g_rmt_objlocks[from]);
|
||||||
|
g_rmt_objlocks[from] = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rmtLoop(rmt_obj_t* rmt, rmt_data_t* data, size_t size)
|
||||||
|
{
|
||||||
|
if (!_rmtCheckTXnotRX(rmt, RMT_TX_MODE)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int channel = rmt->channel;
|
||||||
|
RMT_MUTEX_LOCK(channel);
|
||||||
|
rmt_tx_stop(channel);
|
||||||
|
rmt_set_tx_loop_mode(channel, true);
|
||||||
|
rmt_write_items(channel, (const rmt_item32_t *)data, size, false);
|
||||||
|
RMT_MUTEX_UNLOCK(channel);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rmtWrite(rmt_obj_t* rmt, rmt_data_t* data, size_t size)
|
||||||
|
{
|
||||||
|
if (!_rmtCheckTXnotRX(rmt, RMT_TX_MODE)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int channel = rmt->channel;
|
||||||
|
RMT_MUTEX_LOCK(channel);
|
||||||
|
rmt_tx_stop(channel);
|
||||||
|
rmt_set_tx_loop_mode(channel, false);
|
||||||
|
rmt_write_items(channel, (const rmt_item32_t *)data, size, false);
|
||||||
|
RMT_MUTEX_UNLOCK(channel);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rmtWriteBlocking(rmt_obj_t* rmt, rmt_data_t* data, size_t size)
|
||||||
|
{
|
||||||
|
if (!_rmtCheckTXnotRX(rmt, RMT_TX_MODE)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int channel = rmt->channel;
|
||||||
|
RMT_MUTEX_LOCK(channel);
|
||||||
|
rmt_tx_stop(channel);
|
||||||
|
rmt_set_tx_loop_mode(channel, false);
|
||||||
|
rmt_write_items(channel, (const rmt_item32_t *)data, size, true);
|
||||||
|
RMT_MUTEX_UNLOCK(channel);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rmtReadData(rmt_obj_t* rmt, uint32_t* data, size_t size)
|
||||||
|
{
|
||||||
|
if (!_rmtCheckTXnotRX(rmt, RMT_RX_MODE)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
rmtReadAsync(rmt, (rmt_data_t*) data, size, NULL, false, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool rmtBeginReceive(rmt_obj_t* rmt)
|
||||||
|
{
|
||||||
|
if (!_rmtCheckTXnotRX(rmt, RMT_RX_MODE)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int channel = rmt->channel;
|
||||||
|
|
||||||
|
RMT_MUTEX_LOCK(channel);
|
||||||
|
rmt_set_memory_owner(channel, RMT_MEM_OWNER_RX);
|
||||||
|
rmt_rx_start(channel, true);
|
||||||
|
rmt->rx_completed = false;
|
||||||
|
RMT_MUTEX_UNLOCK(channel);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rmtReceiveCompleted(rmt_obj_t* rmt)
|
||||||
|
{
|
||||||
|
if (!rmt) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rmt->rx_completed;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rmtRead(rmt_obj_t* rmt, rmt_rx_data_cb_t cb, void * arg)
|
||||||
|
{
|
||||||
|
if (!_rmtCheckTXnotRX(rmt, RMT_RX_MODE)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int channel = rmt->channel;
|
||||||
|
|
||||||
|
rmt->arg = arg;
|
||||||
|
rmt->cb = cb;
|
||||||
|
|
||||||
|
RMT_MUTEX_LOCK(channel);
|
||||||
|
// cb as NULL is a way to cancel the callback process
|
||||||
|
if (cb == NULL) {
|
||||||
|
rmt_rx_stop(channel);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Start a read process but now with a call back function
|
||||||
|
rmt_set_memory_owner(channel, RMT_MEM_OWNER_RX);
|
||||||
|
rmt_rx_start(channel, true);
|
||||||
|
rmt->rx_completed = false;
|
||||||
|
_rmtCreateRxTask(rmt);
|
||||||
|
RMT_MUTEX_UNLOCK(channel);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rmtEnd(rmt_obj_t* rmt)
|
||||||
|
{
|
||||||
|
if (!rmt) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int channel = rmt->channel;
|
||||||
|
|
||||||
|
RMT_MUTEX_LOCK(channel);
|
||||||
|
if (rmt->tx_not_rx) {
|
||||||
|
rmt_tx_stop(channel);
|
||||||
|
} else {
|
||||||
|
rmt_rx_stop(channel);
|
||||||
|
rmt->rx_completed = true;
|
||||||
|
}
|
||||||
|
RMT_MUTEX_UNLOCK(channel);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rmtReadAsync(rmt_obj_t* rmt, rmt_data_t* data, size_t size, void* eventFlag, bool waitForData, uint32_t timeout)
|
||||||
|
{
|
||||||
|
if (!_rmtCheckTXnotRX(rmt, RMT_RX_MODE)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int channel = rmt->channel;
|
||||||
|
|
||||||
|
// No limit on size with IDF ;-)
|
||||||
|
//if (g_rmt_objects[channel].buffers < size/SOC_RMT_MEM_WORDS_PER_CHANNEL) {
|
||||||
|
// return false;
|
||||||
|
//}
|
||||||
|
|
||||||
|
RMT_MUTEX_LOCK(channel);
|
||||||
|
if (eventFlag) {
|
||||||
|
xEventGroupClearBits(eventFlag, RMT_FLAGS_ALL);
|
||||||
|
}
|
||||||
|
// if NULL, no problems - rmtReadAsync works as a plain rmtReadData()
|
||||||
|
rmt->events = eventFlag;
|
||||||
|
|
||||||
|
// if NULL, no problems - task will take care of it
|
||||||
|
rmt->data_ptr = (uint32_t*)data;
|
||||||
|
rmt->data_size = size;
|
||||||
|
|
||||||
|
// Start a read process
|
||||||
|
rmt_set_memory_owner(channel, RMT_MEM_OWNER_RX);
|
||||||
|
rmt_rx_start(channel, true);
|
||||||
|
rmt->rx_completed = false;
|
||||||
|
_rmtCreateRxTask(rmt);
|
||||||
|
RMT_MUTEX_UNLOCK(channel);
|
||||||
|
|
||||||
|
// wait for data if requested so
|
||||||
|
if (waitForData && eventFlag) {
|
||||||
|
xEventGroupWaitBits(eventFlag, RMT_FLAGS_ALL,
|
||||||
|
pdTRUE /* clear on exit */, pdFALSE /* wait for all bits */, timeout);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
float rmtSetTick(rmt_obj_t* rmt, float tick)
|
||||||
|
{
|
||||||
|
if (!rmt) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size_t channel = rmt->channel;
|
||||||
|
|
||||||
|
RMT_MUTEX_LOCK(channel);
|
||||||
|
// RMT_BASECLK_REF (1MHz) is not supported in IDF upon Programmming Guide
|
||||||
|
// Only APB works
|
||||||
|
rmt_set_source_clk(channel, RMT_BASECLK_APB);
|
||||||
|
int apb_div = _LIMIT(tick/12.5f, 256);
|
||||||
|
float apb_tick = 12.5f * apb_div;
|
||||||
|
|
||||||
|
rmt_set_clk_div(channel, apb_div & 0xFF);
|
||||||
|
RMT_MUTEX_UNLOCK(channel);
|
||||||
|
return apb_tick;
|
||||||
|
}
|
||||||
|
|
||||||
|
rmt_obj_t* rmtInit(int pin, bool tx_not_rx, rmt_reserve_memsize_t memsize)
|
||||||
|
{
|
||||||
|
int buffers = memsize;
|
||||||
|
rmt_obj_t* rmt = NULL;
|
||||||
|
size_t i = 0;
|
||||||
|
size_t j = 0;
|
||||||
|
|
||||||
|
// create common block mutex for protecting allocs from multiple threads
|
||||||
|
if (!g_rmt_block_lock) {
|
||||||
|
g_rmt_block_lock = xSemaphoreCreateMutex();
|
||||||
|
}
|
||||||
|
// lock
|
||||||
|
while (xSemaphoreTake(g_rmt_block_lock, portMAX_DELAY) != pdPASS) {}
|
||||||
|
|
||||||
|
// Some SoC may have fixed channel numbers for TX and RX - example: ESP32C3
|
||||||
|
uint8_t ch_start, ch_end;
|
||||||
|
if (tx_not_rx) {
|
||||||
|
ch_start = RMT_TX_CH_START;
|
||||||
|
ch_end = RMT_TX_CH_END;
|
||||||
|
} else {
|
||||||
|
ch_start = RMT_RX_CH_START;
|
||||||
|
ch_end = RMT_RX_CH_END;
|
||||||
|
}
|
||||||
|
for (i=ch_start; i<=ch_end; i++) {
|
||||||
|
for (j=0; j<buffers && i+j <= ch_end; j++) {
|
||||||
|
// if the space is ocupied break and continue on other channel
|
||||||
|
if (g_rmt_objects[i+j].allocated) {
|
||||||
|
i += j; // continue searching from latter channel
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (j == buffers) {
|
||||||
|
// found a space in channel descriptors
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == MAX_CHANNELS || i+j > MAX_CHANNELS || j != buffers) {
|
||||||
|
xSemaphoreGive(g_rmt_block_lock);
|
||||||
|
log_e("rmInit Failed - not enough channels");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A suitable channel has been found, it has to block its resources in our internal data strucuture
|
||||||
|
size_t channel = i;
|
||||||
|
rmt = _rmtAllocate(pin, i, buffers);
|
||||||
|
|
||||||
|
xSemaphoreGive(g_rmt_block_lock);
|
||||||
|
|
||||||
|
rmt->buffers = buffers;
|
||||||
|
rmt->channel = channel;
|
||||||
|
rmt->arg = NULL;
|
||||||
|
rmt->cb = NULL;
|
||||||
|
rmt->data_ptr = NULL;
|
||||||
|
rmt->data_size = 0;
|
||||||
|
rmt->rx_completed = false;
|
||||||
|
rmt->events = NULL;
|
||||||
|
rmt->tx_not_rx = tx_not_rx;
|
||||||
|
|
||||||
|
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||||
|
if(g_rmt_objlocks[channel] == NULL) {
|
||||||
|
g_rmt_objlocks[channel] = xSemaphoreCreateMutex();
|
||||||
|
if(g_rmt_objlocks[channel] == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
RMT_MUTEX_LOCK(channel);
|
||||||
|
esp_err_t esp_err_code = ESP_OK;
|
||||||
|
|
||||||
|
if (tx_not_rx) {
|
||||||
|
rmt_config_t config = RMT_DEFAULT_ARD_CONFIG_TX(pin, channel, buffers);
|
||||||
|
esp_err_code = rmt_config(&config);
|
||||||
|
if (esp_err_code == ESP_OK)
|
||||||
|
esp_err_code = rmt_driver_install(channel, 0, 0);
|
||||||
|
log_d(" -- %s RMT - CH %d - %d RAM Blocks - pin %d", tx_not_rx?"TX":"RX", channel, buffers, pin);
|
||||||
|
} else {
|
||||||
|
rmt_config_t config = RMT_DEFAULT_ARD_CONFIG_RX(pin, channel, buffers);
|
||||||
|
esp_err_code = rmt_config(&config);
|
||||||
|
if (esp_err_code == ESP_OK)
|
||||||
|
esp_err_code = rmt_driver_install(channel, 1024, 0);
|
||||||
|
if (esp_err_code == ESP_OK)
|
||||||
|
esp_err_code = rmt_set_memory_owner(channel, RMT_MEM_OWNER_RX);
|
||||||
|
log_d(" -- %s RMT - CH %d - %d RAM Blocks - pin %d", tx_not_rx?"TX":"RX", channel, buffers, pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
RMT_MUTEX_UNLOCK(channel);
|
||||||
|
|
||||||
|
if (esp_err_code == ESP_OK) {
|
||||||
|
return rmt;
|
||||||
|
} else {
|
||||||
|
log_e("RMT failed to initilize.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
169
cores/esp32/esp32-hal-rmt.h
Normal file
169
cores/esp32/esp32-hal-rmt.h
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
// Copyright 2018 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_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// notification flags
|
||||||
|
#define RMT_FLAG_TX_DONE (1)
|
||||||
|
#define RMT_FLAG_RX_DONE (2)
|
||||||
|
#define RMT_FLAG_ERROR (4)
|
||||||
|
#define RMT_FLAGS_ALL (RMT_FLAG_TX_DONE | RMT_FLAG_RX_DONE | RMT_FLAG_ERROR)
|
||||||
|
|
||||||
|
#define RMT_TX_MODE true
|
||||||
|
#define RMT_RX_MODE false
|
||||||
|
|
||||||
|
struct rmt_obj_s;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
RMT_MEM_64 = 1,
|
||||||
|
RMT_MEM_128 = 2,
|
||||||
|
RMT_MEM_192 = 3,
|
||||||
|
RMT_MEM_256 = 4,
|
||||||
|
RMT_MEM_320 = 5,
|
||||||
|
RMT_MEM_384 = 6,
|
||||||
|
RMT_MEM_448 = 7,
|
||||||
|
RMT_MEM_512 = 8,
|
||||||
|
} rmt_reserve_memsize_t;
|
||||||
|
|
||||||
|
typedef struct rmt_obj_s rmt_obj_t;
|
||||||
|
|
||||||
|
typedef void (*rmt_rx_data_cb_t)(uint32_t *data, size_t len, void *arg);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
uint32_t duration0 :15;
|
||||||
|
uint32_t level0 :1;
|
||||||
|
uint32_t duration1 :15;
|
||||||
|
uint32_t level1 :1;
|
||||||
|
};
|
||||||
|
uint32_t val;
|
||||||
|
};
|
||||||
|
} rmt_data_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints object information
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void _rmtDumpStatus(rmt_obj_t* rmt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the object
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
rmt_obj_t* rmtInit(int pin, bool tx_not_rx, rmt_reserve_memsize_t memsize);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the clock/divider of timebase the nearest tick to the supplied value in nanoseconds
|
||||||
|
* return the real actual tick value in ns
|
||||||
|
*/
|
||||||
|
float rmtSetTick(rmt_obj_t* rmt, float tick);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sending data in one-go mode or continual mode
|
||||||
|
* (more data being send while updating buffers in interrupts)
|
||||||
|
* Non-Blocking mode - returns right after executing
|
||||||
|
*/
|
||||||
|
bool rmtWrite(rmt_obj_t* rmt, rmt_data_t* data, size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sending data in one-go mode or continual mode
|
||||||
|
* (more data being send while updating buffers in interrupts)
|
||||||
|
* Blocking mode - only returns when data has been sent
|
||||||
|
*/
|
||||||
|
bool rmtWriteBlocking(rmt_obj_t* rmt, rmt_data_t* data, size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loop data up to the reserved memsize continuously
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool rmtLoop(rmt_obj_t* rmt, rmt_data_t* data, size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates async receive, event flag indicates data received
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool rmtReadAsync(rmt_obj_t* rmt, rmt_data_t* data, size_t size, void* eventFlag, bool waitForData, uint32_t timeout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates async receive with automatic buffering
|
||||||
|
* and callback with data from ISR
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool rmtRead(rmt_obj_t* rmt, rmt_rx_data_cb_t cb, void * arg);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Ends async receive started with rmtRead(); but does not
|
||||||
|
* rmtDeInit().
|
||||||
|
*/
|
||||||
|
bool rmtEnd(rmt_obj_t* rmt);
|
||||||
|
|
||||||
|
/* Additional interface */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start reception
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool rmtBeginReceive(rmt_obj_t* rmt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if reception completes
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool rmtReceiveCompleted(rmt_obj_t* rmt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the data for particular channel
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool rmtReadData(rmt_obj_t* rmt, uint32_t* data, size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setting threshold for Rx completed
|
||||||
|
*/
|
||||||
|
bool rmtSetRxThreshold(rmt_obj_t* rmt, uint32_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setting carrier
|
||||||
|
*/
|
||||||
|
bool rmtSetCarrier(rmt_obj_t* rmt, bool carrier_en, bool carrier_level, uint32_t low, uint32_t high);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setting input filter
|
||||||
|
*/
|
||||||
|
bool rmtSetFilter(rmt_obj_t* rmt, bool filter_en, uint32_t filter_level);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deinitialize the driver
|
||||||
|
*/
|
||||||
|
bool rmtDeinit(rmt_obj_t *rmt);
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// * uninstall interrupt when all channels are deinit
|
||||||
|
// * send only-conti mode with circular-buffer
|
||||||
|
// * put sanity checks to some macro or inlines
|
||||||
|
// * doxy comments
|
||||||
|
// * error reporting
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* MAIN_ESP32_HAL_RMT_H_ */
|
89
cores/esp32/esp32-hal-sigmadelta.c
Normal file
89
cores/esp32/esp32-hal-sigmadelta.c
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
// 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 "soc/soc_caps.h"
|
||||||
|
#include "driver/sigmadelta.h"
|
||||||
|
|
||||||
|
static uint8_t duty_set[SOC_SIGMADELTA_CHANNEL_NUM] = {0};
|
||||||
|
static uint32_t prescaler_set[SOC_SIGMADELTA_CHANNEL_NUM] = {0};
|
||||||
|
|
||||||
|
static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){
|
||||||
|
if(old_apb == new_apb){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint32_t iarg = (uint32_t)arg;
|
||||||
|
uint8_t channel = iarg;
|
||||||
|
if(ev_type == APB_AFTER_CHANGE){
|
||||||
|
old_apb /= 1000000;
|
||||||
|
new_apb /= 1000000;
|
||||||
|
uint32_t old_prescale = prescaler_set[channel] + 1;
|
||||||
|
uint32_t new_prescale = ((new_apb * old_prescale) / old_apb) - 1;
|
||||||
|
sigmadelta_set_prescale(channel,new_prescale);
|
||||||
|
prescaler_set[channel] = new_prescale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t sigmaDeltaSetup(uint8_t pin, uint8_t channel, uint32_t freq) //chan 0-x according to SOC, freq 1220-312500
|
||||||
|
{
|
||||||
|
if(channel >= SOC_SIGMADELTA_CHANNEL_NUM){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t apb_freq = getApbFrequency();
|
||||||
|
uint32_t prescale = (apb_freq/(freq*256)) - 1;
|
||||||
|
if(prescale > 0xFF) {
|
||||||
|
prescale = 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
sigmadelta_config_t sigmadelta_cfg = {
|
||||||
|
.channel = channel,
|
||||||
|
.sigmadelta_prescale = prescale,
|
||||||
|
.sigmadelta_duty = 0,
|
||||||
|
.sigmadelta_gpio = pin,
|
||||||
|
};
|
||||||
|
sigmadelta_config(&sigmadelta_cfg);
|
||||||
|
|
||||||
|
prescaler_set[channel] = prescale;
|
||||||
|
uint32_t iarg = channel;
|
||||||
|
addApbChangeCallback((void*)iarg, _on_apb_change);
|
||||||
|
|
||||||
|
return apb_freq/((prescale + 1) * 256);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sigmaDeltaWrite(uint8_t channel, uint8_t duty) //chan 0-x according to SOC duty 8 bit
|
||||||
|
{
|
||||||
|
if(channel >= SOC_SIGMADELTA_CHANNEL_NUM){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
duty -= 128;
|
||||||
|
|
||||||
|
sigmadelta_set_duty(channel,duty);
|
||||||
|
duty_set[channel] = duty;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t sigmaDeltaRead(uint8_t channel) //chan 0-x according to SOC
|
||||||
|
{
|
||||||
|
if(channel >= SOC_SIGMADELTA_CHANNEL_NUM){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return duty_set[channel]+128;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sigmaDeltaDetachPin(uint8_t pin)
|
||||||
|
{
|
||||||
|
pinMatrixOutDetach(pin, false, false);
|
||||||
|
}
|
36
cores/esp32/esp32-hal-sigmadelta.h
Normal file
36
cores/esp32/esp32-hal-sigmadelta.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// 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_SD_H_
|
||||||
|
#define _ESP32_HAL_SD_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
//channel 0-7 freq 1220-312500 duty 0-255
|
||||||
|
uint32_t sigmaDeltaSetup(uint8_t pin, uint8_t channel, uint32_t freq);
|
||||||
|
void sigmaDeltaWrite(uint8_t channel, uint8_t duty);
|
||||||
|
uint8_t sigmaDeltaRead(uint8_t channel);
|
||||||
|
void sigmaDeltaDetachPin(uint8_t pin);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _ESP32_HAL_SD_H_ */
|
1560
cores/esp32/esp32-hal-spi.c
Normal file
1560
cores/esp32/esp32-hal-spi.c
Normal file
File diff suppressed because it is too large
Load Diff
149
cores/esp32/esp32-hal-spi.h
Normal file
149
cores/esp32/esp32-hal-spi.h
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
// 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_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define SPI_HAS_TRANSACTION
|
||||||
|
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
#define FSPI 0
|
||||||
|
#define HSPI 1
|
||||||
|
#else
|
||||||
|
#define FSPI 1 //SPI bus attached to the flash (can use the same data lines but different SS)
|
||||||
|
#define HSPI 2 //SPI bus normally mapped to pins 12 - 15, but can be matrixed to any pins
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
#define VSPI 3 //SPI bus normally attached to pins 5, 18, 19 and 23, but can be matrixed to any pins
|
||||||
|
#endif
|
||||||
|
#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_CS0 0
|
||||||
|
#define SPI_CS1 1
|
||||||
|
#define SPI_CS2 2
|
||||||
|
#define SPI_CS_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
|
||||||
|
void spiAttachSCK(spi_t * spi, int8_t sck);
|
||||||
|
void spiAttachMISO(spi_t * spi, int8_t miso);
|
||||||
|
void spiAttachMOSI(spi_t * spi, int8_t mosi);
|
||||||
|
void spiDetachSCK(spi_t * spi, int8_t sck);
|
||||||
|
void spiDetachMISO(spi_t * spi, int8_t miso);
|
||||||
|
void spiDetachMOSI(spi_t * spi, int8_t mosi);
|
||||||
|
|
||||||
|
//Attach/Detach SS pin to SPI_CSx signal
|
||||||
|
void spiAttachSS(spi_t * spi, uint8_t cs_num, int8_t ss);
|
||||||
|
void spiDetachSS(spi_t * spi, int8_t ss);
|
||||||
|
|
||||||
|
//Enable/Disable SPI_CSx pins
|
||||||
|
void spiEnableSSPins(spi_t * spi, uint8_t cs_mask);
|
||||||
|
void spiDisableSSPins(spi_t * spi, uint8_t cs_mask);
|
||||||
|
|
||||||
|
//Enable/Disable hardware control of SPI_CSx pins
|
||||||
|
void spiSSEnable(spi_t * spi);
|
||||||
|
void spiSSDisable(spi_t * spi);
|
||||||
|
|
||||||
|
//Activate enabled SPI_CSx pins
|
||||||
|
void spiSSSet(spi_t * spi);
|
||||||
|
//Deactivate enabled SPI_CSx pins
|
||||||
|
void spiSSClear(spi_t * spi);
|
||||||
|
|
||||||
|
void spiWaitReady(spi_t * spi);
|
||||||
|
|
||||||
|
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(uint32_t freq);
|
||||||
|
uint32_t spiClockDivToFrequency(uint32_t freq);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* MAIN_ESP32_HAL_SPI_H_ */
|
97
cores/esp32/esp32-hal-time.c
Normal file
97
cores/esp32/esp32-hal-time.c
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
// 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"
|
||||||
|
|
||||||
|
static void setTimeZone(long offset, int daylight)
|
||||||
|
{
|
||||||
|
char cst[17] = {0};
|
||||||
|
char cdt[17] = "DST";
|
||||||
|
char tz[33] = {0};
|
||||||
|
|
||||||
|
if(offset % 3600){
|
||||||
|
sprintf(cst, "UTC%ld:%02u:%02u", offset / 3600, abs((offset % 3600) / 60), abs(offset % 60));
|
||||||
|
} else {
|
||||||
|
sprintf(cst, "UTC%ld", offset / 3600);
|
||||||
|
}
|
||||||
|
if(daylight != 3600){
|
||||||
|
long tz_dst = offset - daylight;
|
||||||
|
if(tz_dst % 3600){
|
||||||
|
sprintf(cdt, "DST%ld:%02u:%02u", tz_dst / 3600, abs((tz_dst % 3600) / 60), abs(tz_dst % 60));
|
||||||
|
} else {
|
||||||
|
sprintf(cdt, "DST%ld", tz_dst / 3600);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sprintf(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();
|
||||||
|
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();
|
||||||
|
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();
|
||||||
|
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();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
269
cores/esp32/esp32-hal-timer.c
Normal file
269
cores/esp32/esp32-hal-timer.c
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
// 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"
|
||||||
|
#include "driver/timer.h"
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
struct {
|
||||||
|
uint32_t reserved0: 10;
|
||||||
|
uint32_t alarm_en: 1; /*When set alarm is enabled*/
|
||||||
|
uint32_t level_int_en: 1; /*When set level type interrupt will be generated during alarm*/
|
||||||
|
uint32_t edge_int_en: 1; /*When set edge type interrupt will be generated during alarm*/
|
||||||
|
uint32_t divider: 16; /*Timer clock (T0/1_clk) pre-scale value.*/
|
||||||
|
uint32_t autoreload: 1; /*When set timer 0/1 auto-reload at alarming is enabled*/
|
||||||
|
uint32_t increase: 1; /*When set timer 0/1 time-base counter increment. When cleared timer 0 time-base counter decrement.*/
|
||||||
|
uint32_t enable: 1; /*When set timer 0/1 time-base counter is enabled*/
|
||||||
|
};
|
||||||
|
uint32_t val;
|
||||||
|
} timer_cfg_t;
|
||||||
|
|
||||||
|
#define NUM_OF_TIMERS SOC_TIMER_GROUP_TOTAL_TIMERS
|
||||||
|
|
||||||
|
typedef struct hw_timer_s
|
||||||
|
{
|
||||||
|
uint8_t group;
|
||||||
|
uint8_t num;
|
||||||
|
} hw_timer_t;
|
||||||
|
|
||||||
|
// Works for all chips
|
||||||
|
static hw_timer_t timer_dev[4] = {
|
||||||
|
{0,0}, {1,0}, {0,1}, {1,1}
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOTE: (in IDF 5.0 there wont be need to know groups/numbers
|
||||||
|
// timer_init() will list thru all timers and return free timer handle)
|
||||||
|
|
||||||
|
|
||||||
|
uint64_t inline timerRead(hw_timer_t *timer){
|
||||||
|
|
||||||
|
uint64_t value;
|
||||||
|
timer_get_counter_value(timer->group, timer->num,&value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t timerAlarmRead(hw_timer_t *timer){
|
||||||
|
uint64_t value;
|
||||||
|
timer_get_alarm_value(timer->group, timer->num, &value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void timerWrite(hw_timer_t *timer, uint64_t val){
|
||||||
|
timer_set_counter_value(timer->group, timer->num, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void timerAlarmWrite(hw_timer_t *timer, uint64_t alarm_value, bool autoreload){
|
||||||
|
timer_set_alarm_value(timer->group, timer->num, alarm_value);
|
||||||
|
timerSetAutoReload(timer,autoreload);
|
||||||
|
}
|
||||||
|
|
||||||
|
void timerSetConfig(hw_timer_t *timer, uint32_t config){
|
||||||
|
timer_cfg_t cfg;
|
||||||
|
cfg.val = config;
|
||||||
|
timer_set_alarm(timer->group, timer->num, cfg.alarm_en);
|
||||||
|
timerSetDivider(timer,cfg.divider);
|
||||||
|
timerSetAutoReload(timer,cfg.autoreload);
|
||||||
|
timerSetCountUp(timer, cfg.increase);
|
||||||
|
|
||||||
|
if (cfg.enable) {
|
||||||
|
timerStart(timer);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
timerStop(timer);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t timerGetConfig(hw_timer_t *timer){
|
||||||
|
timer_config_t timer_cfg;
|
||||||
|
timer_get_config(timer->group, timer->num,&timer_cfg);
|
||||||
|
|
||||||
|
//Translate to default uint32_t
|
||||||
|
timer_cfg_t cfg;
|
||||||
|
cfg.alarm_en = timer_cfg.alarm_en;
|
||||||
|
cfg.autoreload = timer_cfg.auto_reload;
|
||||||
|
cfg.divider = timer_cfg.divider;
|
||||||
|
cfg.edge_int_en = timer_cfg.intr_type;
|
||||||
|
cfg.level_int_en = !timer_cfg.intr_type;
|
||||||
|
cfg.enable = timer_cfg.counter_en;
|
||||||
|
cfg.increase = timer_cfg.counter_dir;
|
||||||
|
|
||||||
|
return cfg.val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void timerSetCountUp(hw_timer_t *timer, bool countUp){
|
||||||
|
timer_set_counter_mode(timer->group, timer->num,countUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool timerGetCountUp(hw_timer_t *timer){
|
||||||
|
timer_cfg_t config;
|
||||||
|
config.val = timerGetConfig(timer);
|
||||||
|
return config.increase;
|
||||||
|
}
|
||||||
|
|
||||||
|
void timerSetAutoReload(hw_timer_t *timer, bool autoreload){
|
||||||
|
timer_set_auto_reload(timer->group, timer->num,autoreload);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool timerGetAutoReload(hw_timer_t *timer){
|
||||||
|
timer_cfg_t config;
|
||||||
|
config.val= timerGetConfig(timer);
|
||||||
|
return config.autoreload;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set divider from 2 to 65535
|
||||||
|
void timerSetDivider(hw_timer_t *timer, uint16_t divider){
|
||||||
|
if(divider < 2)
|
||||||
|
{
|
||||||
|
log_e("Timer divider must be set in range of 2 to 65535");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
timer_set_divider(timer->group, timer->num,divider);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t timerGetDivider(hw_timer_t *timer){
|
||||||
|
timer_cfg_t config;
|
||||||
|
config.val = timerGetConfig(timer);
|
||||||
|
return config.divider;
|
||||||
|
}
|
||||||
|
|
||||||
|
void timerStart(hw_timer_t *timer){
|
||||||
|
timer_start(timer->group, timer->num);
|
||||||
|
}
|
||||||
|
|
||||||
|
void timerStop(hw_timer_t *timer){
|
||||||
|
timer_pause(timer->group, timer->num);
|
||||||
|
}
|
||||||
|
|
||||||
|
void timerRestart(hw_timer_t *timer){
|
||||||
|
timerWrite(timer,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool timerStarted(hw_timer_t *timer){
|
||||||
|
timer_cfg_t config;
|
||||||
|
config.val = timerGetConfig(timer);
|
||||||
|
return config.enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void timerAlarmEnable(hw_timer_t *timer){
|
||||||
|
timer_set_alarm(timer->group, timer->num,true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void timerAlarmDisable(hw_timer_t *timer){
|
||||||
|
timer_set_alarm(timer->group, timer->num,false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool timerAlarmEnabled(hw_timer_t *timer){
|
||||||
|
timer_cfg_t config;
|
||||||
|
config.val = timerGetConfig(timer);
|
||||||
|
return config.alarm_en;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){
|
||||||
|
hw_timer_t * timer = (hw_timer_t *)arg;
|
||||||
|
if(ev_type == APB_BEFORE_CHANGE){
|
||||||
|
timerStop(timer);
|
||||||
|
} else {
|
||||||
|
old_apb /= 1000000;
|
||||||
|
new_apb /= 1000000;
|
||||||
|
uint16_t divider = (new_apb * timerGetDivider(timer)) / old_apb;
|
||||||
|
timerSetDivider(timer,divider);
|
||||||
|
timerStart(timer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hw_timer_t * timerBegin(uint8_t num, uint16_t divider, bool countUp){
|
||||||
|
if(num >= NUM_OF_TIMERS)
|
||||||
|
{
|
||||||
|
log_e("Timer number %u exceeds available number of Timers.", num);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
hw_timer_t * timer = &timer_dev[num]; //Get Timer group/num from 0-3 number
|
||||||
|
|
||||||
|
timer_config_t config = {
|
||||||
|
.divider = divider,
|
||||||
|
.counter_dir = countUp,
|
||||||
|
.counter_en = TIMER_PAUSE,
|
||||||
|
.alarm_en = TIMER_ALARM_DIS,
|
||||||
|
.auto_reload = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
timer_init(timer->group, timer->num, &config);
|
||||||
|
timer_set_counter_value(timer->group, timer->num, 0);
|
||||||
|
timerStart(timer);
|
||||||
|
addApbChangeCallback(timer, _on_apb_change);
|
||||||
|
return timer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void timerEnd(hw_timer_t *timer){
|
||||||
|
removeApbChangeCallback(timer, _on_apb_change);
|
||||||
|
timer_deinit(timer->group, timer->num);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IRAM_ATTR timerFnWrapper(void *arg){
|
||||||
|
void (*fn)(void) = arg;
|
||||||
|
fn();
|
||||||
|
|
||||||
|
// some additional logic or handling may be required here to approriately yield or not
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge){
|
||||||
|
if(edge){
|
||||||
|
log_w("EDGE timer interrupt is not supported! Setting to LEVEL...");
|
||||||
|
}
|
||||||
|
timer_isr_callback_add(timer->group, timer->num, timerFnWrapper, fn, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void timerDetachInterrupt(hw_timer_t *timer){
|
||||||
|
timer_isr_callback_remove(timer->group, timer->num);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t timerReadMicros(hw_timer_t *timer){
|
||||||
|
uint64_t timer_val = timerRead(timer);
|
||||||
|
uint16_t div = timerGetDivider(timer);
|
||||||
|
return timer_val * div / (getApbFrequency() / 1000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t timerReadMilis(hw_timer_t *timer){
|
||||||
|
uint64_t timer_val = timerRead(timer);
|
||||||
|
uint16_t div = timerGetDivider(timer);
|
||||||
|
return timer_val * div / (getApbFrequency() / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
double timerReadSeconds(hw_timer_t *timer){
|
||||||
|
uint64_t timer_val = timerRead(timer);
|
||||||
|
uint16_t div = timerGetDivider(timer);
|
||||||
|
return (double)timer_val * div / getApbFrequency();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t timerAlarmReadMicros(hw_timer_t *timer){
|
||||||
|
uint64_t timer_val = timerAlarmRead(timer);
|
||||||
|
uint16_t div = timerGetDivider(timer);
|
||||||
|
return timer_val * div / (getApbFrequency() / 1000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t timerAlarmReadMilis(hw_timer_t *timer){
|
||||||
|
uint64_t timer_val = timerAlarmRead(timer);
|
||||||
|
uint16_t div = timerGetDivider(timer);
|
||||||
|
return timer_val * div / (getApbFrequency() / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
double timerAlarmReadSeconds(hw_timer_t *timer){
|
||||||
|
uint64_t timer_val = timerAlarmRead(timer);
|
||||||
|
uint16_t div = timerGetDivider(timer);
|
||||||
|
return (double)timer_val * div / getApbFrequency();
|
||||||
|
}
|
73
cores/esp32/esp32-hal-timer.h
Normal file
73
cores/esp32/esp32-hal-timer.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
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_TIMER_H_
|
||||||
|
#define MAIN_ESP32_HAL_TIMER_H_
|
||||||
|
|
||||||
|
#include "esp32-hal.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct hw_timer_s;
|
||||||
|
typedef struct hw_timer_s hw_timer_t;
|
||||||
|
|
||||||
|
hw_timer_t * timerBegin(uint8_t timer, uint16_t divider, bool countUp);
|
||||||
|
void timerEnd(hw_timer_t *timer);
|
||||||
|
|
||||||
|
void timerSetConfig(hw_timer_t *timer, uint32_t config);
|
||||||
|
uint32_t timerGetConfig(hw_timer_t *timer);
|
||||||
|
|
||||||
|
void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge);
|
||||||
|
void timerDetachInterrupt(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);
|
||||||
|
void timerSetDivider(hw_timer_t *timer, uint16_t divider);
|
||||||
|
void timerSetCountUp(hw_timer_t *timer, bool countUp);
|
||||||
|
void timerSetAutoReload(hw_timer_t *timer, bool autoreload);
|
||||||
|
|
||||||
|
bool timerStarted(hw_timer_t *timer);
|
||||||
|
uint64_t timerRead(hw_timer_t *timer);
|
||||||
|
uint64_t timerReadMicros(hw_timer_t *timer);
|
||||||
|
uint64_t timerReadMilis(hw_timer_t *timer);
|
||||||
|
double timerReadSeconds(hw_timer_t *timer);
|
||||||
|
uint16_t timerGetDivider(hw_timer_t *timer);
|
||||||
|
bool timerGetCountUp(hw_timer_t *timer);
|
||||||
|
bool timerGetAutoReload(hw_timer_t *timer);
|
||||||
|
|
||||||
|
void timerAlarmEnable(hw_timer_t *timer);
|
||||||
|
void timerAlarmDisable(hw_timer_t *timer);
|
||||||
|
void timerAlarmWrite(hw_timer_t *timer, uint64_t alarm_value, bool autoreload);
|
||||||
|
|
||||||
|
bool timerAlarmEnabled(hw_timer_t *timer);
|
||||||
|
uint64_t timerAlarmRead(hw_timer_t *timer);
|
||||||
|
uint64_t timerAlarmReadMicros(hw_timer_t *timer);
|
||||||
|
double timerAlarmReadSeconds(hw_timer_t *timer);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* MAIN_ESP32_HAL_TIMER_H_ */
|
776
cores/esp32/esp32-hal-tinyusb.c
Normal file
776
cores/esp32/esp32-hal-tinyusb.c
Normal file
@ -0,0 +1,776 @@
|
|||||||
|
|
||||||
|
#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"
|
||||||
|
#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"
|
||||||
|
#include "soc/periph_defs.h"
|
||||||
|
#include "soc/timer_group_struct.h"
|
||||||
|
#include "soc/system_reg.h"
|
||||||
|
|
||||||
|
#include "hal/usb_hal.h"
|
||||||
|
#include "hal/gpio_ll.h"
|
||||||
|
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "driver/periph_ctrl.h"
|
||||||
|
|
||||||
|
#include "esp_efuse.h"
|
||||||
|
#include "esp_efuse_table.h"
|
||||||
|
#include "esp_rom_gpio.h"
|
||||||
|
|
||||||
|
#include "esp32-hal.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
|
||||||
|
#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"
|
||||||
|
#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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t tinyusb_driver_install(const tinyusb_config_t *config)
|
||||||
|
{
|
||||||
|
usb_hal_context_t hal = {
|
||||||
|
.use_external_phy = config->external_phy
|
||||||
|
};
|
||||||
|
usb_hal_init(&hal);
|
||||||
|
configure_pins(&hal);
|
||||||
|
if (!tusb_init()) {
|
||||||
|
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_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
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Private API
|
||||||
|
* */
|
||||||
|
static bool usb_persist_enabled = false;
|
||||||
|
static restart_type_t usb_persist_mode = RESTART_NO_PERSIST;
|
||||||
|
|
||||||
|
#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((xSemaphoreHandle)arg, &xTaskWoken);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xTaskWoken == pdTRUE) {
|
||||||
|
portYIELD_FROM_ISR();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usb_switch_to_cdc_jtag(){
|
||||||
|
// Disable USB-OTG
|
||||||
|
periph_module_reset(PERIPH_USB_MODULE);
|
||||||
|
//periph_module_enable(PERIPH_USB_MODULE);
|
||||||
|
periph_module_disable(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
|
||||||
|
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;
|
||||||
|
xSemaphoreHandle 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
|
||||||
|
|
||||||
|
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_module_reset(PERIPH_USB_MODULE);
|
||||||
|
periph_module_enable(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void usb_persist_restart(restart_type_t mode)
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool tinyusb_reserve_in_endpoint(uint8_t endpoint){
|
||||||
|
if(endpoint > 6 || (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 > 6 || (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 = 4, active_endpoints = 0;
|
||||||
|
if (tinyusb_loaded_interfaces_mask & BIT(USB_INTERFACE_CDC)) {
|
||||||
|
max_endpoints = 5; //CDC endpoint 0x85 is actually not linked to FIFO and not used
|
||||||
|
}
|
||||||
|
for(uint8_t i=1; i<7; 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 */
|
||||||
|
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);
|
||||||
|
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", "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)
|
||||||
|
{
|
||||||
|
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_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_module_reset(PERIPH_USB_MODULE);
|
||||||
|
periph_module_enable(PERIPH_USB_MODULE);
|
||||||
|
}
|
||||||
|
|
||||||
|
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<7; 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<7; 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<7; 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<7; 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<7; i++){
|
||||||
|
if((tinyusb_endpoints.out & BIT(i)) == 0){
|
||||||
|
tinyusb_endpoints.out |= BIT(i);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_TINYUSB_ENABLED */
|
106
cores/esp32/esp32-hal-tinyusb.h
Normal file
106
cores/esp32/esp32-hal-tinyusb.h
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
// 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 "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
|
||||||
|
|
||||||
|
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_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);
|
||||||
|
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 */
|
273
cores/esp32/esp32-hal-touch.c
Normal file
273
cores/esp32/esp32-hal-touch.c
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
// 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 "soc/soc_caps.h"
|
||||||
|
#if SOC_TOUCH_SENSOR_NUM > 0
|
||||||
|
|
||||||
|
#include "driver/touch_sensor.h"
|
||||||
|
#include "esp32-hal-touch.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Internal Private Touch Data Structure and Functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if SOC_TOUCH_VERSION_1 // ESP32
|
||||||
|
static uint16_t __touchSleepCycles = 0x1000;
|
||||||
|
static uint16_t __touchMeasureCycles = 0x1000;
|
||||||
|
#elif SOC_TOUCH_VERSION_2 // ESP32S2, ESP32S3
|
||||||
|
static uint16_t __touchSleepCycles = TOUCH_PAD_SLEEP_CYCLE_DEFAULT;
|
||||||
|
static uint16_t __touchMeasureCycles = TOUCH_PAD_MEASURE_CYCLE_DEFAULT;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef void (*voidFuncPtr)(void);
|
||||||
|
typedef void (*voidArgFuncPtr)(void *);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
voidFuncPtr fn;
|
||||||
|
bool callWithArgs;
|
||||||
|
void* arg;
|
||||||
|
#if SOC_TOUCH_VERSION_2 // Only for ESP32S2 and ESP32S3
|
||||||
|
bool lastStatusIsPressed;
|
||||||
|
#endif
|
||||||
|
} TouchInterruptHandle_t;
|
||||||
|
|
||||||
|
static TouchInterruptHandle_t __touchInterruptHandlers[SOC_TOUCH_SENSOR_NUM] = {0,};
|
||||||
|
|
||||||
|
static void ARDUINO_ISR_ATTR __touchISR(void * arg)
|
||||||
|
{
|
||||||
|
#if SOC_TOUCH_VERSION_1 // ESP32
|
||||||
|
uint32_t pad_intr = touch_pad_get_status();
|
||||||
|
//clear interrupt
|
||||||
|
touch_pad_clear_status();
|
||||||
|
// call Pad ISR User callback
|
||||||
|
for (int i = 0; i < SOC_TOUCH_SENSOR_NUM; i++) {
|
||||||
|
if ((pad_intr >> i) & 0x01) {
|
||||||
|
if(__touchInterruptHandlers[i].fn){
|
||||||
|
// keeping backward compatibility with "void cb(void)" and with new "void cb(vooid *)"
|
||||||
|
if (__touchInterruptHandlers[i].callWithArgs) {
|
||||||
|
((voidArgFuncPtr)__touchInterruptHandlers[i].fn)(__touchInterruptHandlers[i].arg);
|
||||||
|
} else {
|
||||||
|
__touchInterruptHandlers[i].fn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#elif SOC_TOUCH_VERSION_2 // ESP32S2, ESP32S3
|
||||||
|
touch_pad_intr_mask_t evt = touch_pad_read_intr_status_mask();
|
||||||
|
uint8_t pad_num = touch_pad_get_current_meas_channel();
|
||||||
|
if (evt & TOUCH_PAD_INTR_MASK_ACTIVE) {
|
||||||
|
// touch has been pressed / touched
|
||||||
|
__touchInterruptHandlers[pad_num].lastStatusIsPressed = true;
|
||||||
|
}
|
||||||
|
if (evt & TOUCH_PAD_INTR_MASK_INACTIVE) {
|
||||||
|
// touch has been released / untouched
|
||||||
|
__touchInterruptHandlers[pad_num].lastStatusIsPressed = false;
|
||||||
|
}
|
||||||
|
if(__touchInterruptHandlers[pad_num].fn){
|
||||||
|
// keeping backward compatibility with "void cb(void)" and with new "void cb(vooid *)"
|
||||||
|
if (__touchInterruptHandlers[pad_num].callWithArgs) {
|
||||||
|
((voidArgFuncPtr)__touchInterruptHandlers[pad_num].fn)(__touchInterruptHandlers[pad_num].arg);
|
||||||
|
} else {
|
||||||
|
__touchInterruptHandlers[pad_num].fn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void __touchSetCycles(uint16_t measure, uint16_t sleep)
|
||||||
|
{
|
||||||
|
__touchSleepCycles = sleep;
|
||||||
|
__touchMeasureCycles = measure;
|
||||||
|
touch_pad_set_meas_time(sleep, measure);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void __touchInit()
|
||||||
|
{
|
||||||
|
static bool initialized = false;
|
||||||
|
if(initialized){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t err = ESP_OK;
|
||||||
|
|
||||||
|
#if SOC_TOUCH_VERSION_1 // ESP32
|
||||||
|
err = touch_pad_init();
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
// the next two lines will drive the touch reading values -- both will return ESP_OK
|
||||||
|
touch_pad_set_voltage(TOUCH_HVOLT_2V7, TOUCH_LVOLT_0V5, TOUCH_HVOLT_ATTEN_0V);
|
||||||
|
touch_pad_set_meas_time(__touchMeasureCycles, __touchSleepCycles);
|
||||||
|
// Touch Sensor Timer initiated
|
||||||
|
touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER); // returns ESP_OK
|
||||||
|
err = touch_pad_filter_start(10);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
// keep ISR activated - it can run all together (ISR + touchRead())
|
||||||
|
err = touch_pad_isr_register(__touchISR, NULL);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
touch_pad_intr_enable(); // returns ESP_OK
|
||||||
|
#elif SOC_TOUCH_VERSION_2 // ESP32S2, ESP32S3
|
||||||
|
err = touch_pad_init();
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
// the next lines will drive the touch reading values -- all os them return ESP_OK
|
||||||
|
touch_pad_set_meas_time(__touchSleepCycles, __touchMeasureCycles);
|
||||||
|
touch_pad_set_voltage(TOUCH_PAD_HIGH_VOLTAGE_THRESHOLD, TOUCH_PAD_LOW_VOLTAGE_THRESHOLD, TOUCH_PAD_ATTEN_VOLTAGE_THRESHOLD);
|
||||||
|
touch_pad_set_idle_channel_connect(TOUCH_PAD_IDLE_CH_CONNECT_DEFAULT);
|
||||||
|
touch_pad_denoise_t denoise = {
|
||||||
|
.grade = TOUCH_PAD_DENOISE_BIT4,
|
||||||
|
.cap_level = TOUCH_PAD_DENOISE_CAP_L4,
|
||||||
|
};
|
||||||
|
touch_pad_denoise_set_config(&denoise);
|
||||||
|
touch_pad_denoise_enable();
|
||||||
|
// Touch Sensor Timer initiated
|
||||||
|
touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER); // returns ESP_OK
|
||||||
|
touch_pad_fsm_start(); // returns ESP_OK
|
||||||
|
//ISR setup moved to __touchChannelInit
|
||||||
|
#endif
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
|
return;
|
||||||
|
err:
|
||||||
|
log_e(" Touch sensor initialization error.");
|
||||||
|
initialized = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __touchChannelInit(int pad)
|
||||||
|
{
|
||||||
|
static bool channels_initialized[SOC_TOUCH_SENSOR_NUM] = { false };
|
||||||
|
if(channels_initialized[pad]){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if SOC_TOUCH_VERSION_1 // ESP32
|
||||||
|
// Initial no Threshold and setup
|
||||||
|
__touchInterruptHandlers[pad].fn = NULL;
|
||||||
|
touch_pad_config(pad, SOC_TOUCH_PAD_THRESHOLD_MAX); // returns ESP_OK
|
||||||
|
#elif SOC_TOUCH_VERSION_2 // ESP32S2, ESP32S3
|
||||||
|
// Initial no Threshold and setup
|
||||||
|
__touchInterruptHandlers[pad].fn = NULL;
|
||||||
|
touch_pad_config(pad); // returns ESP_OK
|
||||||
|
// keep ISR activated - it can run all together (ISR + touchRead())
|
||||||
|
esp_err_t err = touch_pad_isr_register(__touchISR, NULL, TOUCH_PAD_INTR_MASK_ACTIVE | TOUCH_PAD_INTR_MASK_INACTIVE);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
log_e(" Touch sensor initialization error.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
touch_pad_intr_enable(TOUCH_PAD_INTR_MASK_ACTIVE | TOUCH_PAD_INTR_MASK_INACTIVE); // returns ESP_OK
|
||||||
|
#endif
|
||||||
|
|
||||||
|
channels_initialized[pad] = true;
|
||||||
|
delay(20); //delay needed before reading from touch channel after config
|
||||||
|
}
|
||||||
|
|
||||||
|
static touch_value_t __touchRead(uint8_t pin)
|
||||||
|
{
|
||||||
|
int8_t pad = digitalPinToTouchChannel(pin);
|
||||||
|
if(pad < 0){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
__touchInit();
|
||||||
|
__touchChannelInit(pad);
|
||||||
|
|
||||||
|
touch_value_t touch_value;
|
||||||
|
touch_pad_read_raw_data(pad, &touch_value);
|
||||||
|
|
||||||
|
return touch_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __touchConfigInterrupt(uint8_t pin, void (*userFunc)(void), void *Args, touch_value_t threshold, bool callWithArgs)
|
||||||
|
{
|
||||||
|
int8_t pad = digitalPinToTouchChannel(pin);
|
||||||
|
if(pad < 0){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userFunc == NULL) {
|
||||||
|
// dettach ISR User Call
|
||||||
|
__touchInterruptHandlers[pad].fn = NULL;
|
||||||
|
threshold = SOC_TOUCH_PAD_THRESHOLD_MAX; // deactivate the ISR with SOC_TOUCH_PAD_THRESHOLD_MAX
|
||||||
|
} else {
|
||||||
|
// attach ISR User Call
|
||||||
|
__touchInit();
|
||||||
|
__touchChannelInit(pad);
|
||||||
|
__touchInterruptHandlers[pad].fn = userFunc;
|
||||||
|
__touchInterruptHandlers[pad].callWithArgs = callWithArgs;
|
||||||
|
__touchInterruptHandlers[pad].arg = Args;
|
||||||
|
}
|
||||||
|
|
||||||
|
touch_pad_set_thresh(pad, threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
// it keeps backwards compatibility
|
||||||
|
static void __touchAttachInterrupt(uint8_t pin, void (*userFunc)(void), touch_value_t threshold)
|
||||||
|
{
|
||||||
|
__touchConfigInterrupt(pin, userFunc, NULL, threshold, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, threshold, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// new additional API to dettach touch ISR
|
||||||
|
static void __touchDettachInterrupt(uint8_t pin)
|
||||||
|
{
|
||||||
|
__touchConfigInterrupt(pin, NULL, NULL, 0, false); // userFunc as NULL acts as dettaching
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
External Public Touch API Functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if SOC_TOUCH_VERSION_1 // Only for ESP32 SoC
|
||||||
|
void touchInterruptSetThresholdDirection(bool mustbeLower) {
|
||||||
|
if (mustbeLower) {
|
||||||
|
touch_pad_set_trigger_mode(TOUCH_TRIGGER_BELOW);
|
||||||
|
} else {
|
||||||
|
touch_pad_set_trigger_mode(TOUCH_TRIGGER_ABOVE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#elif SOC_TOUCH_VERSION_2 // Only for ESP32S2 and ESP32S3
|
||||||
|
// returns true if touch pad has been and continues pressed and false otherwise
|
||||||
|
bool touchInterruptGetLastStatus(uint8_t pin) {
|
||||||
|
int8_t pad = digitalPinToTouchChannel(pin);
|
||||||
|
if(pad < 0){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return __touchInterruptHandlers[pad].lastStatusIsPressed;
|
||||||
|
}
|
||||||
|
#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")));
|
||||||
|
extern void touchSetCycles(uint16_t, uint16_t) __attribute__ ((weak, alias("__touchSetCycles")));
|
||||||
|
|
||||||
|
#endif // #if SOC_TOUCH_SENSOR_NUM > 0
|
96
cores/esp32/esp32-hal-touch.h
Normal file
96
cores/esp32/esp32-hal-touch.h
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
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_TOUCH_H_
|
||||||
|
#define MAIN_ESP32_HAL_TOUCH_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
|
#include "esp32-hal.h"
|
||||||
|
|
||||||
|
#if SOC_TOUCH_SENSOR_NUM > 0
|
||||||
|
|
||||||
|
#if !defined(SOC_TOUCH_VERSION_1) && !defined(SOC_TOUCH_VERSION_2)
|
||||||
|
#error Touch IDF driver Not supported!
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SOC_TOUCH_VERSION_1 // ESP32
|
||||||
|
typedef uint16_t touch_value_t;
|
||||||
|
#elif SOC_TOUCH_VERSION_2 // ESP32S2 ESP32S3
|
||||||
|
typedef uint32_t touch_value_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set cycles that measurement operation takes
|
||||||
|
* The result from touchRead, threshold and detection
|
||||||
|
* accuracy depend on these values. Defaults are
|
||||||
|
* 0x1000 for measure and 0x1000 for sleep.
|
||||||
|
* With default values touchRead takes 0.5ms
|
||||||
|
* */
|
||||||
|
void touchSetCycles(uint16_t measure, uint16_t sleep);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read touch pad (values close to 0 mean touch detected)
|
||||||
|
* You can use this method to chose a good threshold value
|
||||||
|
* to use as value for touchAttachInterrupt
|
||||||
|
* */
|
||||||
|
touch_value_t touchRead(uint8_t pin);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set function to be called if touch pad value falls (ESP32)
|
||||||
|
* below the given threshold / rises (ESP32-S2/S3) by given increment (threshold).
|
||||||
|
* Use touchRead to determine a proper threshold between touched and untouched state
|
||||||
|
* */
|
||||||
|
void touchAttachInterrupt(uint8_t pin, void (*userFunc)(void), touch_value_t threshold);
|
||||||
|
void touchAttachInterruptArg(uint8_t pin, void (*userFunc)(void*), void *arg, touch_value_t threshold);
|
||||||
|
void touchDetachInterrupt(uint8_t pin);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Specific functions to ESP32
|
||||||
|
* Tells the driver if it shall activate the ISR if the sensor is Lower or Higher than the Threshold
|
||||||
|
* Default if Lower.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#if SOC_TOUCH_VERSION_1 // Only for ESP32 SoC
|
||||||
|
void touchInterruptSetThresholdDirection(bool mustbeLower);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Specific functions to ESP32-S2 and ESP32-S3
|
||||||
|
* Returns true when the latest ISR status for the Touchpad is that it is touched (Active)
|
||||||
|
* and false when the Touchpad is untoouched (Inactive)
|
||||||
|
* This function can be used in conjunction with ISR User callback in order to take action
|
||||||
|
* as soon as the touchpad is touched and/or released
|
||||||
|
**/
|
||||||
|
|
||||||
|
#if SOC_TOUCH_VERSION_2 // Only for ESP32S2 and ESP32S3
|
||||||
|
// returns true if touch pad has been and continues pressed and false otherwise
|
||||||
|
bool touchInterruptGetLastStatus(uint8_t pin);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // SOC_TOUCH_SENSOR_NUM > 0
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif /* MAIN_ESP32_HAL_TOUCH_H_ */
|
666
cores/esp32/esp32-hal-uart.c
Normal file
666
cores/esp32/esp32-hal-uart.c
Normal file
@ -0,0 +1,666 @@
|
|||||||
|
// 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-uart.h"
|
||||||
|
#include "esp32-hal.h"
|
||||||
|
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
|
|
||||||
|
#include "driver/uart.h"
|
||||||
|
#include "hal/uart_ll.h"
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
|
#include "soc/uart_struct.h"
|
||||||
|
|
||||||
|
static int s_uart_debug_nr = 0;
|
||||||
|
|
||||||
|
struct uart_struct_t {
|
||||||
|
|
||||||
|
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||||
|
xSemaphoreHandle lock;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint8_t num;
|
||||||
|
bool has_peek;
|
||||||
|
uint8_t peek_byte;
|
||||||
|
QueueHandle_t uart_event_queue; // export it by some uartGetEventQueue() function
|
||||||
|
};
|
||||||
|
|
||||||
|
#if CONFIG_DISABLE_HAL_LOCKS
|
||||||
|
|
||||||
|
#define UART_MUTEX_LOCK()
|
||||||
|
#define UART_MUTEX_UNLOCK()
|
||||||
|
|
||||||
|
static uart_t _uart_bus_array[] = {
|
||||||
|
{0, false, 0, NULL},
|
||||||
|
#if SOC_UART_NUM > 1
|
||||||
|
{1, false, 0, NULL},
|
||||||
|
#endif
|
||||||
|
#if SOC_UART_NUM > 2
|
||||||
|
{2, false, 0, NULL},
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define UART_MUTEX_LOCK() do {} while (xSemaphoreTake(uart->lock, portMAX_DELAY) != pdPASS)
|
||||||
|
#define UART_MUTEX_UNLOCK() xSemaphoreGive(uart->lock)
|
||||||
|
|
||||||
|
static uart_t _uart_bus_array[] = {
|
||||||
|
{NULL, 0, false, 0, NULL},
|
||||||
|
#if SOC_UART_NUM > 1
|
||||||
|
{NULL, 1, false, 0, NULL},
|
||||||
|
#endif
|
||||||
|
#if SOC_UART_NUM > 2
|
||||||
|
{NULL, 2, false, 0, NULL},
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// solves issue https://github.com/espressif/arduino-esp32/issues/6032
|
||||||
|
// baudrate must be multiplied when CPU Frequency is lower than APB 80MHz
|
||||||
|
uint32_t _get_effective_baudrate(uint32_t baudrate)
|
||||||
|
{
|
||||||
|
uint32_t Freq = getApbFrequency()/1000000;
|
||||||
|
if (Freq < 80) {
|
||||||
|
return 80 / Freq * baudrate;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return baudrate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Routines that take care of UART events will be in the HardwareSerial Class code
|
||||||
|
void uartGetEventQueue(uart_t* uart, QueueHandle_t *q)
|
||||||
|
{
|
||||||
|
// passing back NULL for the Queue pointer when UART is not initialized yet
|
||||||
|
*q = NULL;
|
||||||
|
if(uart == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*q = uart->uart_event_queue;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool uartIsDriverInstalled(uart_t* uart)
|
||||||
|
{
|
||||||
|
if(uart == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uart_is_driver_installed(uart->num)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid pin UART_PIN_NO_CHANGE is defined to (-1)
|
||||||
|
// Negative Pin Number will keep it unmodified, thus this function can set individual pins
|
||||||
|
void uartSetPins(uart_t* uart, int8_t rxPin, int8_t txPin, int8_t ctsPin, int8_t rtsPin)
|
||||||
|
{
|
||||||
|
if(uart == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UART_MUTEX_LOCK();
|
||||||
|
// IDF uart_set_pin() will issue necessary Error Message and take care of all GPIO Number validation.
|
||||||
|
uart_set_pin(uart->num, txPin, rxPin, rtsPin, ctsPin);
|
||||||
|
UART_MUTEX_UNLOCK();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
void uartSetHwFlowCtrlMode(uart_t *uart, uint8_t mode, uint8_t threshold) {
|
||||||
|
if(uart == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// IDF will issue corresponding error message when mode or threshold are wrong and prevent crashing
|
||||||
|
// IDF will check (mode > HW_FLOWCTRL_CTS_RTS || threshold >= SOC_UART_FIFO_LEN)
|
||||||
|
uart_set_hw_flow_ctrl(uart->num, (uart_hw_flowcontrol_t) mode, threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rxPin, int8_t txPin, uint16_t rx_buffer_size, uint16_t tx_buffer_size, bool inverted, uint8_t rxfifo_full_thrhd)
|
||||||
|
{
|
||||||
|
if(uart_nr >= SOC_UART_NUM) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uart_t* uart = &_uart_bus_array[uart_nr];
|
||||||
|
|
||||||
|
if (uart_is_driver_installed(uart_nr)) {
|
||||||
|
uartEnd(uart);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||||
|
if(uart->lock == NULL) {
|
||||||
|
uart->lock = xSemaphoreCreateMutex();
|
||||||
|
if(uart->lock == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
UART_MUTEX_LOCK();
|
||||||
|
|
||||||
|
uart_config_t uart_config;
|
||||||
|
uart_config.baud_rate = _get_effective_baudrate(baudrate);
|
||||||
|
uart_config.data_bits = (config & 0xc) >> 2;
|
||||||
|
uart_config.parity = (config & 0x3);
|
||||||
|
uart_config.stop_bits = (config & 0x30) >> 4;
|
||||||
|
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
|
||||||
|
uart_config.rx_flow_ctrl_thresh = rxfifo_full_thrhd;
|
||||||
|
uart_config.source_clk = UART_SCLK_APB;
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(uart_driver_install(uart_nr, rx_buffer_size, tx_buffer_size, 20, &(uart->uart_event_queue), 0));
|
||||||
|
ESP_ERROR_CHECK(uart_param_config(uart_nr, &uart_config));
|
||||||
|
ESP_ERROR_CHECK(uart_set_pin(uart_nr, txPin, rxPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
|
||||||
|
|
||||||
|
// Is it right or the idea is to swap rx and tx pins?
|
||||||
|
if (inverted) {
|
||||||
|
// invert signal for both Rx and Tx
|
||||||
|
ESP_ERROR_CHECK(uart_set_line_inverse(uart_nr, UART_SIGNAL_TXD_INV | UART_SIGNAL_RXD_INV));
|
||||||
|
}
|
||||||
|
|
||||||
|
UART_MUTEX_UNLOCK();
|
||||||
|
|
||||||
|
uartFlush(uart);
|
||||||
|
return uart;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This code is under testing - for now just keep it here
|
||||||
|
void uartSetFastReading(uart_t* uart)
|
||||||
|
{
|
||||||
|
if(uart == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UART_MUTEX_LOCK();
|
||||||
|
// override default RX IDF Driver Interrupt - no BREAK, PARITY or OVERFLOW
|
||||||
|
uart_intr_config_t uart_intr = {
|
||||||
|
.intr_enable_mask = UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT, // only these IRQs - no BREAK, PARITY or OVERFLOW
|
||||||
|
.rx_timeout_thresh = 1,
|
||||||
|
.txfifo_empty_intr_thresh = 10,
|
||||||
|
.rxfifo_full_thresh = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(uart_intr_config(uart->num, &uart_intr));
|
||||||
|
UART_MUTEX_UNLOCK();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void uartSetRxTimeout(uart_t* uart, uint8_t numSymbTimeout)
|
||||||
|
{
|
||||||
|
if(uart == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UART_MUTEX_LOCK();
|
||||||
|
uart_set_rx_timeout(uart->num, numSymbTimeout);
|
||||||
|
UART_MUTEX_UNLOCK();
|
||||||
|
}
|
||||||
|
|
||||||
|
void uartSetRxFIFOFull(uart_t* uart, uint8_t numBytesFIFOFull)
|
||||||
|
{
|
||||||
|
if(uart == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UART_MUTEX_LOCK();
|
||||||
|
uart_set_rx_full_threshold(uart->num, numBytesFIFOFull);
|
||||||
|
UART_MUTEX_UNLOCK();
|
||||||
|
}
|
||||||
|
|
||||||
|
void uartEnd(uart_t* uart)
|
||||||
|
{
|
||||||
|
if(uart == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UART_MUTEX_LOCK();
|
||||||
|
uart_driver_delete(uart->num);
|
||||||
|
UART_MUTEX_UNLOCK();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void uartSetRxInvert(uart_t* uart, bool invert)
|
||||||
|
{
|
||||||
|
if (uart == NULL)
|
||||||
|
return;
|
||||||
|
#if 0
|
||||||
|
// POTENTIAL ISSUE :: original code only set/reset rxd_inv bit
|
||||||
|
// IDF or LL set/reset the whole inv_mask!
|
||||||
|
if (invert)
|
||||||
|
ESP_ERROR_CHECK(uart_set_line_inverse(uart->num, UART_SIGNAL_RXD_INV));
|
||||||
|
else
|
||||||
|
ESP_ERROR_CHECK(uart_set_line_inverse(uart->num, UART_SIGNAL_INV_DISABLE));
|
||||||
|
|
||||||
|
#else
|
||||||
|
// this implementation is better over IDF API because it only affects RXD
|
||||||
|
// this is supported in ESP32, ESP32-S2 and ESP32-C3
|
||||||
|
uart_dev_t *hw = UART_LL_GET_HW(uart->num);
|
||||||
|
if (invert)
|
||||||
|
hw->conf0.rxd_inv = 1;
|
||||||
|
else
|
||||||
|
hw->conf0.rxd_inv = 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t uartAvailable(uart_t* uart)
|
||||||
|
{
|
||||||
|
|
||||||
|
if(uart == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
UART_MUTEX_LOCK();
|
||||||
|
size_t available;
|
||||||
|
uart_get_buffered_data_len(uart->num, &available);
|
||||||
|
if (uart->has_peek) available++;
|
||||||
|
UART_MUTEX_UNLOCK();
|
||||||
|
return available;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t uartAvailableForWrite(uart_t* uart)
|
||||||
|
{
|
||||||
|
if(uart == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
UART_MUTEX_LOCK();
|
||||||
|
uint32_t available = uart_ll_get_txfifo_len(UART_LL_GET_HW(uart->num));
|
||||||
|
size_t txRingBufferAvailable = 0;
|
||||||
|
if (ESP_OK == uart_get_tx_buffer_free_size(uart->num, &txRingBufferAvailable)) {
|
||||||
|
available += txRingBufferAvailable;
|
||||||
|
}
|
||||||
|
UART_MUTEX_UNLOCK();
|
||||||
|
return available;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t uartRead(uart_t* uart)
|
||||||
|
{
|
||||||
|
if(uart == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
uint8_t c = 0;
|
||||||
|
|
||||||
|
UART_MUTEX_LOCK();
|
||||||
|
|
||||||
|
if (uart->has_peek) {
|
||||||
|
uart->has_peek = false;
|
||||||
|
c = uart->peek_byte;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
int len = uart_read_bytes(uart->num, &c, 1, 20 / portTICK_RATE_MS);
|
||||||
|
if (len == 0) {
|
||||||
|
c = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UART_MUTEX_UNLOCK();
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t uartPeek(uart_t* uart)
|
||||||
|
{
|
||||||
|
if(uart == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
uint8_t c = 0;
|
||||||
|
|
||||||
|
UART_MUTEX_LOCK();
|
||||||
|
|
||||||
|
if (uart->has_peek) {
|
||||||
|
c = uart->peek_byte;
|
||||||
|
} else {
|
||||||
|
int len = uart_read_bytes(uart->num, &c, 1, 20 / portTICK_RATE_MS);
|
||||||
|
if (len == 0) {
|
||||||
|
c = 0;
|
||||||
|
} else {
|
||||||
|
uart->has_peek = true;
|
||||||
|
uart->peek_byte = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UART_MUTEX_UNLOCK();
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uartWrite(uart_t* uart, uint8_t c)
|
||||||
|
{
|
||||||
|
if(uart == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UART_MUTEX_LOCK();
|
||||||
|
uart_write_bytes(uart->num, &c, 1);
|
||||||
|
UART_MUTEX_UNLOCK();
|
||||||
|
}
|
||||||
|
|
||||||
|
void uartWriteBuf(uart_t* uart, const uint8_t * data, size_t len)
|
||||||
|
{
|
||||||
|
if(uart == NULL || data == NULL || !len) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UART_MUTEX_LOCK();
|
||||||
|
uart_write_bytes(uart->num, data, len);
|
||||||
|
UART_MUTEX_UNLOCK();
|
||||||
|
}
|
||||||
|
|
||||||
|
void uartFlush(uart_t* uart)
|
||||||
|
{
|
||||||
|
uartFlushTxOnly(uart, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uartFlushTxOnly(uart_t* uart, bool txOnly)
|
||||||
|
{
|
||||||
|
if(uart == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UART_MUTEX_LOCK();
|
||||||
|
while(!uart_ll_is_tx_idle(UART_LL_GET_HW(uart->num)));
|
||||||
|
|
||||||
|
if ( !txOnly ) {
|
||||||
|
ESP_ERROR_CHECK(uart_flush_input(uart->num));
|
||||||
|
}
|
||||||
|
UART_MUTEX_UNLOCK();
|
||||||
|
}
|
||||||
|
|
||||||
|
void uartSetBaudRate(uart_t* uart, uint32_t baud_rate)
|
||||||
|
{
|
||||||
|
if(uart == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UART_MUTEX_LOCK();
|
||||||
|
uart_ll_set_baudrate(UART_LL_GET_HW(uart->num), _get_effective_baudrate(baud_rate));
|
||||||
|
UART_MUTEX_UNLOCK();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t uartGetBaudRate(uart_t* uart)
|
||||||
|
{
|
||||||
|
if(uart == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
UART_MUTEX_LOCK();
|
||||||
|
uint32_t baud_rate = uart_ll_get_baudrate(UART_LL_GET_HW(uart->num));
|
||||||
|
UART_MUTEX_UNLOCK();
|
||||||
|
return baud_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ARDUINO_ISR_ATTR uart0_write_char(char c)
|
||||||
|
{
|
||||||
|
while (uart_ll_get_txfifo_len(&UART0) == 0);
|
||||||
|
uart_ll_write_txfifo(&UART0, (const uint8_t *) &c, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if SOC_UART_NUM > 1
|
||||||
|
static void ARDUINO_ISR_ATTR uart1_write_char(char c)
|
||||||
|
{
|
||||||
|
while (uart_ll_get_txfifo_len(&UART1) == 0);
|
||||||
|
uart_ll_write_txfifo(&UART1, (const uint8_t *) &c, 1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SOC_UART_NUM > 2
|
||||||
|
static void ARDUINO_ISR_ATTR uart2_write_char(char c)
|
||||||
|
{
|
||||||
|
while (uart_ll_get_txfifo_len(&UART2) == 0);
|
||||||
|
uart_ll_write_txfifo(&UART2, (const uint8_t *) &c, 1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void uart_install_putc()
|
||||||
|
{
|
||||||
|
switch(s_uart_debug_nr) {
|
||||||
|
case 0:
|
||||||
|
ets_install_putc1((void (*)(char)) &uart0_write_char);
|
||||||
|
break;
|
||||||
|
#if SOC_UART_NUM > 1
|
||||||
|
case 1:
|
||||||
|
ets_install_putc1((void (*)(char)) &uart1_write_char);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
#if SOC_UART_NUM > 2
|
||||||
|
case 2:
|
||||||
|
ets_install_putc1((void (*)(char)) &uart2_write_char);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
ets_install_putc1(NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void uartSetDebug(uart_t* uart)
|
||||||
|
{
|
||||||
|
if(uart == NULL || uart->num >= SOC_UART_NUM) {
|
||||||
|
s_uart_debug_nr = -1;
|
||||||
|
} else {
|
||||||
|
s_uart_debug_nr = uart->num;
|
||||||
|
}
|
||||||
|
uart_install_putc();
|
||||||
|
}
|
||||||
|
|
||||||
|
int uartGetDebug()
|
||||||
|
{
|
||||||
|
return s_uart_debug_nr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int log_printf(const char *format, ...)
|
||||||
|
{
|
||||||
|
static char loc_buf[64];
|
||||||
|
char * temp = loc_buf;
|
||||||
|
int len;
|
||||||
|
va_list arg;
|
||||||
|
va_list copy;
|
||||||
|
va_start(arg, format);
|
||||||
|
va_copy(copy, arg);
|
||||||
|
len = vsnprintf(NULL, 0, format, copy);
|
||||||
|
va_end(copy);
|
||||||
|
if(len >= sizeof(loc_buf)){
|
||||||
|
temp = (char*)malloc(len+1);
|
||||||
|
if(temp == NULL) {
|
||||||
|
va_end(arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||||
|
if(s_uart_debug_nr != -1 && _uart_bus_array[s_uart_debug_nr].lock){
|
||||||
|
xSemaphoreTake(_uart_bus_array[s_uart_debug_nr].lock, portMAX_DELAY);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
vsnprintf(temp, len+1, format, arg);
|
||||||
|
ets_printf("%s", temp);
|
||||||
|
|
||||||
|
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||||
|
if(s_uart_debug_nr != -1 && _uart_bus_array[s_uart_debug_nr].lock){
|
||||||
|
xSemaphoreGive(_uart_bus_array[s_uart_debug_nr].lock);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
va_end(arg);
|
||||||
|
if(len >= sizeof(loc_buf)){
|
||||||
|
free(temp);
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void log_print_buf_line(const uint8_t *b, size_t len, size_t total_len){
|
||||||
|
for(size_t i = 0; i<len; i++){
|
||||||
|
log_printf("%s0x%02x,",i?" ":"", b[i]);
|
||||||
|
}
|
||||||
|
if(total_len > 16){
|
||||||
|
for(size_t i = len; i<16; i++){
|
||||||
|
log_printf(" ");
|
||||||
|
}
|
||||||
|
log_printf(" // ");
|
||||||
|
} else {
|
||||||
|
log_printf(" // ");
|
||||||
|
}
|
||||||
|
for(size_t i = 0; i<len; i++){
|
||||||
|
log_printf("%c",((b[i] >= 0x20) && (b[i] < 0x80))?b[i]:'.');
|
||||||
|
}
|
||||||
|
log_printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_print_buf(const uint8_t *b, size_t len){
|
||||||
|
if(!len || !b){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for(size_t i = 0; i<len; i+=16){
|
||||||
|
if(len > 16){
|
||||||
|
log_printf("/* 0x%04X */ ", i);
|
||||||
|
}
|
||||||
|
log_print_buf_line(b+i, ((len-i)<16)?(len - i):16, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if enough pulses are detected return the minimum high pulse duration + minimum low pulse duration divided by two.
|
||||||
|
* This equals one bit period. If flag is true the function return inmediately, otherwise it waits for enough pulses.
|
||||||
|
*/
|
||||||
|
unsigned long uartBaudrateDetect(uart_t *uart, bool flg)
|
||||||
|
{
|
||||||
|
#ifndef CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
if(uart == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uart_dev_t *hw = UART_LL_GET_HW(uart->num);
|
||||||
|
|
||||||
|
while(hw->rxd_cnt.edge_cnt < 30) { // UART_PULSE_NUM(uart_num)
|
||||||
|
if(flg) return 0;
|
||||||
|
ets_delay_us(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
UART_MUTEX_LOCK();
|
||||||
|
//log_i("lowpulse_min_cnt = %d hightpulse_min_cnt = %d", hw->lowpulse.min_cnt, hw->highpulse.min_cnt);
|
||||||
|
unsigned long ret = ((hw->lowpulse.min_cnt + hw->highpulse.min_cnt) >> 1);
|
||||||
|
UART_MUTEX_UNLOCK();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To start detection of baud rate with the uart the auto_baud.en bit needs to be cleared and set. The bit period is
|
||||||
|
* detected calling uartBadrateDetect(). The raw baudrate is computed using the UART_CLK_FREQ. The raw baudrate is
|
||||||
|
* rounded to the closed real baudrate.
|
||||||
|
*
|
||||||
|
* ESP32-C3 reports wrong baud rate detection as shown below:
|
||||||
|
*
|
||||||
|
* This will help in a future recall for the C3.
|
||||||
|
* Baud Sent: Baud Read:
|
||||||
|
* 300 --> 19536
|
||||||
|
* 2400 --> 19536
|
||||||
|
* 4800 --> 19536
|
||||||
|
* 9600 --> 28818
|
||||||
|
* 19200 --> 57678
|
||||||
|
* 38400 --> 115440
|
||||||
|
* 57600 --> 173535
|
||||||
|
* 115200 --> 347826
|
||||||
|
* 230400 --> 701754
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void uartStartDetectBaudrate(uart_t *uart) {
|
||||||
|
if(uart == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_IDF_TARGET_ESP32C3
|
||||||
|
|
||||||
|
// ESP32-C3 requires further testing
|
||||||
|
// Baud rate detection returns wrong values
|
||||||
|
|
||||||
|
log_e("ESP32-C3 baud rate detection is not supported.");
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Code bellow for C3 kept for future recall
|
||||||
|
//hw->rx_filt.glitch_filt = 0x08;
|
||||||
|
//hw->rx_filt.glitch_filt_en = 1;
|
||||||
|
//hw->conf0.autobaud_en = 0;
|
||||||
|
//hw->conf0.autobaud_en = 1;
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
#else
|
||||||
|
uart_dev_t *hw = UART_LL_GET_HW(uart->num);
|
||||||
|
hw->auto_baud.glitch_filt = 0x08;
|
||||||
|
hw->auto_baud.en = 0;
|
||||||
|
hw->auto_baud.en = 1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long
|
||||||
|
uartDetectBaudrate(uart_t *uart)
|
||||||
|
{
|
||||||
|
if(uart == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef CONFIG_IDF_TARGET_ESP32C3 // ESP32-C3 requires further testing - Baud rate detection returns wrong values
|
||||||
|
|
||||||
|
static bool uartStateDetectingBaudrate = false;
|
||||||
|
|
||||||
|
if(!uartStateDetectingBaudrate) {
|
||||||
|
uartStartDetectBaudrate(uart);
|
||||||
|
uartStateDetectingBaudrate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long divisor = uartBaudrateDetect(uart, true);
|
||||||
|
if (!divisor) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// log_i(...) below has been used to check C3 baud rate detection results
|
||||||
|
//log_i("Divisor = %d\n", divisor);
|
||||||
|
//log_i("BAUD RATE based on Positive Pulse %d\n", getApbFrequency()/((hw->pospulse.min_cnt + 1)/2));
|
||||||
|
//log_i("BAUD RATE based on Negative Pulse %d\n", getApbFrequency()/((hw->negpulse.min_cnt + 1)/2));
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_IDF_TARGET_ESP32C3
|
||||||
|
//hw->conf0.autobaud_en = 0;
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
#else
|
||||||
|
uart_dev_t *hw = UART_LL_GET_HW(uart->num);
|
||||||
|
hw->auto_baud.en = 0;
|
||||||
|
#endif
|
||||||
|
uartStateDetectingBaudrate = false; // Initialize for the next round
|
||||||
|
|
||||||
|
unsigned long baudrate = getApbFrequency() / divisor;
|
||||||
|
//log_i("APB_FREQ = %d\nraw baudrate detected = %d", getApbFrequency(), baudrate);
|
||||||
|
|
||||||
|
static const unsigned long default_rates[] = {300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 74880, 115200, 230400, 256000, 460800, 921600, 1843200, 3686400};
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
for (i = 1; i < sizeof(default_rates) / sizeof(default_rates[0]) - 1; i++) // find the nearest real baudrate
|
||||||
|
{
|
||||||
|
if (baudrate <= default_rates[i])
|
||||||
|
{
|
||||||
|
if (baudrate - default_rates[i - 1] < default_rates[i] - baudrate) {
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return default_rates[i];
|
||||||
|
#else
|
||||||
|
log_e("ESP32-C3 baud rate detection is not supported.");
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
108
cores/esp32/esp32-hal-uart.h
Normal file
108
cores/esp32/esp32-hal-uart.h
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
// 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_UART_H_
|
||||||
|
#define MAIN_ESP32_HAL_UART_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/queue.h"
|
||||||
|
|
||||||
|
#define SERIAL_5N1 0x8000010
|
||||||
|
#define SERIAL_6N1 0x8000014
|
||||||
|
#define SERIAL_7N1 0x8000018
|
||||||
|
#define SERIAL_8N1 0x800001c
|
||||||
|
#define SERIAL_5N2 0x8000030
|
||||||
|
#define SERIAL_6N2 0x8000034
|
||||||
|
#define SERIAL_7N2 0x8000038
|
||||||
|
#define SERIAL_8N2 0x800003c
|
||||||
|
#define SERIAL_5E1 0x8000012
|
||||||
|
#define SERIAL_6E1 0x8000016
|
||||||
|
#define SERIAL_7E1 0x800001a
|
||||||
|
#define SERIAL_8E1 0x800001e
|
||||||
|
#define SERIAL_5E2 0x8000032
|
||||||
|
#define SERIAL_6E2 0x8000036
|
||||||
|
#define SERIAL_7E2 0x800003a
|
||||||
|
#define SERIAL_8E2 0x800003e
|
||||||
|
#define SERIAL_5O1 0x8000013
|
||||||
|
#define SERIAL_6O1 0x8000017
|
||||||
|
#define SERIAL_7O1 0x800001b
|
||||||
|
#define SERIAL_8O1 0x800001f
|
||||||
|
#define SERIAL_5O2 0x8000033
|
||||||
|
#define SERIAL_6O2 0x8000037
|
||||||
|
#define SERIAL_7O2 0x800003b
|
||||||
|
#define SERIAL_8O2 0x800003f
|
||||||
|
|
||||||
|
// These are Hardware Flow Contol possible usage
|
||||||
|
// equivalent to UDF enum uart_hw_flowcontrol_t from
|
||||||
|
// https://github.com/espressif/esp-idf/blob/master/components/hal/include/hal/uart_types.h#L75-L81
|
||||||
|
#define HW_FLOWCTRL_DISABLE 0x0 // disable HW Flow Control
|
||||||
|
#define HW_FLOWCTRL_RTS 0x1 // use only RTS PIN for HW Flow Control
|
||||||
|
#define HW_FLOWCTRL_CTS 0x2 // use only CTS PIN for HW Flow Control
|
||||||
|
#define HW_FLOWCTRL_CTS_RTS 0x3 // use both CTS and RTS PIN for HW Flow Control
|
||||||
|
|
||||||
|
struct uart_struct_t;
|
||||||
|
typedef struct uart_struct_t uart_t;
|
||||||
|
|
||||||
|
uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rxPin, int8_t txPin, uint16_t rx_buffer_size, uint16_t tx_buffer_size, bool inverted, uint8_t rxfifo_full_thrhd);
|
||||||
|
void uartEnd(uart_t* uart);
|
||||||
|
|
||||||
|
// This is used to retrieve the Event Queue pointer from a UART IDF Driver in order to allow user to deal with its events
|
||||||
|
void uartGetEventQueue(uart_t* uart, QueueHandle_t *q);
|
||||||
|
|
||||||
|
uint32_t uartAvailable(uart_t* uart);
|
||||||
|
uint32_t uartAvailableForWrite(uart_t* uart);
|
||||||
|
uint8_t uartRead(uart_t* uart);
|
||||||
|
uint8_t uartPeek(uart_t* uart);
|
||||||
|
|
||||||
|
void uartWrite(uart_t* uart, uint8_t c);
|
||||||
|
void uartWriteBuf(uart_t* uart, const uint8_t * data, size_t len);
|
||||||
|
|
||||||
|
void uartFlush(uart_t* uart);
|
||||||
|
void uartFlushTxOnly(uart_t* uart, bool txOnly );
|
||||||
|
|
||||||
|
void uartSetBaudRate(uart_t* uart, uint32_t baud_rate);
|
||||||
|
uint32_t uartGetBaudRate(uart_t* uart);
|
||||||
|
|
||||||
|
void uartSetRxInvert(uart_t* uart, bool invert);
|
||||||
|
void uartSetRxTimeout(uart_t* uart, uint8_t numSymbTimeout);
|
||||||
|
void uartSetRxFIFOFull(uart_t* uart, uint8_t numBytesFIFOFull);
|
||||||
|
void uartSetFastReading(uart_t* uart);
|
||||||
|
|
||||||
|
void uartSetDebug(uart_t* uart);
|
||||||
|
int uartGetDebug();
|
||||||
|
|
||||||
|
bool uartIsDriverInstalled(uart_t* uart);
|
||||||
|
|
||||||
|
// Negative Pin Number will keep it unmodified, thus this function can set individual pins
|
||||||
|
void uartSetPins(uart_t* uart, int8_t rxPin, int8_t txPin, int8_t ctsPin, int8_t rtsPin);
|
||||||
|
|
||||||
|
// Enables or disables HW Flow Control function -- needs also to set CTS and/or RTS pins
|
||||||
|
void uartSetHwFlowCtrlMode(uart_t *uart, uint8_t mode, uint8_t threshold);
|
||||||
|
|
||||||
|
void uartStartDetectBaudrate(uart_t *uart);
|
||||||
|
unsigned long uartDetectBaudrate(uart_t *uart);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* MAIN_ESP32_HAL_UART_H_ */
|
147
cores/esp32/esp32-hal.h
Normal file
147
cores/esp32/esp32-hal.h
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
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 HAL_ESP32_HAL_H_
|
||||||
|
#define HAL_ESP32_HAL_H_
|
||||||
|
|
||||||
|
#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_system.h"
|
||||||
|
#include "esp_sleep.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/queue.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
|
#include "freertos/event_groups.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef F_CPU
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||||
|
#define F_CPU (CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ * 1000000U)
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||||
|
#define F_CPU (CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ * 1000000U)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if CONFIG_ARDUINO_ISR_IRAM
|
||||||
|
#define ARDUINO_ISR_ATTR IRAM_ATTR
|
||||||
|
#define ARDUINO_ISR_FLAG ESP_INTR_FLAG_IRAM
|
||||||
|
#else
|
||||||
|
#define ARDUINO_ISR_ATTR
|
||||||
|
#define ARDUINO_ISR_FLAG (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ARDUINO_RUNNING_CORE
|
||||||
|
#define ARDUINO_RUNNING_CORE CONFIG_ARDUINO_RUNNING_CORE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ARDUINO_EVENT_RUNNING_CORE
|
||||||
|
#define ARDUINO_EVENT_RUNNING_CORE CONFIG_ARDUINO_EVENT_RUNNING_CORE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//forward declaration from freertos/portmacro.h
|
||||||
|
void vPortYield(void);
|
||||||
|
void yield(void);
|
||||||
|
#define optimistic_yield(u)
|
||||||
|
|
||||||
|
#define ESP_REG(addr) *((volatile uint32_t *)(addr))
|
||||||
|
#define NOP() asm volatile ("nop")
|
||||||
|
|
||||||
|
#include "esp32-hal-log.h"
|
||||||
|
#include "esp32-hal-matrix.h"
|
||||||
|
#include "esp32-hal-uart.h"
|
||||||
|
#include "esp32-hal-gpio.h"
|
||||||
|
#include "esp32-hal-touch.h"
|
||||||
|
#include "esp32-hal-dac.h"
|
||||||
|
#include "esp32-hal-adc.h"
|
||||||
|
#include "esp32-hal-spi.h"
|
||||||
|
#include "esp32-hal-i2c.h"
|
||||||
|
#include "esp32-hal-ledc.h"
|
||||||
|
#include "esp32-hal-rmt.h"
|
||||||
|
#include "esp32-hal-sigmadelta.h"
|
||||||
|
#include "esp32-hal-timer.h"
|
||||||
|
#include "esp32-hal-bt.h"
|
||||||
|
#include "esp32-hal-psram.h"
|
||||||
|
#include "esp32-hal-rgb-led.h"
|
||||||
|
#include "esp32-hal-cpu.h"
|
||||||
|
|
||||||
|
void analogWrite(uint8_t pin, int value);
|
||||||
|
int8_t analogGetChannel(uint8_t pin);
|
||||||
|
|
||||||
|
//returns chip temperature in Celsius
|
||||||
|
float temperatureRead();
|
||||||
|
|
||||||
|
//allows user to bypass SPI RAM test routine
|
||||||
|
bool testSPIRAM(void);
|
||||||
|
|
||||||
|
#if CONFIG_AUTOSTART_ARDUINO
|
||||||
|
//enable/disable WDT for Arduino's setup and loop functions
|
||||||
|
void enableLoopWDT();
|
||||||
|
void disableLoopWDT();
|
||||||
|
//feed WDT for the loop task
|
||||||
|
void feedLoopWDT();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//enable/disable WDT for the IDLE task on Core 0 (SYSTEM)
|
||||||
|
void enableCore0WDT();
|
||||||
|
void disableCore0WDT();
|
||||||
|
#ifndef CONFIG_FREERTOS_UNICORE
|
||||||
|
//enable/disable WDT for the IDLE task on Core 1 (Arduino)
|
||||||
|
void enableCore1WDT();
|
||||||
|
void disableCore1WDT();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//if xCoreID < 0 or CPU is unicore, it will use xTaskCreate, else xTaskCreatePinnedToCore
|
||||||
|
//allows to easily handle all possible situations without repetitive code
|
||||||
|
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 );
|
||||||
|
|
||||||
|
unsigned long micros();
|
||||||
|
unsigned long millis();
|
||||||
|
void delay(uint32_t);
|
||||||
|
void delayMicroseconds(uint32_t us);
|
||||||
|
|
||||||
|
#if !CONFIG_ESP32_PHY_AUTO_INIT
|
||||||
|
void arduino_phy_init();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !CONFIG_AUTOSTART_ARDUINO
|
||||||
|
void initArduino();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* HAL_ESP32_HAL_H_ */
|
24
cores/esp32/esp8266-compat.h
Normal file
24
cores/esp32/esp8266-compat.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// esp8266-compat.h - Compatibility functions to help ESP8266 libraries and user code run on ESP32
|
||||||
|
|
||||||
|
// Copyright (c) 2017 Evandro Luis Copercini. 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.
|
||||||
|
|
||||||
|
#ifndef _ESP8266_COMPAT_H_
|
||||||
|
#define _ESP8266_COMPAT_H_
|
||||||
|
|
||||||
|
#define ICACHE_FLASH_ATTR
|
||||||
|
#define ICACHE_RAM_ATTR ARDUINO_ISR_ATTR
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _ESP8266_COMPAT_H_ */
|
46
cores/esp32/esp_arduino_version.h
Normal file
46
cores/esp32/esp_arduino_version.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright 2019 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
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Major version number (X.x.x) */
|
||||||
|
#define ESP_ARDUINO_VERSION_MAJOR 2
|
||||||
|
/** Minor version number (x.X.x) */
|
||||||
|
#define ESP_ARDUINO_VERSION_MINOR 0
|
||||||
|
/** Patch version number (x.x.X) */
|
||||||
|
#define ESP_ARDUINO_VERSION_PATCH 5
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Macro to convert ARDUINO version number into an integer
|
||||||
|
*
|
||||||
|
* To be used in comparisons, such as ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(2, 0, 0)
|
||||||
|
*/
|
||||||
|
#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current ARDUINO version, as an integer
|
||||||
|
*
|
||||||
|
* To be used in comparisons, such as ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(2, 0, 0)
|
||||||
|
*/
|
||||||
|
#define ESP_ARDUINO_VERSION ESP_ARDUINO_VERSION_VAL(ESP_ARDUINO_VERSION_MAJOR, \
|
||||||
|
ESP_ARDUINO_VERSION_MINOR, \
|
||||||
|
ESP_ARDUINO_VERSION_PATCH)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
204
cores/esp32/firmware_msc_fat.c
Normal file
204
cores/esp32/firmware_msc_fat.c
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
// 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 "firmware_msc_fat.h"
|
||||||
|
//copy up to max_len chars from src to dst and do not terminate
|
||||||
|
static size_t cplstr(void *dst, const void * src, size_t max_len){
|
||||||
|
if(!src || !dst || !max_len){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
size_t l = strlen((const char *)src);
|
||||||
|
if(l > max_len){
|
||||||
|
l = max_len;
|
||||||
|
}
|
||||||
|
memcpy(dst, src, l);
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
//copy up to max_len chars from src to dst, adding spaces up to max_len. do not terminate
|
||||||
|
static void cplstrsp(void *dst, const void * src, size_t max_len){
|
||||||
|
size_t l = cplstr(dst, src, max_len);
|
||||||
|
for(; l < max_len; l++){
|
||||||
|
((uint8_t*)dst)[l] = 0x20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FAT12
|
||||||
|
static const char * FAT12_FILE_SYSTEM_TYPE = "FAT12";
|
||||||
|
|
||||||
|
static uint16_t fat12_sectors_per_alloc_table(uint32_t sector_num){
|
||||||
|
uint32_t required_bytes = (((sector_num * 3)+1)/2);
|
||||||
|
return (required_bytes / DISK_SECTOR_SIZE) + ((required_bytes & (DISK_SECTOR_SIZE - 1))?1:0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t * fat12_add_table(uint8_t * dst, fat_boot_sector_t * boot){
|
||||||
|
memset(dst+DISK_SECTOR_SIZE, 0, boot->sectors_per_alloc_table * DISK_SECTOR_SIZE);
|
||||||
|
uint8_t * d = dst + DISK_SECTOR_SIZE;
|
||||||
|
d[0] = 0xF8;
|
||||||
|
d[1] = 0xFF;
|
||||||
|
d[2] = 0xFF;
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fat12_set_table_index(uint8_t * table, uint16_t index, uint16_t value){
|
||||||
|
uint16_t offset = (index >> 1) * 3;
|
||||||
|
uint8_t * data = table + offset;
|
||||||
|
if(index & 1){
|
||||||
|
data[2] = (value >> 4) & 0xFF;
|
||||||
|
data[1] = (data[1] & 0xF) | ((value & 0xF) << 4);
|
||||||
|
} else {
|
||||||
|
data[0] = value & 0xFF;
|
||||||
|
data[1] = (data[1] & 0xF0) | ((value >> 8) & 0xF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//FAT16
|
||||||
|
static const char * FAT16_FILE_SYSTEM_TYPE = "FAT16";
|
||||||
|
|
||||||
|
static uint16_t fat16_sectors_per_alloc_table(uint32_t sector_num){
|
||||||
|
uint32_t required_bytes = sector_num * 2;
|
||||||
|
return (required_bytes / DISK_SECTOR_SIZE) + ((required_bytes & (DISK_SECTOR_SIZE - 1))?1:0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t * fat16_add_table(uint8_t * dst, fat_boot_sector_t * boot){
|
||||||
|
memset(dst+DISK_SECTOR_SIZE, 0, boot->sectors_per_alloc_table * DISK_SECTOR_SIZE);
|
||||||
|
uint16_t * d = (uint16_t *)(dst + DISK_SECTOR_SIZE);
|
||||||
|
d[0] = 0xFFF8;
|
||||||
|
d[1] = 0xFFFF;
|
||||||
|
return (uint8_t *)d;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fat16_set_table_index(uint8_t * table, uint16_t index, uint16_t value){
|
||||||
|
uint16_t offset = index * 2;
|
||||||
|
*(uint16_t *)(table + offset) = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Interface
|
||||||
|
const char * fat_file_system_type(bool fat16) {
|
||||||
|
return ((fat16)?FAT16_FILE_SYSTEM_TYPE:FAT12_FILE_SYSTEM_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t fat_sectors_per_alloc_table(uint32_t sector_num, bool fat16){
|
||||||
|
if(fat16){
|
||||||
|
return fat16_sectors_per_alloc_table(sector_num);
|
||||||
|
}
|
||||||
|
return fat12_sectors_per_alloc_table(sector_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t * fat_add_table(uint8_t * dst, fat_boot_sector_t * boot, bool fat16){
|
||||||
|
if(fat16){
|
||||||
|
return fat16_add_table(dst, boot);
|
||||||
|
}
|
||||||
|
return fat12_add_table(dst, boot);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fat_set_table_index(uint8_t * table, uint16_t index, uint16_t value, bool fat16){
|
||||||
|
if(fat16){
|
||||||
|
fat16_set_table_index(table, index, value);
|
||||||
|
} else {
|
||||||
|
fat12_set_table_index(table, index, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fat_boot_sector_t * fat_add_boot_sector(uint8_t * dst, uint16_t sector_num, uint16_t table_sectors, const char * file_system_type, const char * volume_label, uint32_t serial_number){
|
||||||
|
fat_boot_sector_t *boot = (fat_boot_sector_t*)dst;
|
||||||
|
boot->jump_instruction[0] = 0xEB;
|
||||||
|
boot->jump_instruction[1] = 0x3C;
|
||||||
|
boot->jump_instruction[2] = 0x90;
|
||||||
|
cplstr(boot->oem_name, "MSDOS5.0", 8);
|
||||||
|
boot->bytes_per_sector = DISK_SECTOR_SIZE;
|
||||||
|
boot->sectors_per_cluster = 1;
|
||||||
|
boot->reserved_sectors_count = 1;
|
||||||
|
boot->file_alloc_tables_num = 1;
|
||||||
|
boot->max_root_dir_entries = 16;
|
||||||
|
boot->fat12_sector_num = sector_num;
|
||||||
|
boot->media_descriptor = 0xF8;
|
||||||
|
boot->sectors_per_alloc_table = table_sectors;
|
||||||
|
boot->sectors_per_track = 1;
|
||||||
|
boot->num_heads = 1;
|
||||||
|
boot->hidden_sectors_count = 0;
|
||||||
|
boot->total_sectors_32 = 0;
|
||||||
|
boot->physical_drive_number = 0x80;
|
||||||
|
boot->reserved0 = 0x00;
|
||||||
|
boot->extended_boot_signature = 0x29;
|
||||||
|
boot->serial_number = serial_number;
|
||||||
|
cplstrsp(boot->volume_label, volume_label, 11);
|
||||||
|
memset(boot->reserved, 0, 448);
|
||||||
|
cplstrsp(boot->file_system_type, file_system_type, 8);
|
||||||
|
boot->signature = 0xAA55;
|
||||||
|
return boot;
|
||||||
|
}
|
||||||
|
|
||||||
|
fat_dir_entry_t * fat_add_label(uint8_t * dst, const char * volume_label){
|
||||||
|
fat_boot_sector_t * boot = (fat_boot_sector_t *)dst;
|
||||||
|
fat_dir_entry_t * entry = (fat_dir_entry_t *)(dst + ((boot->sectors_per_alloc_table+1) * DISK_SECTOR_SIZE));
|
||||||
|
memset(entry, 0, sizeof(fat_dir_entry_t));
|
||||||
|
cplstrsp(entry->volume_label, volume_label, 11);
|
||||||
|
entry->file_attr = FAT_FILE_ATTR_VOLUME_LABEL;
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
fat_dir_entry_t * fat_add_root_file(uint8_t * dst, uint8_t index, const char * file_name, const char * file_extension, size_t file_size, uint16_t data_start_sector, bool is_fat16){
|
||||||
|
fat_boot_sector_t * boot = (fat_boot_sector_t *)dst;
|
||||||
|
uint8_t * table = dst + DISK_SECTOR_SIZE;
|
||||||
|
fat_dir_entry_t * entry = (fat_dir_entry_t *)(dst + ((boot->sectors_per_alloc_table+1) * DISK_SECTOR_SIZE) + (index * sizeof(fat_dir_entry_t)));
|
||||||
|
memset(entry, 0, sizeof(fat_dir_entry_t));
|
||||||
|
cplstrsp(entry->file_name, file_name, 8);
|
||||||
|
cplstrsp(entry->file_extension, file_extension, 3);
|
||||||
|
entry->file_attr = FAT_FILE_ATTR_ARCHIVE;
|
||||||
|
entry->file_size = file_size;
|
||||||
|
entry->data_start_sector = data_start_sector;
|
||||||
|
entry->extended_attr = 0;
|
||||||
|
|
||||||
|
uint16_t file_sectors = file_size / DISK_SECTOR_SIZE;
|
||||||
|
if(file_size % DISK_SECTOR_SIZE){
|
||||||
|
file_sectors++;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t data_end_sector = data_start_sector + file_sectors;
|
||||||
|
for(uint16_t i=data_start_sector; i<(data_end_sector-1); i++){
|
||||||
|
fat_set_table_index(table, i, i+1, is_fat16);
|
||||||
|
}
|
||||||
|
fat_set_table_index(table, data_end_sector-1, 0xFFFF, is_fat16);
|
||||||
|
|
||||||
|
//Set Firmware Date based on the build time
|
||||||
|
static const char * month_names_short[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
||||||
|
char mstr[8] = {'\0',};
|
||||||
|
const char *str = __DATE__ " " __TIME__;
|
||||||
|
int ms=0, seconds=0, minutes=0, hours=0, year=0, date=0, month=0;
|
||||||
|
int r = sscanf(str,"%s %d %d %d:%d:%d", mstr, &date, &year, &hours, &minutes, &seconds);
|
||||||
|
if(r >= 0){
|
||||||
|
for(int i=0; i<12; i++){
|
||||||
|
if(!strcmp(mstr, month_names_short[i])){
|
||||||
|
month = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entry->creation_time_ms = FAT_MS2V(seconds, ms);
|
||||||
|
entry->creation_time_hms = FAT_HMS2V(hours, minutes, seconds);
|
||||||
|
entry->creation_time_ymd = FAT_YMD2V(year, month, date);
|
||||||
|
entry->last_access_ymd = entry->creation_time_ymd;
|
||||||
|
entry->last_modified_hms = entry->creation_time_hms;
|
||||||
|
entry->last_modified_ymd = entry->creation_time_ymd;
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t fat_lfn_checksum(const uint8_t *short_filename){
|
||||||
|
uint8_t sum = 0;
|
||||||
|
for (uint8_t i = 11; i; i--) {
|
||||||
|
sum = ((sum & 1) << 7) + (sum >> 1) + *short_filename++;
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
141
cores/esp32/firmware_msc_fat.h
Normal file
141
cores/esp32/firmware_msc_fat.h
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
// 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 <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FAT_U8(v) ((v) & 0xFF)
|
||||||
|
#define FAT_U16(v) FAT_U8(v), FAT_U8((v) >> 8)
|
||||||
|
#define FAT_U32(v) FAT_U8(v), FAT_U8((v) >> 8), FAT_U8((v) >> 16), FAT_U8((v) >> 24)
|
||||||
|
|
||||||
|
#define FAT12_TBL2B(l,h) FAT_U8(l), FAT_U8(((l >> 8) & 0xF) | ((h << 4) & 0xF0)), FAT_U8(h >> 4)
|
||||||
|
|
||||||
|
#define FAT_MS2B(s,ms) FAT_U8(((((s) & 0x1) * 1000) + (ms)) / 10)
|
||||||
|
#define FAT_HMS2B(h,m,s) FAT_U8(((s) >> 1)|(((m) & 0x7) << 5)), FAT_U8((((m) >> 3) & 0x7)|((h) << 3))
|
||||||
|
#define FAT_YMD2B(y,m,d) FAT_U8(((d) & 0x1F)|(((m) & 0x7) << 5)), FAT_U8((((m) >> 3) & 0x1)|((((y) - 1980) & 0x7F) << 1))
|
||||||
|
|
||||||
|
#define FAT_MS2V(s,ms) FAT_U8(((((s) & 0x1) * 1000) + (ms)) / 10)
|
||||||
|
#define FAT_HMS2V(h,m,s) (FAT_U8(((s) >> 1)|(((m) & 0x7) << 5)) | (FAT_U8((((m) >> 3) & 0x7)|((h) << 3)) << 8))
|
||||||
|
#define FAT_YMD2V(y,m,d) (FAT_U8(((d) & 0x1F)|(((m) & 0x7) << 5)) | (FAT_U8((((m) >> 3) & 0x1)|((((y) - 1980) & 0x7F) << 1)) << 8))
|
||||||
|
|
||||||
|
#define FAT_B2HMS(hms) ((hms >> 11) & 0x1F), ((hms >> 5) & 0x3F), ((hms & 0x1F) << 1)
|
||||||
|
#define FAT_B2YMD(ymd) (((ymd >> 9) & 0x7F) + 1980), ((ymd >> 5) & 0x0F), (ymd & 0x1F)
|
||||||
|
|
||||||
|
#define FAT_FILE_ATTR_READ_ONLY 0x01
|
||||||
|
#define FAT_FILE_ATTR_HIDDEN 0x02
|
||||||
|
#define FAT_FILE_ATTR_SYSTEM 0x04
|
||||||
|
#define FAT_FILE_ATTR_VOLUME_LABEL 0x08
|
||||||
|
#define FAT_FILE_ATTR_SUBDIRECTORY 0x10
|
||||||
|
#define FAT_FILE_ATTR_ARCHIVE 0x20
|
||||||
|
#define FAT_FILE_ATTR_DEVICE 0x40
|
||||||
|
|
||||||
|
static const uint16_t DISK_SECTOR_SIZE = 512;
|
||||||
|
|
||||||
|
#define FAT_SIZE_TO_SECTORS(bytes) ((bytes) / DISK_SECTOR_SIZE) + (((bytes) % DISK_SECTOR_SIZE)?1:0)
|
||||||
|
|
||||||
|
typedef struct __attribute__ ((packed)) {
|
||||||
|
uint8_t jump_instruction[3];
|
||||||
|
char oem_name[8];//padded with spaces (0x20)
|
||||||
|
uint16_t bytes_per_sector;//DISK_SECTOR_SIZE usually 512
|
||||||
|
uint8_t sectors_per_cluster;//Allowed values are 1, 2, 4, 8, 16, 32, 64, and 128
|
||||||
|
uint16_t reserved_sectors_count;//At least 1 for this sector, usually 32 for FAT32
|
||||||
|
uint8_t file_alloc_tables_num;//Almost always 2; RAM disks might use 1
|
||||||
|
uint16_t max_root_dir_entries;//FAT12 and FAT16
|
||||||
|
uint16_t fat12_sector_num;//DISK_SECTOR_NUM FAT12 and FAT16
|
||||||
|
uint8_t media_descriptor;
|
||||||
|
uint16_t sectors_per_alloc_table;//FAT12 and FAT16
|
||||||
|
uint16_t sectors_per_track;//A value of 0 may indicate LBA-only access
|
||||||
|
uint16_t num_heads;
|
||||||
|
uint32_t hidden_sectors_count;
|
||||||
|
uint32_t total_sectors_32;
|
||||||
|
uint8_t physical_drive_number;//0x00 for (first) removable media, 0x80 for (first) fixed disk
|
||||||
|
uint8_t reserved0;
|
||||||
|
uint8_t extended_boot_signature;//should be 0x29
|
||||||
|
uint32_t serial_number;//0x1234 => 1234
|
||||||
|
char volume_label[11];//padded with spaces (0x20)
|
||||||
|
char file_system_type[8];//padded with spaces (0x20)
|
||||||
|
uint8_t reserved[448];
|
||||||
|
uint16_t signature;//should be 0xAA55
|
||||||
|
} fat_boot_sector_t;
|
||||||
|
|
||||||
|
typedef struct __attribute__ ((packed)) {
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
char file_name[8];//padded with spaces (0x20)
|
||||||
|
char file_extension[3];//padded with spaces (0x20)
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
uint8_t file_magic;// 0xE5:deleted, 0x05:will_be_deleted, 0x00:end_marker, 0x2E:dot_marker(. or ..)
|
||||||
|
char file_magic_data[10];
|
||||||
|
};
|
||||||
|
char volume_label[11];//padded with spaces (0x20)
|
||||||
|
};
|
||||||
|
uint8_t file_attr;//mask of FAT_FILE_ATTR_*
|
||||||
|
uint8_t reserved;//always 0
|
||||||
|
uint8_t creation_time_ms;//ms * 10; max 1990 (1s 990ms)
|
||||||
|
uint16_t creation_time_hms; // [5:6:5] => h:m:(s/2)
|
||||||
|
uint16_t creation_time_ymd; // [7:4:5] => (y+1980):m:d
|
||||||
|
uint16_t last_access_ymd;
|
||||||
|
uint16_t extended_attr;
|
||||||
|
uint16_t last_modified_hms;
|
||||||
|
uint16_t last_modified_ymd;
|
||||||
|
uint16_t data_start_sector;
|
||||||
|
uint32_t file_size;
|
||||||
|
} fat_dir_entry_t;
|
||||||
|
|
||||||
|
typedef struct __attribute__ ((packed)) {
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
uint8_t number:5;
|
||||||
|
uint8_t reserved0:1;
|
||||||
|
uint8_t llfp:1;
|
||||||
|
uint8_t reserved1:1;
|
||||||
|
} seq;
|
||||||
|
uint8_t seq_num; //0xE5: Deleted Entry
|
||||||
|
};
|
||||||
|
uint16_t name0[5];
|
||||||
|
uint8_t attr; //ALWAYS 0x0F
|
||||||
|
uint8_t type; //ALWAYS 0x00
|
||||||
|
uint8_t dos_checksum;
|
||||||
|
uint16_t name1[6];
|
||||||
|
uint16_t first_cluster; //ALWAYS 0x0000
|
||||||
|
uint16_t name2[2];
|
||||||
|
} fat_lfn_entry_t;
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
fat_dir_entry_t dir;
|
||||||
|
fat_lfn_entry_t lfn;
|
||||||
|
} fat_entry_t;
|
||||||
|
|
||||||
|
const char * fat_file_system_type(bool fat16);
|
||||||
|
uint16_t fat_sectors_per_alloc_table(uint32_t sector_num, bool fat16);
|
||||||
|
uint8_t * fat_add_table(uint8_t * dst, fat_boot_sector_t * boot, bool fat16);
|
||||||
|
void fat_set_table_index(uint8_t * table, uint16_t index, uint16_t value, bool fat16);
|
||||||
|
fat_boot_sector_t * fat_add_boot_sector(uint8_t * dst, uint16_t sector_num, uint16_t table_sectors, const char * file_system_type, const char * volume_label, uint32_t serial_number);
|
||||||
|
fat_dir_entry_t * fat_add_label(uint8_t * dst, const char * volume_label);
|
||||||
|
fat_dir_entry_t * fat_add_root_file(uint8_t * dst, uint8_t index, const char * file_name, const char * file_extension, size_t file_size, uint16_t data_start_sector, bool is_fat16);
|
||||||
|
uint8_t fat_lfn_checksum(const uint8_t *short_filename);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
7
cores/esp32/libb64/AUTHORS
Normal file
7
cores/esp32/libb64/AUTHORS
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
libb64: Base64 Encoding/Decoding Routines
|
||||||
|
======================================
|
||||||
|
|
||||||
|
Authors:
|
||||||
|
-------
|
||||||
|
|
||||||
|
Chris Venter chris.venter@gmail.com http://rocketpod.blogspot.com
|
29
cores/esp32/libb64/LICENSE
Normal file
29
cores/esp32/libb64/LICENSE
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
Copyright-Only Dedication (based on United States law)
|
||||||
|
or Public Domain Certification
|
||||||
|
|
||||||
|
The person or persons who have associated work with this document (the
|
||||||
|
"Dedicator" or "Certifier") hereby either (a) certifies that, to the best of
|
||||||
|
his knowledge, the work of authorship identified is in the public domain of the
|
||||||
|
country from which the work is published, or (b) hereby dedicates whatever
|
||||||
|
copyright the dedicators holds in the work of authorship identified below (the
|
||||||
|
"Work") to the public domain. A certifier, moreover, dedicates any copyright
|
||||||
|
interest he may have in the associated work, and for these purposes, is
|
||||||
|
described as a "dedicator" below.
|
||||||
|
|
||||||
|
A certifier has taken reasonable steps to verify the copyright status of this
|
||||||
|
work. Certifier recognizes that his good faith efforts may not shield him from
|
||||||
|
liability if in fact the work certified is not in the public domain.
|
||||||
|
|
||||||
|
Dedicator makes this dedication for the benefit of the public at large and to
|
||||||
|
the detriment of the Dedicator's heirs and successors. Dedicator intends this
|
||||||
|
dedication to be an overt act of relinquishment in perpetuity of all present
|
||||||
|
and future rights under copyright law, whether vested or contingent, in the
|
||||||
|
Work. Dedicator understands that such relinquishment of all rights includes
|
||||||
|
the relinquishment of all rights to enforce (by lawsuit or otherwise) those
|
||||||
|
copyrights in the Work.
|
||||||
|
|
||||||
|
Dedicator recognizes that, once placed in the public domain, the Work may be
|
||||||
|
freely reproduced, distributed, transmitted, used, modified, built upon, or
|
||||||
|
otherwise exploited by anyone for any purpose, commercial or non-commercial,
|
||||||
|
and in any way, including by methods that have not yet been invented or
|
||||||
|
conceived.
|
102
cores/esp32/libb64/cdecode.c
Normal file
102
cores/esp32/libb64/cdecode.c
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
cdecoder.c - c source to a base64 decoding algorithm implementation
|
||||||
|
|
||||||
|
This is part of the libb64 project, and has been placed in the public domain.
|
||||||
|
For details, see http://sourceforge.net/projects/libb64
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cdecode.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
static int base64_decode_value_signed(int8_t value_in){
|
||||||
|
static const int8_t decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51};
|
||||||
|
static const int8_t decoding_size = sizeof(decoding);
|
||||||
|
value_in -= 43;
|
||||||
|
if (value_in < 0 || value_in >= decoding_size) return -1;
|
||||||
|
return decoding[(int)value_in];
|
||||||
|
}
|
||||||
|
|
||||||
|
void base64_init_decodestate(base64_decodestate* state_in){
|
||||||
|
state_in->step = step_a;
|
||||||
|
state_in->plainchar = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int base64_decode_block_signed(const int8_t* code_in, const int length_in, int8_t* plaintext_out, base64_decodestate* state_in){
|
||||||
|
const int8_t* codechar = code_in;
|
||||||
|
int8_t* plainchar = plaintext_out;
|
||||||
|
int8_t fragment;
|
||||||
|
|
||||||
|
*plainchar = state_in->plainchar;
|
||||||
|
|
||||||
|
switch (state_in->step){
|
||||||
|
while (1){
|
||||||
|
case step_a:
|
||||||
|
do {
|
||||||
|
if (codechar == code_in+length_in){
|
||||||
|
state_in->step = step_a;
|
||||||
|
state_in->plainchar = *plainchar;
|
||||||
|
return plainchar - plaintext_out;
|
||||||
|
}
|
||||||
|
fragment = (int8_t)base64_decode_value_signed(*codechar++);
|
||||||
|
} while (fragment < 0);
|
||||||
|
*plainchar = (fragment & 0x03f) << 2;
|
||||||
|
// fall through
|
||||||
|
case step_b:
|
||||||
|
do {
|
||||||
|
if (codechar == code_in+length_in){
|
||||||
|
state_in->step = step_b;
|
||||||
|
state_in->plainchar = *plainchar;
|
||||||
|
return plainchar - plaintext_out;
|
||||||
|
}
|
||||||
|
fragment = (int8_t)base64_decode_value_signed(*codechar++);
|
||||||
|
} while (fragment < 0);
|
||||||
|
*plainchar++ |= (fragment & 0x030) >> 4;
|
||||||
|
*plainchar = (fragment & 0x00f) << 4;
|
||||||
|
// fall through
|
||||||
|
case step_c:
|
||||||
|
do {
|
||||||
|
if (codechar == code_in+length_in){
|
||||||
|
state_in->step = step_c;
|
||||||
|
state_in->plainchar = *plainchar;
|
||||||
|
return plainchar - plaintext_out;
|
||||||
|
}
|
||||||
|
fragment = (int8_t)base64_decode_value_signed(*codechar++);
|
||||||
|
} while (fragment < 0);
|
||||||
|
*plainchar++ |= (fragment & 0x03c) >> 2;
|
||||||
|
*plainchar = (fragment & 0x003) << 6;
|
||||||
|
// fall through
|
||||||
|
case step_d:
|
||||||
|
do {
|
||||||
|
if (codechar == code_in+length_in){
|
||||||
|
state_in->step = step_d;
|
||||||
|
state_in->plainchar = *plainchar;
|
||||||
|
return plainchar - plaintext_out;
|
||||||
|
}
|
||||||
|
fragment = (int8_t)base64_decode_value_signed(*codechar++);
|
||||||
|
} while (fragment < 0);
|
||||||
|
*plainchar++ |= (fragment & 0x03f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* control should not reach here */
|
||||||
|
return plainchar - plaintext_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int base64_decode_chars_signed(const int8_t* code_in, const int length_in, int8_t* plaintext_out){
|
||||||
|
base64_decodestate _state;
|
||||||
|
base64_init_decodestate(&_state);
|
||||||
|
int len = base64_decode_block_signed(code_in, length_in, plaintext_out, &_state);
|
||||||
|
if(len > 0) plaintext_out[len] = 0;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int base64_decode_value(char value_in){
|
||||||
|
return base64_decode_value_signed(*((int8_t *) &value_in));
|
||||||
|
}
|
||||||
|
|
||||||
|
int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in){
|
||||||
|
return base64_decode_block_signed((int8_t *) code_in, length_in, (int8_t *) plaintext_out, state_in);
|
||||||
|
}
|
||||||
|
|
||||||
|
int base64_decode_chars(const char* code_in, const int length_in, char* plaintext_out){
|
||||||
|
return base64_decode_chars_signed((int8_t *) code_in, length_in, (int8_t *) plaintext_out);
|
||||||
|
}
|
38
cores/esp32/libb64/cdecode.h
Normal file
38
cores/esp32/libb64/cdecode.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
cdecode.h - c header for a base64 decoding algorithm
|
||||||
|
|
||||||
|
This is part of the libb64 project, and has been placed in the public domain.
|
||||||
|
For details, see http://sourceforge.net/projects/libb64
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BASE64_CDECODE_H
|
||||||
|
#define BASE64_CDECODE_H
|
||||||
|
|
||||||
|
#define base64_decode_expected_len(n) ((n * 3) / 4)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
step_a, step_b, step_c, step_d
|
||||||
|
} base64_decodestep;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
base64_decodestep step;
|
||||||
|
char plainchar;
|
||||||
|
} base64_decodestate;
|
||||||
|
|
||||||
|
void base64_init_decodestate(base64_decodestate* state_in);
|
||||||
|
|
||||||
|
int base64_decode_value(char value_in);
|
||||||
|
|
||||||
|
int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in);
|
||||||
|
|
||||||
|
int base64_decode_chars(const char* code_in, const int length_in, char* plaintext_out);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* BASE64_CDECODE_H */
|
104
cores/esp32/libb64/cencode.c
Normal file
104
cores/esp32/libb64/cencode.c
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
cencoder.c - c source to a base64 encoding algorithm implementation
|
||||||
|
|
||||||
|
This is part of the libb64 project, and has been placed in the public domain.
|
||||||
|
For details, see http://sourceforge.net/projects/libb64
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cencode.h"
|
||||||
|
|
||||||
|
void base64_init_encodestate(base64_encodestate* state_in)
|
||||||
|
{
|
||||||
|
state_in->step = step_A;
|
||||||
|
state_in->result = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char base64_encode_value(char value_in)
|
||||||
|
{
|
||||||
|
static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
if (value_in > 63) {
|
||||||
|
return '=';
|
||||||
|
}
|
||||||
|
return encoding[(int)value_in];
|
||||||
|
}
|
||||||
|
|
||||||
|
int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in)
|
||||||
|
{
|
||||||
|
const char* plainchar = plaintext_in;
|
||||||
|
const char* const plaintextend = plaintext_in + length_in;
|
||||||
|
char* codechar = code_out;
|
||||||
|
char result;
|
||||||
|
char fragment;
|
||||||
|
|
||||||
|
result = state_in->result;
|
||||||
|
|
||||||
|
switch (state_in->step) {
|
||||||
|
while (1) {
|
||||||
|
case step_A:
|
||||||
|
if (plainchar == plaintextend) {
|
||||||
|
state_in->result = result;
|
||||||
|
state_in->step = step_A;
|
||||||
|
return codechar - code_out;
|
||||||
|
}
|
||||||
|
fragment = *plainchar++;
|
||||||
|
result = (fragment & 0x0fc) >> 2;
|
||||||
|
*codechar++ = base64_encode_value(result);
|
||||||
|
result = (fragment & 0x003) << 4;
|
||||||
|
// fall through
|
||||||
|
case step_B:
|
||||||
|
if (plainchar == plaintextend) {
|
||||||
|
state_in->result = result;
|
||||||
|
state_in->step = step_B;
|
||||||
|
return codechar - code_out;
|
||||||
|
}
|
||||||
|
fragment = *plainchar++;
|
||||||
|
result |= (fragment & 0x0f0) >> 4;
|
||||||
|
*codechar++ = base64_encode_value(result);
|
||||||
|
result = (fragment & 0x00f) << 2;
|
||||||
|
// fall through
|
||||||
|
case step_C:
|
||||||
|
if (plainchar == plaintextend) {
|
||||||
|
state_in->result = result;
|
||||||
|
state_in->step = step_C;
|
||||||
|
return codechar - code_out;
|
||||||
|
}
|
||||||
|
fragment = *plainchar++;
|
||||||
|
result |= (fragment & 0x0c0) >> 6;
|
||||||
|
*codechar++ = base64_encode_value(result);
|
||||||
|
result = (fragment & 0x03f) >> 0;
|
||||||
|
*codechar++ = base64_encode_value(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* control should not reach here */
|
||||||
|
return codechar - code_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
int base64_encode_blockend(char* code_out, base64_encodestate* state_in)
|
||||||
|
{
|
||||||
|
char* codechar = code_out;
|
||||||
|
|
||||||
|
switch (state_in->step) {
|
||||||
|
case step_B:
|
||||||
|
*codechar++ = base64_encode_value(state_in->result);
|
||||||
|
*codechar++ = '=';
|
||||||
|
*codechar++ = '=';
|
||||||
|
break;
|
||||||
|
case step_C:
|
||||||
|
*codechar++ = base64_encode_value(state_in->result);
|
||||||
|
*codechar++ = '=';
|
||||||
|
break;
|
||||||
|
case step_A:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*codechar = 0x00;
|
||||||
|
|
||||||
|
return codechar - code_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
int base64_encode_chars(const char* plaintext_in, int length_in, char* code_out)
|
||||||
|
{
|
||||||
|
base64_encodestate _state;
|
||||||
|
base64_init_encodestate(&_state);
|
||||||
|
int len = base64_encode_block(plaintext_in, length_in, code_out, &_state);
|
||||||
|
return len + base64_encode_blockend((code_out + len), &_state);
|
||||||
|
}
|
41
cores/esp32/libb64/cencode.h
Normal file
41
cores/esp32/libb64/cencode.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
cencode.h - c header for a base64 encoding algorithm
|
||||||
|
|
||||||
|
This is part of the libb64 project, and has been placed in the public domain.
|
||||||
|
For details, see http://sourceforge.net/projects/libb64
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BASE64_CENCODE_H
|
||||||
|
#define BASE64_CENCODE_H
|
||||||
|
|
||||||
|
#define base64_encode_expected_len(n) ((((4 * n) / 3) + 3) & ~3)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
step_A, step_B, step_C
|
||||||
|
} base64_encodestep;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
base64_encodestep step;
|
||||||
|
char result;
|
||||||
|
int stepcount;
|
||||||
|
} base64_encodestate;
|
||||||
|
|
||||||
|
void base64_init_encodestate(base64_encodestate* state_in);
|
||||||
|
|
||||||
|
char base64_encode_value(char value_in);
|
||||||
|
|
||||||
|
int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in);
|
||||||
|
|
||||||
|
int base64_encode_blockend(char* code_out, base64_encodestate* state_in);
|
||||||
|
|
||||||
|
int base64_encode_chars(const char* plaintext_in, int length_in, char* code_out);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* BASE64_CENCODE_H */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user