mirror of
https://github.com/0xFEEDC0DE64/arduino-esp32.git
synced 2025-06-25 09:51:33 +02:00
Compare commits
129 Commits
Author | SHA1 | Date | |
---|---|---|---|
d218e58f96 | |||
14ece43654 | |||
ae7173d4d5 | |||
c7080b0a83 | |||
96c184d213 | |||
da72bc90b7 | |||
16a9cf781f | |||
b1497fb257 | |||
5e04eb48b6 | |||
674cf812e7 | |||
83884a0cda | |||
80b8262595 | |||
5747bfab67 | |||
4b9dc61447 | |||
a2d7c0dcf6 | |||
666cd3cde5 | |||
3e851b537a | |||
f410728444 | |||
79e0889a16 | |||
4aff6dde39 | |||
bb50046540 | |||
10258b4cc2 | |||
418ac74be0 | |||
caa8d07aaf | |||
15bbd0a187 | |||
02c3ec01cc | |||
cb5a490616 | |||
f257d6f126 | |||
aabbed0bbc | |||
a418058a66 | |||
96ad341451 | |||
2673b88582 | |||
67c99142d2 | |||
951c8bece5 | |||
4413dbbd87 | |||
ed53b6c8d4 | |||
2e53300da5 | |||
64c7f746fd | |||
1049be7d56 | |||
34c81be93b | |||
2fb2ef54ce | |||
49f525c91d | |||
b145e65975 | |||
951c32056a | |||
7a7bd37e51 | |||
a75602dc68 | |||
88789cd817 | |||
335cedf4f7 | |||
f9f70d2f73 | |||
5b207104aa | |||
1706af4656 | |||
bd54ee442b | |||
00214d5c2a | |||
381e88ec75 | |||
f87107dedb | |||
ce85cf03cc | |||
f2a20e8a38 | |||
c5bb8334d7 | |||
6de7f16f28 | |||
1688b7c179 | |||
36ff442698 | |||
93f10609f4 | |||
67583e84d6 | |||
108e467164 | |||
91bca6c074 | |||
204f360dce | |||
3f06a38f69 | |||
2f6f251400 | |||
e4acfbc54a | |||
79d53bdc4c | |||
f3f6dad14a | |||
0f174aae88 | |||
317be68cef | |||
1f4dd7f131 | |||
8be2f7b1cc | |||
9f827a66d5 | |||
e1cdbd7816 | |||
7a35be3e7e | |||
078671d273 | |||
541cef9149 | |||
6dfaf6cdd4 | |||
92ce408f4c | |||
453af3800c | |||
44c11981d2 | |||
9eea85f9ff | |||
24b76cbb14 | |||
4a55ff970d | |||
a62979d8a0 | |||
1f59c5abec | |||
0730e0ec93 | |||
c45cff5f83 | |||
b1d072df9f | |||
929cf2c2d5 | |||
87853353db | |||
e265bd0d7c | |||
94809ce38b | |||
29455a0447 | |||
78499c459b | |||
ce680708ec | |||
90c01dab77 | |||
000d967db3 | |||
44dd99f5a5 | |||
b580bb23fd | |||
a7ea737f30 | |||
2af8cc3485 | |||
e5bd18d6aa | |||
a4118ea889 | |||
c4fcab28e4 | |||
0acbe781f5 | |||
0b0dfab3cf | |||
5fd737925f | |||
5bb8177aa1 | |||
be84c8219c | |||
31127f4260 | |||
4365a45401 | |||
023ae75b97 | |||
c5a1f3efd7 | |||
9406f8e464 | |||
65eafd16b5 | |||
d5a98f9a39 | |||
3780b5c924 | |||
1775dd1faa | |||
6972695d95 | |||
e0e5c88658 | |||
6e47e18a1b | |||
34125cee1d | |||
ee24736042 | |||
5458df0a54 | |||
e12d8c8ff1 |
16
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
16
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
----------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
This entire section can be deleted if all items are checked.
|
||||
|
||||
*By completing this PR sufficiently, you help us to improve the quality of Release Notes*
|
||||
|
||||
### Checklist
|
||||
|
||||
1. [ ] Please provide specific title of the PR describing the change, including the component name (eg."Update of Documentation link on Readme.md")
|
||||
2. [ ] Please provide related links (eg. Issue, other Project, submodule PR..)
|
||||
----------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
## Summary
|
||||
Please describe your proposed PR and what it contains.
|
||||
|
||||
## Impact
|
||||
Please describe impact of your PR and it's function.
|
2
.github/scripts/check-cmakelists.sh
vendored
2
.github/scripts/check-cmakelists.sh
vendored
@ -15,7 +15,7 @@ git submodule update --init --recursive
|
||||
REPO_SRCS=`find cores/esp32/ libraries/ -name 'examples' -prune -o -name '*.c' -print -o -name '*.cpp' -print | sort`
|
||||
|
||||
# find all source files named in CMakeLists.txt COMPONENT_SRCS
|
||||
CMAKE_SRCS=`cmake --trace-expand -C CMakeLists.txt 2>&1 | grep set\(srcs | cut -d'(' -f3 | sed 's/ )//' | sed 's/srcs //' | tr ' ;' '\n' | sort`
|
||||
CMAKE_SRCS=`cmake --trace-expand -P CMakeLists.txt 2>&1 | grep set\(srcs | cut -d'(' -f3 | sed 's/ )//' | sed 's/srcs //' | tr ' ;' '\n' | sort`
|
||||
|
||||
if ! diff -u0 --label "Repo Files" --label "srcs" <(echo "$REPO_SRCS") <(echo "$CMAKE_SRCS"); then
|
||||
echo "Source files in repo (-) and source files in CMakeLists.txt (+) don't match"
|
||||
|
79
.github/stale.yml
vendored
79
.github/stale.yml
vendored
@ -1,61 +1,26 @@
|
||||
# Configuration for probot-stale - https://github.com/probot/stale
|
||||
# This workflow firstly warns and then closes issues that have had no activity for a specified amount of time.
|
||||
#
|
||||
# You can adjust the behavior by modifying this file.
|
||||
# For more information can be found here: https://github.com/actions/stale
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
||||
daysUntilStale: 60
|
||||
name: Mark stale issues
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 9 * * *'
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
|
||||
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
|
||||
daysUntilClose: 14
|
||||
jobs:
|
||||
stale:
|
||||
|
||||
# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
|
||||
onlyLabels: []
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
|
||||
exemptLabels:
|
||||
- "Type: For reference"
|
||||
- "Type: To be implemented"
|
||||
- "Type: Feature request"
|
||||
|
||||
# Set to true to ignore issues in a project (defaults to false)
|
||||
exemptProjects: false
|
||||
|
||||
# Set to true to ignore issues in a milestone (defaults to false)
|
||||
exemptMilestones: false
|
||||
|
||||
# Set to true to ignore issues with an assignee (defaults to false)
|
||||
exemptAssignees: false
|
||||
|
||||
# Label to use when marking as stale
|
||||
staleLabel: "Status: Stale"
|
||||
|
||||
# Comment to post when marking as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
[STALE_SET] This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed in 14 days if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
|
||||
# Comment to post when removing the stale label.
|
||||
unmarkComment: >
|
||||
[STALE_CLR] This issue has been removed from the stale queue. Please ensure activity to keep it openin the future.
|
||||
|
||||
# Comment to post when closing a stale Issue or Pull Request.
|
||||
closeComment: >
|
||||
[STALE_DEL] This stale issue has been automatically closed. Thank you for your contributions.
|
||||
|
||||
# Limit the number of actions per hour, from 1-30. Default is 30
|
||||
limitPerRun: 30
|
||||
|
||||
# Limit to only `issues` or `pulls`
|
||||
only: issues
|
||||
|
||||
# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
|
||||
# pulls:
|
||||
# daysUntilStale: 30
|
||||
# markComment: >
|
||||
# This pull request has been automatically marked as stale because it has not had
|
||||
# recent activity. It will be closed if no further activity occurs. Thank you
|
||||
# for your contributions.
|
||||
|
||||
# issues:
|
||||
# exemptLabels:
|
||||
# - confirmed
|
||||
steps:
|
||||
- uses: actions/stale@v3
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs. Thank you for your contributions.'
|
||||
days-before-stale: 60
|
||||
days-before-close: 14
|
||||
exempt-issue-labels: 'Type: For reference,Type: To be implemented,Type: Feature request'
|
||||
stale-issue-label: 'Status: Stale'
|
38
.github/workflows/docs.yml
vendored
Normal file
38
.github/workflows/docs.yml
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
name: ReadTheDocs CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- release/*
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- '.github/workflows/docs.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- '.github/workflows/docs.yml'
|
||||
|
||||
jobs:
|
||||
|
||||
build-docs:
|
||||
name: Build ReadTheDocs
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Build
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install python3-pip python3-setuptools
|
||||
# GitHub CI installs pip3 and setuptools outside the path.
|
||||
# Update the path to include them and run.
|
||||
PATH=/home/runner/.local/bin:$PATH pip3 install --user -r ./docs/requirements.txt
|
||||
cd ./docs && PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" make html
|
27
.github/workflows/test_selfhosted_runner.yml
vendored
Normal file
27
.github/workflows/test_selfhosted_runner.yml
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
name: Test Github action on self hosted RPI runnes
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Dummy test - self hosted GHR
|
||||
runs-on: self-hosted
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@v2
|
||||
- name: Test message 1
|
||||
run: echo "This is test message"
|
||||
- name: Test message 2
|
||||
run: echo "This is test message2"
|
||||
- name: List directory
|
||||
run: ls
|
||||
- name: Create copy of README
|
||||
run: cp README.md README2.md
|
||||
- name: Read README2
|
||||
run: cat README2.md
|
||||
- name: Delete README2
|
||||
run: rm README2.md
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -21,3 +21,4 @@ boards.sloeber.txt
|
||||
|
||||
# Ignore docs build (Sphinx)
|
||||
docs/build
|
||||
docs/source/_build
|
||||
|
@ -1,3 +1,27 @@
|
||||
# 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
|
||||
@ -7,6 +31,7 @@ set(CORE_SRCS
|
||||
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
|
||||
@ -32,8 +57,12 @@ set(CORE_SRCS
|
||||
cores/esp32/stdlib_noniso.c
|
||||
cores/esp32/Stream.cpp
|
||||
cores/esp32/StreamString.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
|
||||
@ -50,6 +79,7 @@ set(LIBRARY_SRCS
|
||||
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
|
||||
@ -73,12 +103,19 @@ set(LIBRARY_SRCS
|
||||
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/WiFiClientSecure.cpp
|
||||
libraries/WiFi/src/ETH.cpp
|
||||
libraries/WiFi/src/WiFiAP.cpp
|
||||
libraries/WiFi/src/WiFiClient.cpp
|
||||
libraries/WiFi/src/WiFi.cpp
|
||||
@ -151,6 +188,7 @@ set(includedirs
|
||||
libraries/SPI/src
|
||||
libraries/Ticker/src
|
||||
libraries/Update/src
|
||||
libraries/USB/src
|
||||
libraries/WebServer/src
|
||||
libraries/WiFiClientSecure/src
|
||||
libraries/WiFi/src
|
||||
@ -161,7 +199,7 @@ set(includedirs
|
||||
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)
|
||||
set(priv_requires fatfs nvs_flash app_update spiffs bootloader_support openssl bt esp_ipc)
|
||||
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})
|
||||
|
||||
|
19
README.md
19
README.md
@ -1,31 +1,20 @@
|
||||
# Arduino core for the ESP32
|
||||
# Arduino core for the ESP32, ESP32-S2 and ESP32-C3
|
||||
|
||||
[](https://travis-ci.org/espressif/arduino-esp32)  [](https://docs.espressif.com/projects/arduino-esp32/en/latest/?badge=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?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
## Contents
|
||||
|
||||
- [ESP32-S2 and ESP32-C3 Support](#esp32-s2-and-esp32-c3-support)
|
||||
- [Development Status](#development-status)
|
||||
- [Decoding Exceptions](#decoding-exceptions)
|
||||
- [Issue/Bug report template](#issuebug-report-template)
|
||||
|
||||
### ESP32-S2 and ESP32-C3 Support
|
||||
|
||||
If you want to test ESP32-S2 and/or ESP32-C3 through the board manager, please use the development release link:
|
||||
|
||||
```
|
||||
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json
|
||||
```
|
||||
|
||||
Now you can install the latest 2.0.0 version from the boards manager.
|
||||
|
||||
### Development Status
|
||||
|
||||
Latest Stable Release [](https://github.com/espressif/arduino-esp32/releases/latest/) [](https://github.com/espressif/arduino-esp32/releases/latest/) [](https://github.com/espressif/arduino-esp32/releases/latest/)
|
||||
|
||||
Latest Development Release [](https://github.com/espressif/arduino-esp32/releases/latest/) [](https://github.com/espressif/arduino-esp32/releases/latest/) [](https://github.com/espressif/arduino-esp32/releases/latest/)
|
||||
Latest Development Release [](https://github.com/espressif/arduino-esp32/releases/) [](https://github.com/espressif/arduino-esp32/releases/) [](https://github.com/espressif/arduino-esp32/releases/)
|
||||
|
||||
### Documentation
|
||||
|
||||
@ -43,7 +32,7 @@ You can use [Arduino-ESP32 Online Documentation](https://docs.espressif.com/proj
|
||||
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 [for reference](https://github.com/espressif/arduino-esp32/issues?utf8=%E2%9C%93&q=is%3Aissue%20label%3A%22for%20reference%22%20).
|
||||
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](docs/ISSUE_TEMPLATE.md) while reporting any issue.
|
||||
|
||||
|
1262
boards.txt
1262
boards.txt
File diff suppressed because it is too large
Load Diff
@ -38,6 +38,7 @@ extern "C" {
|
||||
#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_ESP32C3
|
||||
#include "esp32c3/rom/spi_flash.h"
|
||||
@ -270,7 +271,17 @@ const char * EspClass::getChipModel(void)
|
||||
return "Unknown";
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
return "ESP32-S2";
|
||||
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
|
||||
|
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 */
|
388
cores/esp32/HWCDC.cpp
Normal file
388
cores/esp32/HWCDC.cpp
Normal file
@ -0,0 +1,388 @@
|
||||
// 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
|
||||
|
||||
#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_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY | USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT | USB_SERIAL_JTAG_INTR_BUS_RESET);
|
||||
usb_serial_jtag_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;
|
||||
}
|
||||
}
|
||||
|
||||
void HWCDC::end()
|
||||
{
|
||||
//Disable tx/rx interrupt.
|
||||
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY | USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT | USB_SERIAL_JTAG_INTR_BUS_RESET);
|
||||
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_HW_CDC_ON_BOOT //Serial used for USB CDC
|
||||
HWCDC Serial;
|
||||
#else
|
||||
HWCDC USBSerial;
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_TINYUSB_CDC_ENABLED */
|
107
cores/esp32/HWCDC.h
Normal file
107
cores/esp32/HWCDC.h
Normal file
@ -0,0 +1,107 @@
|
||||
// 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
|
||||
|
||||
#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_HW_CDC_ON_BOOT //Serial used for USB CDC
|
||||
extern HWCDC Serial;
|
||||
#else
|
||||
extern HWCDC USBSerial;
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_IDF_TARGET_ESP32C3 */
|
@ -5,87 +5,141 @@
|
||||
|
||||
#include "pins_arduino.h"
|
||||
#include "HardwareSerial.h"
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#ifndef SOC_RX0
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#define SOC_RX0 3
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#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
|
||||
#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
|
||||
#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
|
||||
#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
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef TX2
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#define TX2 17
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#ifndef RX1
|
||||
#define RX1 18
|
||||
#endif
|
||||
|
||||
#ifndef TX1
|
||||
#define TX1 17
|
||||
#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_SERIAL_PORT //Serial used for USB CDC
|
||||
#if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC
|
||||
HardwareSerial Serial0(0);
|
||||
#elif ARDUINO_HW_CDC_ON_BOOT
|
||||
HardwareSerial Serial0(0);
|
||||
#else
|
||||
HardwareSerial Serial(0);
|
||||
#endif
|
||||
#if SOC_UART_NUM > 1
|
||||
HardwareSerial Serial1(1);
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#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();
|
||||
#elif ARDUINO_HW_CDC_ON_BOOT
|
||||
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
|
||||
|
||||
HardwareSerial::HardwareSerial(int uart_nr) : _uart_nr(uart_nr), _uart(NULL) {}
|
||||
|
||||
HardwareSerial::HardwareSerial(int uart_nr) : _uart_nr(uart_nr), _uart(NULL), _rxBufferSize(256) {}
|
||||
|
||||
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 > 2) {
|
||||
log_e("Serial number is invalid, please use 0, 1 or 2");
|
||||
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(_uart) {
|
||||
end();
|
||||
// in this case it is a begin() over a previous begin() - maybe to change baud rate
|
||||
// thus do not disable debug output
|
||||
end(false);
|
||||
}
|
||||
if(_uart_nr == 0 && rxPin < 0 && txPin < 0) {
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
rxPin = 3;
|
||||
txPin = 1;
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
rxPin = 44;
|
||||
txPin = 43;
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
rxPin = 20;
|
||||
txPin = 21;
|
||||
#endif
|
||||
rxPin = SOC_RX0;
|
||||
txPin = SOC_TX0;
|
||||
}
|
||||
#if SOC_UART_NUM > 1
|
||||
if(_uart_nr == 1 && rxPin < 0 && txPin < 0) {
|
||||
rxPin = RX1;
|
||||
txPin = TX1;
|
||||
}
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#endif
|
||||
#if SOC_UART_NUM > 2
|
||||
if(_uart_nr == 2 && rxPin < 0 && txPin < 0) {
|
||||
rxPin = RX2;
|
||||
txPin = TX2;
|
||||
}
|
||||
#endif
|
||||
_uart = uartBegin(_uart_nr, baud ? baud : 9600, config, rxPin, txPin, 256, invert, rxfifo_full_thrhd);
|
||||
_tx_pin = txPin;
|
||||
_rx_pin = rxPin;
|
||||
|
||||
if(!baud) {
|
||||
_uart = uartBegin(_uart_nr, baud ? baud : 9600, config, rxPin, txPin, _rxBufferSize, 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;
|
||||
@ -93,16 +147,14 @@ void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, in
|
||||
yield();
|
||||
}
|
||||
|
||||
end();
|
||||
end(false);
|
||||
|
||||
if(detectedBaudRate) {
|
||||
delay(100); // Give some time...
|
||||
_uart = uartBegin(_uart_nr, detectedBaudRate, config, rxPin, txPin, 256, invert, rxfifo_full_thrhd);
|
||||
_uart = uartBegin(_uart_nr, detectedBaudRate, config, rxPin, txPin, _rxBufferSize, 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;
|
||||
_tx_pin = 255;
|
||||
_rx_pin = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -112,21 +164,16 @@ void HardwareSerial::updateBaudRate(unsigned long baud)
|
||||
uartSetBaudRate(_uart, baud);
|
||||
}
|
||||
|
||||
void HardwareSerial::end()
|
||||
void HardwareSerial::end(bool turnOffDebug)
|
||||
{
|
||||
if(uartGetDebug() == _uart_nr) {
|
||||
if(turnOffDebug && uartGetDebug() == _uart_nr) {
|
||||
uartSetDebug(0);
|
||||
}
|
||||
delay(10);
|
||||
log_v("pins %d %d",_tx_pin, _rx_pin);
|
||||
uartEnd(_uart, _tx_pin, _rx_pin);
|
||||
uartEnd(_uart);
|
||||
_uart = 0;
|
||||
}
|
||||
|
||||
size_t HardwareSerial::setRxBufferSize(size_t new_size) {
|
||||
return uartResizeRxBuffer(_uart, new_size);
|
||||
}
|
||||
|
||||
void HardwareSerial::setDebugOutput(bool en)
|
||||
{
|
||||
if(_uart == 0) {
|
||||
@ -212,10 +259,31 @@ uint32_t HardwareSerial::baudRate()
|
||||
}
|
||||
HardwareSerial::operator bool() const
|
||||
{
|
||||
return true;
|
||||
return uartIsDriverInstalled(_uart);
|
||||
}
|
||||
|
||||
void HardwareSerial::setRxInvert(bool invert)
|
||||
{
|
||||
uartSetRxInvert(_uart, invert);
|
||||
}
|
||||
|
||||
void HardwareSerial::setPins(uint8_t rxPin, uint8_t txPin)
|
||||
{
|
||||
uartSetPins(_uart, rxPin, txPin);
|
||||
}
|
||||
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_rxBufferSize = new_size;
|
||||
return _rxBufferSize;
|
||||
}
|
||||
|
@ -49,6 +49,8 @@
|
||||
|
||||
#include "Stream.h"
|
||||
#include "esp32-hal.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "HWCDC.h"
|
||||
|
||||
class HardwareSerial: public Stream
|
||||
{
|
||||
@ -56,7 +58,7 @@ public:
|
||||
HardwareSerial(int uart_nr);
|
||||
|
||||
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();
|
||||
void end(bool turnOffDebug = true);
|
||||
void updateBaudRate(unsigned long baud);
|
||||
int available(void);
|
||||
int availableForWrite(void);
|
||||
@ -98,33 +100,37 @@ public:
|
||||
uint32_t baudRate();
|
||||
operator bool() const;
|
||||
|
||||
size_t setRxBufferSize(size_t);
|
||||
void setDebugOutput(bool);
|
||||
|
||||
void setRxInvert(bool);
|
||||
void setPins(uint8_t rxPin, uint8_t txPin);
|
||||
size_t setRxBufferSize(size_t new_size);
|
||||
|
||||
protected:
|
||||
int _uart_nr;
|
||||
uart_t* _uart;
|
||||
uint8_t _tx_pin;
|
||||
uint8_t _rx_pin;
|
||||
size_t _rxBufferSize;
|
||||
};
|
||||
|
||||
extern void serialEventRun(void) __attribute__((weak));
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL)
|
||||
#ifndef ARDUINO_SERIAL_PORT
|
||||
#define ARDUINO_SERIAL_PORT 0
|
||||
#ifndef ARDUINO_USB_CDC_ON_BOOT
|
||||
#define ARDUINO_USB_CDC_ON_BOOT 0
|
||||
#endif
|
||||
#if ARDUINO_SERIAL_PORT //Serial used for USB CDC
|
||||
#if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC
|
||||
#include "USB.h"
|
||||
#include "USBCDC.h"
|
||||
extern HardwareSerial Serial0;
|
||||
#elif ARDUINO_HW_CDC_ON_BOOT
|
||||
extern HardwareSerial Serial0;
|
||||
#else
|
||||
extern HardwareSerial Serial;
|
||||
#endif
|
||||
#if SOC_UART_NUM > 1
|
||||
extern HardwareSerial Serial1;
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#endif
|
||||
#if SOC_UART_NUM > 2
|
||||
extern HardwareSerial Serial2;
|
||||
#endif
|
||||
#endif
|
||||
|
@ -256,7 +256,7 @@ float Stream::parseFloat(char skipChar)
|
||||
} else if(c >= '0' && c <= '9') { // is c a digit?
|
||||
value = value * 10 + c - '0';
|
||||
if(isFraction) {
|
||||
fraction *= 0.1;
|
||||
fraction *= 0.1f;
|
||||
}
|
||||
}
|
||||
read(); // consume the character we got with peek
|
||||
|
@ -11,10 +11,14 @@
|
||||
// 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 "USB.h"
|
||||
#if CONFIG_TINYUSB_ENABLED
|
||||
#include "common/tusb_common.h"
|
||||
|
||||
#ifndef USB_VID
|
||||
#define USB_VID USB_ESPRESSIF_VID
|
||||
@ -31,14 +35,16 @@
|
||||
#ifndef USB_SERIAL
|
||||
#define USB_SERIAL "0"
|
||||
#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_ATTR_CAN_DOWNLOAD 1
|
||||
#define DFU_ATTR_CAN_UPLOAD 2
|
||||
#define DFU_ATTR_MANIFESTATION_TOLERANT 4
|
||||
#define DFU_ATTR_WILL_DETACH 8
|
||||
#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");
|
||||
@ -80,14 +86,14 @@ 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 = {0};
|
||||
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 = {0};
|
||||
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);
|
||||
}
|
||||
|
||||
@ -95,7 +101,7 @@ void tud_umount_cb(void){
|
||||
// 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 = {0};
|
||||
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);
|
||||
}
|
||||
@ -103,7 +109,7 @@ void tud_suspend_cb(bool remote_wakeup_en){
|
||||
// Invoked when usb bus is resumed
|
||||
void tud_resume_cb(void){
|
||||
tinyusb_device_suspended = false;
|
||||
arduino_usb_event_data_t p = {0};
|
||||
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);
|
||||
}
|
||||
|
||||
@ -120,8 +126,8 @@ ESPUSB::ESPUSB(size_t task_stack_size, uint8_t event_task_priority)
|
||||
,usb_protocol(MISC_PROTOCOL_IAD)
|
||||
,usb_attributes(TUSB_DESC_CONFIG_ATT_SELF_POWERED)
|
||||
,usb_power_ma(500)
|
||||
,webusb_enabled(false)
|
||||
,webusb_url("https://espressif.github.io/arduino-esp32/webusb.html")
|
||||
,webusb_enabled(USB_WEBUSB_ENABLED)
|
||||
,webusb_url(USB_WEBUSB_URL)
|
||||
,_started(false)
|
||||
,_task_stack_size(task_stack_size)
|
||||
,_event_task_priority(event_task_priority)
|
||||
|
@ -14,12 +14,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if CONFIG_TINYUSB_ENABLED
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "esp_event.h"
|
||||
#include "USBCDC.h"
|
||||
|
||||
#include "esp_event.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);
|
||||
|
||||
|
@ -11,24 +11,22 @@
|
||||
// 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 "esp32-hal-tinyusb.h"
|
||||
#include "USB.h"
|
||||
#if CONFIG_TINYUSB_CDC_ENABLED
|
||||
|
||||
#include "USBCDC.h"
|
||||
#if CONFIG_TINYUSB_ENABLED
|
||||
#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);
|
||||
|
||||
#if CFG_TUD_CDC
|
||||
#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");
|
||||
// Interface number, string index, attributes, detach timeout, transfer size */
|
||||
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)
|
||||
@ -41,7 +39,6 @@ static uint16_t load_cdc_descriptor(uint8_t * dst, uint8_t * itf)
|
||||
// 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)
|
||||
{
|
||||
//isr_log_v("itf: %u, dtr: %u, rts: %u", itf, dtr, rts);
|
||||
if(itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL){
|
||||
devices[itf]->_onLineState(dtr, rts);
|
||||
}
|
||||
@ -50,7 +47,6 @@ void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool 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)
|
||||
{
|
||||
//isr_log_v("itf: %u, bit_rate: %u, data_bits: %u, stop_bits: %u, parity: %u", itf, p_line_coding->bit_rate, p_line_coding->data_bits, p_line_coding->stop_bits, p_line_coding->parity);
|
||||
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);
|
||||
}
|
||||
@ -59,7 +55,6 @@ void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding)
|
||||
// Invoked when received new data
|
||||
void tud_cdc_rx_cb(uint8_t itf)
|
||||
{
|
||||
//isr_log_v("itf: %u", itf);
|
||||
if(itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL){
|
||||
devices[itf]->_onRX();
|
||||
}
|
||||
@ -67,67 +62,50 @@ void tud_cdc_rx_cb(uint8_t itf)
|
||||
|
||||
// Invoked when received send break
|
||||
void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms){
|
||||
//isr_log_v("itf: %u, duration_ms: %u", itf, 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){
|
||||
//isr_log_v("itf: %u", itf);
|
||||
if(itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL){
|
||||
xSemaphoreGive(devices[itf]->tx_sem);
|
||||
devices[itf]->_onTX();
|
||||
}
|
||||
}
|
||||
|
||||
static size_t tinyusb_cdc_write(uint8_t itf, const uint8_t *buffer, size_t size){
|
||||
if(itf >= MAX_USB_CDC_DEVICES){
|
||||
return 0;
|
||||
static void ARDUINO_ISR_ATTR cdc0_write_char(char c){
|
||||
if(devices[0] != NULL){
|
||||
devices[0]->write(c);
|
||||
}
|
||||
if(!tud_cdc_n_connected(itf)){
|
||||
return 0;
|
||||
}
|
||||
size_t tosend = size, sofar = 0;
|
||||
while(tosend){
|
||||
uint32_t space = tud_cdc_n_write_available(itf);
|
||||
if(!space){
|
||||
delay(1);
|
||||
continue;
|
||||
}
|
||||
if(tosend < space){
|
||||
space = tosend;
|
||||
}
|
||||
uint32_t sent = tud_cdc_n_write(itf, buffer + sofar, space);
|
||||
if(!sent){
|
||||
return sofar;
|
||||
}
|
||||
sofar += sent;
|
||||
tosend -= sent;
|
||||
tud_cdc_n_write_flush(itf);
|
||||
xSemaphoreTake(devices[itf]->tx_sem, portMAX_DELAY);
|
||||
}
|
||||
return sofar;
|
||||
}
|
||||
|
||||
static void ARDUINO_ISR_ATTR cdc0_write_char(char c)
|
||||
{
|
||||
tinyusb_cdc_write(0, (const uint8_t *)&c, 1);
|
||||
}
|
||||
|
||||
//void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char);
|
||||
|
||||
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) {
|
||||
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){
|
||||
devices[itf] = this;
|
||||
tx_sem = NULL;
|
||||
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);
|
||||
}
|
||||
@ -137,6 +115,10 @@ void USBCDC::onEvent(arduino_usb_cdc_event_t event, esp_event_handler_t callback
|
||||
|
||||
size_t USBCDC::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));
|
||||
@ -148,27 +130,33 @@ size_t USBCDC::setRxBufferSize(size_t rx_queue_len){
|
||||
|
||||
void USBCDC::begin(unsigned long baud)
|
||||
{
|
||||
setRxBufferSize(256);//default if not preset
|
||||
if(tx_sem == NULL){
|
||||
tx_sem = xSemaphoreCreateBinary();
|
||||
xSemaphoreTake(tx_sem, 0);
|
||||
if(tx_lock == NULL) {
|
||||
tx_lock = xSemaphoreCreateMutex();
|
||||
}
|
||||
setRxBufferSize(256);//default if not preset
|
||||
devices[itf] = this;
|
||||
}
|
||||
|
||||
void USBCDC::end()
|
||||
{
|
||||
if (tx_sem != NULL) {
|
||||
vSemaphoreDelete(tx_sem);
|
||||
tx_sem = NULL;
|
||||
connected = false;
|
||||
devices[itf] = NULL;
|
||||
setRxBufferSize(0);
|
||||
if(tx_lock != NULL) {
|
||||
vSemaphoreDelete(tx_lock);
|
||||
}
|
||||
}
|
||||
|
||||
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 = {0};
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -176,6 +164,11 @@ void USBCDC::_onUnplugged(void){
|
||||
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;
|
||||
|
||||
@ -183,6 +176,11 @@ void USBCDC::_onLineState(bool _dtr, bool _rts){
|
||||
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;
|
||||
}
|
||||
@ -210,14 +208,14 @@ void USBCDC::_onLineState(bool _dtr, bool _rts){
|
||||
if(lineState == CDC_LINE_IDLE){
|
||||
if(dtr && rts && !connected){
|
||||
connected = true;
|
||||
arduino_usb_cdc_event_data_t p = {0};
|
||||
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 && !rts && connected){
|
||||
} else if(!dtr && connected){
|
||||
connected = false;
|
||||
arduino_usb_cdc_event_data_t p = {0};
|
||||
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 = {0};
|
||||
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);
|
||||
@ -228,14 +226,14 @@ void USBCDC::_onLineState(bool _dtr, bool _rts){
|
||||
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(_bit_rate == 1200){
|
||||
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 = {0};
|
||||
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;
|
||||
@ -253,13 +251,13 @@ void USBCDC::_onRX(){
|
||||
return;
|
||||
}
|
||||
}
|
||||
arduino_usb_cdc_event_data_t p = {0};
|
||||
arduino_usb_cdc_event_data_t p;
|
||||
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 = {0};
|
||||
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);
|
||||
}
|
||||
|
||||
@ -317,23 +315,73 @@ size_t USBCDC::read(uint8_t *buffer, size_t size)
|
||||
|
||||
void USBCDC::flush(void)
|
||||
{
|
||||
if(itf >= MAX_USB_CDC_DEVICES){
|
||||
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){
|
||||
return -1;
|
||||
if(itf >= MAX_USB_CDC_DEVICES || tx_lock == NULL || !tud_cdc_n_connected(itf)){
|
||||
return 0;
|
||||
}
|
||||
return tud_cdc_n_write_available(itf);
|
||||
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)
|
||||
{
|
||||
return tinyusb_cdc_write(itf, buffer, 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)
|
||||
@ -364,10 +412,8 @@ USBCDC::operator bool() const
|
||||
return connected;
|
||||
}
|
||||
|
||||
#if ARDUINO_SERIAL_PORT //Serial used for USB CDC
|
||||
#if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC
|
||||
USBCDC Serial(0);
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_TINYUSB_CDC_ENABLED */
|
||||
|
||||
#endif /* CONFIG_TINYUSB_ENABLED */
|
||||
|
@ -13,13 +13,15 @@
|
||||
// limitations under the License.
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "Stream.h"
|
||||
#include "esp32-hal.h"
|
||||
#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);
|
||||
|
||||
@ -54,11 +56,13 @@ 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_t setRxBufferSize(size_t size);
|
||||
void setTxTimeoutMs(uint32_t timeout);
|
||||
void begin(unsigned long baud=0);
|
||||
void end();
|
||||
|
||||
@ -113,7 +117,6 @@ public:
|
||||
void _onRX(void);
|
||||
void _onTX(void);
|
||||
void _onUnplugged(void);
|
||||
xSemaphoreHandle tx_sem;
|
||||
|
||||
protected:
|
||||
uint8_t itf;
|
||||
@ -126,10 +129,12 @@ protected:
|
||||
bool connected;
|
||||
bool reboot_enable;
|
||||
xQueueHandle rx_queue;
|
||||
xSemaphoreHandle tx_lock;
|
||||
uint32_t tx_timeout_ms;
|
||||
|
||||
};
|
||||
|
||||
#if ARDUINO_SERIAL_PORT //Serial used for USB CDC
|
||||
#if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC
|
||||
extern USBCDC Serial;
|
||||
#endif
|
||||
|
||||
|
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 */
|
@ -35,6 +35,12 @@ String::String(const char *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;
|
||||
@ -705,10 +711,7 @@ String String::substring(unsigned int left, unsigned int right) const {
|
||||
return out;
|
||||
if(right > len())
|
||||
right = len();
|
||||
char temp = buffer()[right]; // save the replaced character
|
||||
wbuffer()[right] = '\0';
|
||||
out = wbuffer() + left; // pointer arithmetic
|
||||
wbuffer()[right] = temp; //restore character
|
||||
out.copy(buffer() + left, right - left);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -55,6 +55,10 @@ class String {
|
||||
// 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__
|
||||
@ -108,6 +112,8 @@ class String {
|
||||
// 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);
|
||||
@ -326,7 +332,6 @@ class String {
|
||||
void init(void);
|
||||
void invalidate(void);
|
||||
unsigned char changeBuffer(unsigned int maxStrLen);
|
||||
unsigned char concat(const char *cstr, unsigned int length);
|
||||
|
||||
// copy and move
|
||||
String & copy(const char *cstr, unsigned int length);
|
||||
|
@ -37,7 +37,7 @@ static uint8_t __analogVRefPin = 0;
|
||||
#include "soc/rtc_io_reg.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#include "esp32c3/rom/ets_sys.h"
|
||||
#else
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
#else // ESP32 Before IDF 4.0
|
||||
@ -46,10 +46,23 @@ static uint8_t __analogVRefPin = 0;
|
||||
#endif
|
||||
|
||||
static uint8_t __analogAttenuation = 3;//11db
|
||||
static uint8_t __analogWidth = 3;//12 bits
|
||||
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 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;
|
||||
@ -104,7 +117,9 @@ void __analogSetPinAttenuation(uint8_t pin, adc_attenuation_t attenuation)
|
||||
adc1_config_channel_atten(channel, attenuation);
|
||||
}
|
||||
__analogInit();
|
||||
__pin_attenuation[pin] = attenuation;
|
||||
if((__pin_attenuation[pin] != ADC_ATTENDB_MAX) || (attenuation != __analogAttenuation)){
|
||||
__pin_attenuation[pin] = attenuation;
|
||||
}
|
||||
}
|
||||
|
||||
bool __adcAttachPin(uint8_t pin){
|
||||
@ -113,6 +128,7 @@ bool __adcAttachPin(uint8_t pin){
|
||||
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
|
||||
@ -143,6 +159,7 @@ 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
|
||||
@ -162,7 +179,7 @@ uint16_t __analogRead(uint8_t pin)
|
||||
channel -= 10;
|
||||
r = adc2_get_raw( channel, __analogWidth, &value);
|
||||
if ( r == ESP_OK ) {
|
||||
return value;
|
||||
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 ) {
|
||||
@ -171,9 +188,10 @@ uint16_t __analogRead(uint8_t pin)
|
||||
log_e("GPIO%u: %s", pin, esp_err_to_name(r));
|
||||
}
|
||||
} else {
|
||||
return adc1_get_raw(channel);
|
||||
value = adc1_get_raw(channel);
|
||||
return mapResolution(value);
|
||||
}
|
||||
return value;
|
||||
return mapResolution(value);
|
||||
}
|
||||
|
||||
uint32_t __analogReadMilliVolts(uint8_t pin){
|
||||
|
848
cores/esp32/esp32-hal-i2c-slave.c
Executable file
848
cores/esp32/esp32-hal-i2c-slave.c
Executable file
@ -0,0 +1,848 @@
|
||||
// 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
|
||||
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
|
||||
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
|
||||
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, 32000);
|
||||
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(!i2c->lock){
|
||||
log_e("Lock is not initialized! Did you call i2c_slave_init()?");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
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(!i2c->lock){
|
||||
log_e("Lock is not initialized! Did you call i2c_slave_init()?");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
if(!i2c->tx_queue){
|
||||
return 0;
|
||||
}
|
||||
I2C_SLAVE_MUTEX_LOCK();
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
//make sure that tx is idle
|
||||
uint64_t tout_at = esp_timer_get_time() + (timeout_ms * 1000);
|
||||
while(i2c_ll_slave_addressed(i2c->dev) && i2c_ll_slave_rw(i2c->dev)) {
|
||||
// ongoing MASTER READ
|
||||
//wait up to timeout_ms for current transaction to finish
|
||||
vTaskDelay(2);
|
||||
if((uint64_t)esp_timer_get_time() >= tout_at){
|
||||
log_e("TX IDLE WAIT TIMEOUT!");
|
||||
I2C_SLAVE_MUTEX_UNLOCK();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
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);
|
||||
uint8_t tx_fifo_len = SOC_I2C_FIFO_LEN - i2c_ll_get_txfifo_len(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
|
||||
//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
Executable file
35
cores/esp32/esp32-hal-i2c-slave.h
Executable 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
|
File diff suppressed because it is too large
Load Diff
@ -12,6 +12,7 @@
|
||||
// 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_
|
||||
@ -22,58 +23,16 @@ extern "C" {
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include <esp_err.h>
|
||||
|
||||
// External Wire.h equivalent error Codes
|
||||
typedef enum {
|
||||
I2C_ERROR_OK=0,
|
||||
I2C_ERROR_DEV,
|
||||
I2C_ERROR_ACK,
|
||||
I2C_ERROR_TIMEOUT,
|
||||
I2C_ERROR_BUS,
|
||||
I2C_ERROR_BUSY,
|
||||
I2C_ERROR_MEMORY,
|
||||
I2C_ERROR_CONTINUE,
|
||||
I2C_ERROR_NO_BEGIN
|
||||
} i2c_err_t;
|
||||
|
||||
struct i2c_struct_t;
|
||||
typedef struct i2c_struct_t i2c_t;
|
||||
|
||||
i2c_t * i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t clk_speed);
|
||||
void i2cRelease(i2c_t *i2c); // free ISR, Free DQ, Power off peripheral clock. Must call i2cInit() to recover
|
||||
i2c_err_t i2cWrite(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size, bool sendStop, uint16_t timeOutMillis);
|
||||
i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size, bool sendStop, uint16_t timeOutMillis, uint32_t *readCount);
|
||||
i2c_err_t i2cFlush(i2c_t *i2c);
|
||||
i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed);
|
||||
uint32_t i2cGetFrequency(i2c_t * i2c);
|
||||
uint32_t i2cGetStatus(i2c_t * i2c); // Status register of peripheral
|
||||
|
||||
//Functions below should be used only if well understood
|
||||
//Might be deprecated and removed in future
|
||||
i2c_err_t i2cAttachSCL(i2c_t * i2c, int8_t scl);
|
||||
i2c_err_t i2cDetachSCL(i2c_t * i2c, int8_t scl);
|
||||
i2c_err_t i2cAttachSDA(i2c_t * i2c, int8_t sda);
|
||||
i2c_err_t i2cDetachSDA(i2c_t * i2c, int8_t sda);
|
||||
|
||||
//Stickbreakers ISR Support
|
||||
i2c_err_t i2cProcQueue(i2c_t *i2c, uint32_t *readCount, uint16_t timeOutMillis);
|
||||
i2c_err_t i2cAddQueueWrite(i2c_t *i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen, bool SendStop, EventGroupHandle_t event);
|
||||
i2c_err_t i2cAddQueueRead(i2c_t *i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen, bool SendStop, EventGroupHandle_t event);
|
||||
|
||||
//stickbreaker debug support
|
||||
uint32_t i2cDebug(i2c_t *, uint32_t setBits, uint32_t resetBits);
|
||||
// Debug actions have 3 currently defined locus
|
||||
// 0xXX------ : at entry of ProcQueue
|
||||
// 0x--XX---- : at exit of ProcQueue
|
||||
// 0x------XX : at entry of Flush
|
||||
//
|
||||
// bit 0 causes DumpI2c to execute
|
||||
// bit 1 causes DumpInts to execute
|
||||
// bit 2 causes DumpCmdqueue to execute
|
||||
// bit 3 causes DumpStatus to execute
|
||||
// bit 4 causes DumpFifo to execute
|
||||
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
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp32-hal-matrix.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/ledc_reg.h"
|
||||
#include "soc/ledc_struct.h"
|
||||
#include "driver/periph_ctrl.h"
|
||||
@ -115,6 +116,10 @@ static void _ledcSetupTimer(uint8_t chan, uint32_t div_num, uint8_t bit_num, boo
|
||||
uint8_t group=(chan/8), timer=((chan/2)%4);
|
||||
static bool tHasStarted = false;
|
||||
static uint16_t _activeChannels = 0;
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
// ESP32-S2 TRM v1.0 on Page 789 -> BIT LEDC_TICK_SEL_TIMERx is 0 for LEDC_PWM_CLK and 1 for REF_TICK
|
||||
apb_clk = 0;
|
||||
#endif
|
||||
if(!tHasStarted) {
|
||||
tHasStarted = true;
|
||||
periph_module_enable(PERIPH_LEDC_MODULE);
|
||||
@ -327,3 +332,21 @@ double ledcChangeFrequency(uint8_t chan, double freq, uint8_t bit_num)
|
||||
double res_freq = _ledcSetupTimerFreq(chan, freq, bit_num);
|
||||
return res_freq;
|
||||
}
|
||||
|
||||
static int8_t pin_to_channel[SOC_GPIO_PIN_COUNT] = { 0 };
|
||||
static int cnt_channel = SOC_LEDC_CHANNEL_NUM;
|
||||
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", SOC_LEDC_CHANNEL_NUM);
|
||||
return;
|
||||
}
|
||||
pin_to_channel[pin] = cnt_channel--;
|
||||
ledcAttachPin(pin, cnt_channel);
|
||||
ledcSetup(cnt_channel, 1000, 8);
|
||||
}
|
||||
ledcWrite(pin_to_channel[pin] - 1, value);
|
||||
}
|
||||
}
|
||||
|
@ -66,6 +66,8 @@ extern "C"
|
||||
#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
|
||||
@ -73,12 +75,15 @@ extern "C"
|
||||
#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__
|
||||
@ -87,78 +92,96 @@ int log_printf(const char *fmt, ...);
|
||||
#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, ...)
|
||||
#define isr_log_v(format, ...)
|
||||
#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, ...)
|
||||
#define isr_log_d(format, ...)
|
||||
#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, ...)
|
||||
#define isr_log_i(format, ...)
|
||||
#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, ...)
|
||||
#define isr_log_w(format, ...)
|
||||
#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 {log_to_esp(TAG, ESP_LOG_ERROR, 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, ...)
|
||||
#define isr_log_e(format, ...)
|
||||
#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, ...)
|
||||
#define isr_log_n(format, ...)
|
||||
#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"
|
||||
|
@ -546,16 +546,16 @@ float rmtSetTick(rmt_obj_t* rmt, float tick)
|
||||
size_t channel = rmt->channel;
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32C3
|
||||
int apb_div = _LIMIT(tick/25.0, 256);
|
||||
float apb_tick = 25.0 * apb_div;
|
||||
int apb_div = _LIMIT(tick/25.0f, 256);
|
||||
float apb_tick = 25.0f * apb_div;
|
||||
RMT.tx_conf[channel].div_cnt = apb_div & 0xFF;
|
||||
RMT.tx_conf[channel].conf_update = 1;
|
||||
return apb_tick;
|
||||
#else
|
||||
int apb_div = _LIMIT(tick/12.5, 256);
|
||||
int apb_div = _LIMIT(tick/12.5f, 256);
|
||||
int ref_div = _LIMIT(tick/1000, 256);
|
||||
float apb_tick = 12.5 * apb_div;
|
||||
float ref_tick = 1000.0 * ref_div;
|
||||
float apb_tick = 12.5f * apb_div;
|
||||
float ref_tick = 1000.0f * ref_div;
|
||||
if (_ABS(apb_tick - tick) < _ABS(ref_tick - tick)) {
|
||||
RMT.conf_ch[channel].conf0.div_cnt = apb_div & 0xFF;
|
||||
RMT.conf_ch[channel].conf1.ref_always_on = 1;
|
||||
|
@ -44,6 +44,8 @@ static void setTimeZone(long offset, int daylight)
|
||||
/*
|
||||
* configTime
|
||||
* Source: https://github.com/esp8266/Arduino/blob/master/cores/esp8266/time.c
|
||||
* Note: Bundled Arduino lwip supports only ONE ntp server, 2nd and 3rd options are silently ignored
|
||||
* see CONFIG_LWIP_DHCP_MAX_NTP_SERVERS define in ./tools/sdk/esp32/sdkconfig
|
||||
* */
|
||||
void configTime(long gmtOffset_sec, int daylightOffset_sec, const char* server1, const char* server2, const char* server3)
|
||||
{
|
||||
@ -63,6 +65,8 @@ void configTime(long gmtOffset_sec, int daylightOffset_sec, const char* server1,
|
||||
/*
|
||||
* configTzTime
|
||||
* sntp setup using TZ environment variable
|
||||
* Note: Bundled Arduino lwip supports only ONE ntp server, 2nd and 3rd options are silently ignored
|
||||
* see CONFIG_LWIP_DHCP_MAX_NTP_SERVERS define in ./tools/sdk/esp32/sdkconfig
|
||||
* */
|
||||
void configTzTime(const char* tz, const char* server1, const char* server2, const char* server3)
|
||||
{
|
||||
|
@ -90,17 +90,10 @@ typedef void (*voidFuncPtr)(void);
|
||||
static voidFuncPtr __timerInterruptHandlers[4] = {0,0,0,0};
|
||||
|
||||
void ARDUINO_ISR_ATTR __timerISR(void * arg){
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
uint32_t s0 = TIMERG0.int_st_timers.val;
|
||||
uint32_t s1 = TIMERG1.int_st_timers.val;
|
||||
TIMERG0.int_clr_timers.val = s0;
|
||||
TIMERG1.int_clr_timers.val = s1;
|
||||
#else
|
||||
uint32_t s0 = TIMERG0.int_st.val;
|
||||
uint32_t s1 = TIMERG1.int_st.val;
|
||||
TIMERG0.int_clr.val = s0;
|
||||
TIMERG1.int_clr.val = s1;
|
||||
#endif
|
||||
uint8_t status = (s1 & 3) << 2 | (s0 & 3);
|
||||
uint8_t i = 4;
|
||||
//restart the timers that should autoreload
|
||||
@ -119,8 +112,9 @@ void ARDUINO_ISR_ATTR __timerISR(void * arg){
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t timerRead(hw_timer_t *timer){
|
||||
uint64_t inline timerRead(hw_timer_t *timer){
|
||||
timer->dev->update = 1;
|
||||
while (timer->dev->update) {};
|
||||
uint64_t h = timer->dev->cnt_high;
|
||||
uint64_t l = timer->dev->cnt_low;
|
||||
return (h << 32) | l;
|
||||
@ -238,19 +232,19 @@ hw_timer_t * timerBegin(uint8_t num, uint16_t divider, bool countUp){
|
||||
}
|
||||
timer->dev->config.enable = 0;
|
||||
if(timer->group) {
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
TIMERG1.int_ena.val &= ~BIT(timer->timer);
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
TIMERG1.int_clr_timers.val |= BIT(timer->timer);
|
||||
#else
|
||||
TIMERG1.int_clr.val = BIT(timer->timer);
|
||||
TIMERG1.int_ena_timers.val &= ~BIT(timer->timer);
|
||||
#endif
|
||||
TIMERG1.int_clr_timers.val |= BIT(timer->timer);
|
||||
} else {
|
||||
TIMERG0.int_ena.val &= ~BIT(timer->timer);
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
TIMERG0.int_clr_timers.val |= BIT(timer->timer);
|
||||
TIMERG0.int_ena.val &= ~BIT(timer->timer);
|
||||
#else
|
||||
TIMERG0.int_clr.val = BIT(timer->timer);
|
||||
TIMERG0.int_ena_timers.val &= ~BIT(timer->timer);
|
||||
#endif
|
||||
TIMERG0.int_clr_timers.val |= BIT(timer->timer);
|
||||
}
|
||||
#ifdef TIMER_GROUP_SUPPORTS_XTAL_CLOCK
|
||||
timer->dev->config.use_xtal = 0;
|
||||
@ -288,19 +282,19 @@ void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge){
|
||||
timer->dev->config.edge_int_en = 0;
|
||||
timer->dev->config.alarm_en = 0;
|
||||
if(timer->num & 2){
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
TIMERG1.int_ena.val &= ~BIT(timer->timer);
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#else
|
||||
TIMERG1.int_ena_timers.val &= ~BIT(timer->timer);
|
||||
#endif
|
||||
TIMERG1.int_clr_timers.val |= BIT(timer->timer);
|
||||
#else
|
||||
TIMERG1.int_clr.val = BIT(timer->timer);
|
||||
#endif
|
||||
} else {
|
||||
TIMERG0.int_ena.val &= ~BIT(timer->timer);
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
TIMERG0.int_clr_timers.val |= BIT(timer->timer);
|
||||
TIMERG0.int_ena.val &= ~BIT(timer->timer);
|
||||
#else
|
||||
TIMERG0.int_clr.val = BIT(timer->timer);
|
||||
TIMERG0.int_ena_timers.val &= ~BIT(timer->timer);
|
||||
#endif
|
||||
TIMERG0.int_clr_timers.val |= BIT(timer->timer);
|
||||
}
|
||||
__timerInterruptHandlers[timer->num] = NULL;
|
||||
} else {
|
||||
@ -332,9 +326,17 @@ void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge){
|
||||
intr_matrix_set(esp_intr_get_cpu(intr_handle), intr_source, esp_intr_get_intno(intr_handle));
|
||||
}
|
||||
if(timer->group){
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
TIMERG1.int_ena.val |= BIT(timer->timer);
|
||||
#else
|
||||
TIMERG1.int_ena_timers.val |= BIT(timer->timer);
|
||||
#endif
|
||||
} else {
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
TIMERG0.int_ena.val |= BIT(timer->timer);
|
||||
#else
|
||||
TIMERG0.int_ena_timers.val |= BIT(timer->timer);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if(intr_handle){
|
||||
|
@ -72,20 +72,15 @@ static void configure_pins(usb_hal_context_t *usb)
|
||||
|
||||
esp_err_t tinyusb_driver_install(const tinyusb_config_t *config)
|
||||
{
|
||||
log_i("Driver installation...");
|
||||
|
||||
// Hal init
|
||||
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;
|
||||
}
|
||||
log_i("Driver installed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@ -106,6 +101,7 @@ 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;
|
||||
@ -140,7 +136,7 @@ static tusb_desc_device_t tinyusb_device_descriptor = {
|
||||
static uint32_t tinyusb_string_descriptor_len = 4;
|
||||
static char * tinyusb_string_descriptor[MAX_STRING_DESCRIPTORS] = {
|
||||
// array of pointer to string descriptors
|
||||
"\x09\x04", // 0: is supported language is English (0x0409)
|
||||
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
|
||||
@ -263,7 +259,7 @@ static tinyusb_endpoints_usage_t tinyusb_endpoints;
|
||||
/**
|
||||
* @brief Invoked when received GET CONFIGURATION DESCRIPTOR.
|
||||
*/
|
||||
uint8_t const *tud_descriptor_configuration_cb(uint8_t index)
|
||||
__attribute__ ((weak)) uint8_t const *tud_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
//log_d("%u", index);
|
||||
return tinyusb_config_descriptor;
|
||||
@ -272,7 +268,7 @@ uint8_t const *tud_descriptor_configuration_cb(uint8_t index)
|
||||
/**
|
||||
* @brief Invoked when received GET DEVICE DESCRIPTOR.
|
||||
*/
|
||||
uint8_t const *tud_descriptor_device_cb(void)
|
||||
__attribute__ ((weak)) uint8_t const *tud_descriptor_device_cb(void)
|
||||
{
|
||||
//log_d("");
|
||||
return (uint8_t const *)&tinyusb_device_descriptor;
|
||||
@ -281,7 +277,7 @@ uint8_t const *tud_descriptor_device_cb(void)
|
||||
/**
|
||||
* @brief Invoked when received GET STRING DESCRIPTOR request.
|
||||
*/
|
||||
uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid)
|
||||
__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];
|
||||
@ -428,12 +424,6 @@ static bool tinyusb_load_enabled_interfaces(){
|
||||
log_e("Descriptor Load Failed");
|
||||
return false;
|
||||
} else {
|
||||
if(i == 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 false;
|
||||
}
|
||||
}
|
||||
dst += len;
|
||||
}
|
||||
}
|
||||
@ -509,6 +499,7 @@ static void tinyusb_apply_device_config(tinyusb_device_config_t *config){
|
||||
&& (config->usb_class != TUSB_CLASS_CDC)
|
||||
){
|
||||
config->usb_class = TUSB_CLASS_CDC;
|
||||
config->usb_protocol = 0x00;
|
||||
}
|
||||
|
||||
WEBUSB_ENABLED = config->webusb_enabled;
|
||||
@ -563,31 +554,43 @@ static void usb_device_task(void *param) {
|
||||
/*
|
||||
* PUBLIC API
|
||||
* */
|
||||
static const char *tinyusb_interface_names[USB_INTERFACE_MAX] = {"MSC", "DFU", "HID", "VENDOR", "CDC", "MIDI", "CUSTOM"};
|
||||
|
||||
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((interface >= USB_INTERFACE_MAX) || (tinyusb_loaded_interfaces_mask & (1U << interface))){
|
||||
log_e("Interface %u not enabled", interface);
|
||||
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 %u enabled", interface);
|
||||
log_d("Interface %s enabled", tinyusb_interface_names[interface]);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t tinyusb_init(tinyusb_device_config_t *config) {
|
||||
static bool initialized = false;
|
||||
if(initialized){
|
||||
if(tinyusb_is_initialized){
|
||||
return ESP_OK;
|
||||
}
|
||||
initialized = true;
|
||||
tinyusb_is_initialized = true;
|
||||
|
||||
tinyusb_endpoints.val = 0;
|
||||
//tinyusb_endpoints.val = 0;
|
||||
tinyusb_apply_device_config(config);
|
||||
if (!tinyusb_load_enabled_interfaces()) {
|
||||
initialized = false;
|
||||
tinyusb_is_initialized = false;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
@ -605,7 +608,7 @@ esp_err_t tinyusb_init(tinyusb_device_config_t *config) {
|
||||
}
|
||||
|
||||
if (esp_register_shutdown_handler(usb_persist_shutdown_handler) != ESP_OK) {
|
||||
initialized = false;
|
||||
tinyusb_is_initialized = false;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
@ -614,7 +617,7 @@ esp_err_t tinyusb_init(tinyusb_device_config_t *config) {
|
||||
};
|
||||
esp_err_t err = tinyusb_driver_install(&tusb_cfg);
|
||||
if (err != ESP_OK) {
|
||||
initialized = false;
|
||||
tinyusb_is_initialized = false;
|
||||
return err;
|
||||
}
|
||||
xTaskCreate(usb_device_task, "usbd", 4096, NULL, configMAX_PRIORITIES - 1, NULL);
|
||||
@ -690,84 +693,4 @@ uint8_t tinyusb_get_free_out_endpoint(void){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
void usb_dw_reg_dump(void)
|
||||
{
|
||||
#define USB_PRINT_REG(r) printf("USB0." #r " = 0x%x;\n", USB0.r)
|
||||
#define USB_PRINT_IREG(i, r) printf("USB0.in_ep_reg[%u]." #r " = 0x%x;\n", i, USB0.in_ep_reg[i].r)
|
||||
#define USB_PRINT_OREG(i, r) printf("USB0.out_ep_reg[%u]." #r " = 0x%x;\n", i, USB0.out_ep_reg[i].r)
|
||||
uint8_t i;
|
||||
USB_PRINT_REG(gotgctl);
|
||||
USB_PRINT_REG(gotgint);
|
||||
USB_PRINT_REG(gahbcfg);
|
||||
USB_PRINT_REG(gusbcfg);
|
||||
USB_PRINT_REG(grstctl);
|
||||
USB_PRINT_REG(gintsts);
|
||||
USB_PRINT_REG(gintmsk);
|
||||
USB_PRINT_REG(grxstsr);
|
||||
USB_PRINT_REG(grxstsp);
|
||||
USB_PRINT_REG(grxfsiz);
|
||||
USB_PRINT_REG(gnptxsts);
|
||||
USB_PRINT_REG(gpvndctl);
|
||||
USB_PRINT_REG(ggpio);
|
||||
USB_PRINT_REG(guid);
|
||||
USB_PRINT_REG(gsnpsid);
|
||||
USB_PRINT_REG(ghwcfg1);
|
||||
USB_PRINT_REG(ghwcfg2);
|
||||
USB_PRINT_REG(ghwcfg3);
|
||||
USB_PRINT_REG(ghwcfg4);
|
||||
USB_PRINT_REG(glpmcfg);
|
||||
USB_PRINT_REG(gpwrdn);
|
||||
USB_PRINT_REG(gdfifocfg);
|
||||
USB_PRINT_REG(gadpctl);
|
||||
USB_PRINT_REG(hptxfsiz);
|
||||
USB_PRINT_REG(hcfg);
|
||||
USB_PRINT_REG(hfir);
|
||||
USB_PRINT_REG(hfnum);
|
||||
USB_PRINT_REG(hptxsts);
|
||||
USB_PRINT_REG(haint);
|
||||
USB_PRINT_REG(haintmsk);
|
||||
USB_PRINT_REG(hflbaddr);
|
||||
USB_PRINT_REG(hprt);
|
||||
USB_PRINT_REG(dcfg);
|
||||
USB_PRINT_REG(dctl);
|
||||
USB_PRINT_REG(dsts);
|
||||
USB_PRINT_REG(diepmsk);
|
||||
USB_PRINT_REG(doepmsk);
|
||||
USB_PRINT_REG(daint);
|
||||
USB_PRINT_REG(daintmsk);
|
||||
USB_PRINT_REG(dtknqr1);
|
||||
USB_PRINT_REG(dtknqr2);
|
||||
USB_PRINT_REG(dvbusdis);
|
||||
USB_PRINT_REG(dvbuspulse);
|
||||
USB_PRINT_REG(dtknqr3_dthrctl);
|
||||
USB_PRINT_REG(dtknqr4_fifoemptymsk);
|
||||
USB_PRINT_REG(deachint);
|
||||
USB_PRINT_REG(deachintmsk);
|
||||
USB_PRINT_REG(pcgctrl);
|
||||
USB_PRINT_REG(pcgctrl1);
|
||||
USB_PRINT_REG(gnptxfsiz);
|
||||
for (i = 0; i < 4; i++) {
|
||||
printf("USB0.dieptxf[%u] = 0x%x;\n", i, USB0.dieptxf[i]);
|
||||
}
|
||||
// for (i = 0; i < 16; i++) {
|
||||
// printf("USB0.diepeachintmsk[%u] = 0x%x;\n", i, USB0.diepeachintmsk[i]);
|
||||
// }
|
||||
// for (i = 0; i < 16; i++) {
|
||||
// printf("USB0.doepeachintmsk[%u] = 0x%x;\n", i, USB0.doepeachintmsk[i]);
|
||||
// }
|
||||
for (i = 0; i < 7; i++) {
|
||||
printf("// EP %u:\n", i);
|
||||
USB_PRINT_IREG(i, diepctl);
|
||||
USB_PRINT_IREG(i, diepint);
|
||||
USB_PRINT_IREG(i, dieptsiz);
|
||||
USB_PRINT_IREG(i, diepdma);
|
||||
USB_PRINT_IREG(i, dtxfsts);
|
||||
USB_PRINT_OREG(i, doepctl);
|
||||
USB_PRINT_OREG(i, doepint);
|
||||
USB_PRINT_OREG(i, doeptsiz);
|
||||
USB_PRINT_OREG(i, doepdma);
|
||||
}
|
||||
}
|
||||
*/
|
||||
#endif /* CONFIG_TINYUSB_ENABLED */
|
||||
|
@ -82,11 +82,11 @@ 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_CDC,
|
||||
USB_INTERFACE_MSC,
|
||||
USB_INTERFACE_DFU,
|
||||
USB_INTERFACE_HID,
|
||||
USB_INTERFACE_VENDOR,
|
||||
USB_INTERFACE_MSC,
|
||||
USB_INTERFACE_CDC,
|
||||
USB_INTERFACE_MIDI,
|
||||
USB_INTERFACE_CUSTOM,
|
||||
USB_INTERFACE_MAX
|
||||
|
@ -95,6 +95,7 @@ void __touchInit()
|
||||
SET_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_MEAS_EN_CLR);
|
||||
//clear touch enable
|
||||
WRITE_PERI_REG(SENS_SAR_TOUCH_ENABLE_REG, 0x0);
|
||||
SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_TOUCH_SLP_TIMER_EN);
|
||||
__touchSetCycles(__touchMeasureCycles, __touchSleepCycles);
|
||||
esp_intr_alloc(ETS_RTC_CORE_INTR_SOURCE, (int)ARDUINO_ISR_FLAG, __touchISR, NULL, &touch_intr_handle);
|
||||
#else
|
||||
|
@ -14,205 +14,88 @@
|
||||
|
||||
#include "esp32-hal-uart.h"
|
||||
#include "esp32-hal.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_attr.h"
|
||||
#include "soc/uart_reg.h"
|
||||
#include "soc/uart_struct.h"
|
||||
#include "soc/io_mux_reg.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
#include "soc/rtc.h"
|
||||
|
||||
#include "driver/uart.h"
|
||||
#include "hal/uart_ll.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
|
||||
#include "esp_system.h"
|
||||
#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+
|
||||
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||
#include "soc/dport_reg.h"
|
||||
#include "esp32/rom/ets_sys.h"
|
||||
#include "esp32/rom/uart.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "soc/dport_reg.h"
|
||||
#include "esp32s2/rom/ets_sys.h"
|
||||
#include "esp32s2/rom/uart.h"
|
||||
#include "soc/periph_defs.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#include "esp32c3/rom/ets_sys.h"
|
||||
#include "esp32c3/rom/uart.h"
|
||||
#include "soc/periph_defs.h"
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
#else // ESP32 Before IDF 4.0
|
||||
#include "rom/ets_sys.h"
|
||||
#include "rom/uart.h"
|
||||
#include "esp_intr.h"
|
||||
#endif
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#define UART_PORTS_NUM 3
|
||||
#define UART_REG_BASE(u) ((u==0)?DR_REG_UART_BASE:( (u==1)?DR_REG_UART1_BASE:( (u==2)?DR_REG_UART2_BASE:0)))
|
||||
#define UART_RXD_IDX(u) ((u==0)?U0RXD_IN_IDX:( (u==1)?U1RXD_IN_IDX:( (u==2)?U2RXD_IN_IDX:0)))
|
||||
#define UART_TXD_IDX(u) ((u==0)?U0TXD_OUT_IDX:( (u==1)?U1TXD_OUT_IDX:( (u==2)?U2TXD_OUT_IDX:0)))
|
||||
#define UART_INTR_SOURCE(u) ((u==0)?ETS_UART0_INTR_SOURCE:( (u==1)?ETS_UART1_INTR_SOURCE:((u==2)?ETS_UART2_INTR_SOURCE:0)))
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#define UART_PORTS_NUM 2
|
||||
#define UART_REG_BASE(u) ((u==0)?DR_REG_UART_BASE:( (u==1)?DR_REG_UART1_BASE:0))
|
||||
#define UART_RXD_IDX(u) ((u==0)?U0RXD_IN_IDX:( (u==1)?U1RXD_IN_IDX:0))
|
||||
#define UART_TXD_IDX(u) ((u==0)?U0TXD_OUT_IDX:( (u==1)?U1TXD_OUT_IDX:0))
|
||||
#define UART_INTR_SOURCE(u) ((u==0)?ETS_UART0_INTR_SOURCE:( (u==1)?ETS_UART1_INTR_SOURCE:0))
|
||||
#else
|
||||
#define UART_PORTS_NUM 2
|
||||
#define UART_REG_BASE(u) ((u==0)?DR_REG_UART_BASE:( (u==1)?DR_REG_UART1_BASE:0))
|
||||
#define UART_RXD_IDX(u) ((u==0)?U0RXD_IN_IDX:( (u==1)?U1RXD_IN_IDX:0))
|
||||
#define UART_TXD_IDX(u) ((u==0)?U0TXD_OUT_IDX:( (u==1)?U1TXD_OUT_IDX:0))
|
||||
#define UART_INTR_SOURCE(u) ((u==0)?ETS_UART0_INTR_SOURCE:( (u==1)?ETS_UART1_INTR_SOURCE:0))
|
||||
#endif
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/uart_struct.h"
|
||||
|
||||
static int s_uart_debug_nr = 0;
|
||||
|
||||
struct uart_struct_t {
|
||||
uart_dev_t * dev;
|
||||
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
xSemaphoreHandle lock;
|
||||
#endif
|
||||
|
||||
uint8_t num;
|
||||
xQueueHandle queue;
|
||||
intr_handle_t intr_handle;
|
||||
bool has_peek;
|
||||
uint8_t peek_byte;
|
||||
|
||||
};
|
||||
|
||||
#if CONFIG_DISABLE_HAL_LOCKS
|
||||
|
||||
#define UART_MUTEX_LOCK()
|
||||
#define UART_MUTEX_UNLOCK()
|
||||
|
||||
static uart_t _uart_bus_array[] = {
|
||||
{&UART0, 0, NULL, NULL},
|
||||
{&UART1, 1, NULL, NULL},
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
{&UART2, 2, NULL, NULL}
|
||||
{0, false, 0},
|
||||
#if SOC_UART_NUM > 1
|
||||
{1, false, 0},
|
||||
#endif
|
||||
#if SOC_UART_NUM > 2
|
||||
{2, false, 0},
|
||||
#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[] = {
|
||||
{&UART0, NULL, 0, NULL, NULL},
|
||||
{&UART1, NULL, 1, NULL, NULL},
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
{&UART2, NULL, 2, NULL, NULL}
|
||||
{NULL, 0, false, 0},
|
||||
#if SOC_UART_NUM > 1
|
||||
{NULL, 1, false, 0},
|
||||
#endif
|
||||
#if SOC_UART_NUM > 2
|
||||
{NULL, 2, false, 0},
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
static void uart_on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb);
|
||||
|
||||
static void ARDUINO_ISR_ATTR _uart_isr(void *arg)
|
||||
{
|
||||
uint8_t i, c;
|
||||
BaseType_t xHigherPriorityTaskWoken;
|
||||
uart_t* uart;
|
||||
|
||||
for(i=0;i<UART_PORTS_NUM;i++){
|
||||
uart = &_uart_bus_array[i];
|
||||
if(uart->intr_handle == NULL){
|
||||
continue;
|
||||
}
|
||||
uart->dev->int_clr.rxfifo_full = 1;
|
||||
uart->dev->int_clr.frm_err = 1;
|
||||
uart->dev->int_clr.rxfifo_tout = 1;
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
while(uart->dev->status.rxfifo_cnt || (uart->dev->mem_rx_status.wr_addr != uart->dev->mem_rx_status.rd_addr)) {
|
||||
c = uart->dev->fifo.rw_byte;
|
||||
#else
|
||||
uint32_t fifo_reg = UART_FIFO_AHB_REG(i);
|
||||
while(uart->dev->status.rxfifo_cnt) {
|
||||
c = ESP_REG(fifo_reg);
|
||||
#endif
|
||||
if(uart->queue != NULL) {
|
||||
xQueueSendFromISR(uart->queue, &c, &xHigherPriorityTaskWoken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (xHigherPriorityTaskWoken) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
static void uartEnableInterrupt(uart_t* uart, uint8_t rxfifo_full_thrhd)
|
||||
{
|
||||
UART_MUTEX_LOCK();
|
||||
uart->dev->conf1.rxfifo_full_thrhd = rxfifo_full_thrhd;
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
uart->dev->conf1.rx_tout_thrhd = 2;
|
||||
#else
|
||||
uart->dev->mem_conf.rx_tout_thrhd = 2;
|
||||
#endif
|
||||
uart->dev->conf1.rx_tout_en = 1;
|
||||
uart->dev->int_ena.rxfifo_full = 1;
|
||||
uart->dev->int_ena.frm_err = 1;
|
||||
uart->dev->int_ena.rxfifo_tout = 1;
|
||||
uart->dev->int_clr.val = 0xffffffff;
|
||||
|
||||
esp_intr_alloc(UART_INTR_SOURCE(uart->num), (int)ARDUINO_ISR_FLAG, _uart_isr, NULL, &uart->intr_handle);
|
||||
UART_MUTEX_UNLOCK();
|
||||
}
|
||||
|
||||
static void uartDisableInterrupt(uart_t* uart)
|
||||
{
|
||||
UART_MUTEX_LOCK();
|
||||
uart->dev->conf1.val = 0;
|
||||
uart->dev->int_ena.val = 0;
|
||||
uart->dev->int_clr.val = 0xffffffff;
|
||||
|
||||
esp_intr_free(uart->intr_handle);
|
||||
uart->intr_handle = NULL;
|
||||
|
||||
UART_MUTEX_UNLOCK();
|
||||
}
|
||||
|
||||
static void uartDetachRx(uart_t* uart, uint8_t rxPin)
|
||||
bool uartIsDriverInstalled(uart_t* uart)
|
||||
{
|
||||
if(uart == NULL) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
pinMatrixInDetach(UART_RXD_IDX(uart->num), false, false);
|
||||
uartDisableInterrupt(uart);
|
||||
|
||||
if (uart_is_driver_installed(uart->num)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void uartDetachTx(uart_t* uart, uint8_t txPin)
|
||||
void uartSetPins(uart_t* uart, uint8_t rxPin, uint8_t txPin)
|
||||
{
|
||||
if(uart == NULL) {
|
||||
if(uart == NULL || rxPin >= SOC_GPIO_PIN_COUNT || txPin >= SOC_GPIO_PIN_COUNT) {
|
||||
return;
|
||||
}
|
||||
pinMatrixOutDetach(txPin, false, false);
|
||||
UART_MUTEX_LOCK();
|
||||
ESP_ERROR_CHECK(uart_set_pin(uart->num, txPin, rxPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
|
||||
UART_MUTEX_UNLOCK();
|
||||
|
||||
}
|
||||
|
||||
static void uartAttachRx(uart_t* uart, uint8_t rxPin, bool inverted, uint8_t rxfifo_full_thrhd)
|
||||
{
|
||||
if(uart == NULL || rxPin >= GPIO_PIN_COUNT) {
|
||||
return;
|
||||
}
|
||||
pinMode(rxPin, INPUT);
|
||||
uartEnableInterrupt(uart, rxfifo_full_thrhd);
|
||||
pinMatrixInAttach(rxPin, UART_RXD_IDX(uart->num), inverted);
|
||||
}
|
||||
|
||||
static void uartAttachTx(uart_t* uart, uint8_t txPin, bool inverted)
|
||||
{
|
||||
if(uart == NULL || txPin >= GPIO_PIN_COUNT) {
|
||||
return;
|
||||
}
|
||||
pinMode(txPin, OUTPUT);
|
||||
pinMatrixOutAttach(txPin, UART_TXD_IDX(uart->num), inverted, false);
|
||||
}
|
||||
|
||||
uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rxPin, int8_t txPin, uint16_t queueLen, bool inverted, uint8_t rxfifo_full_thrhd)
|
||||
{
|
||||
if(uart_nr >= UART_PORTS_NUM) {
|
||||
if(uart_nr >= SOC_UART_NUM) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -222,6 +105,10 @@ uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rx
|
||||
|
||||
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();
|
||||
@ -231,189 +118,143 @@ uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rx
|
||||
}
|
||||
#endif
|
||||
|
||||
if(queueLen && uart->queue == NULL) {
|
||||
uart->queue = xQueueCreate(queueLen, sizeof(uint8_t)); //initialize the queue
|
||||
if(uart->queue == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#if CONFIG_IDF_TARGET_ESP32C3
|
||||
|
||||
#else
|
||||
if(uart_nr == 1){
|
||||
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_UART1_CLK_EN);
|
||||
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_UART1_RST);
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
} else if(uart_nr == 2){
|
||||
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_UART2_CLK_EN);
|
||||
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_UART2_RST);
|
||||
#endif
|
||||
} else {
|
||||
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_UART_CLK_EN);
|
||||
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_UART_RST);
|
||||
}
|
||||
#endif
|
||||
uartFlush(uart);
|
||||
uartSetBaudRate(uart, baudrate);
|
||||
UART_MUTEX_LOCK();
|
||||
uart->dev->conf0.val = config;
|
||||
#define TWO_STOP_BITS_CONF 0x3
|
||||
#define ONE_STOP_BITS_CONF 0x1
|
||||
|
||||
if ( uart->dev->conf0.stop_bit_num == TWO_STOP_BITS_CONF) {
|
||||
uart->dev->conf0.stop_bit_num = ONE_STOP_BITS_CONF;
|
||||
uart->dev->rs485_conf.dl1_en = 1;
|
||||
uart_config_t uart_config;
|
||||
uart_config.baud_rate = 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, 2*queueLen, 0, 0, NULL, 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));
|
||||
}
|
||||
|
||||
// tx_idle_num : idle interval after tx FIFO is empty(unit: the time it takes to send one bit under current baudrate)
|
||||
// Setting it to 0 prevents line idle time/delays when sending messages with small intervals
|
||||
uart->dev->idle_conf.tx_idle_num = 0; //
|
||||
|
||||
UART_MUTEX_UNLOCK();
|
||||
|
||||
if(rxPin != -1) {
|
||||
uartAttachRx(uart, rxPin, inverted, rxfifo_full_thrhd);
|
||||
}
|
||||
|
||||
if(txPin != -1) {
|
||||
uartAttachTx(uart, txPin, inverted);
|
||||
}
|
||||
addApbChangeCallback(uart, uart_on_apb_change);
|
||||
uartFlush(uart);
|
||||
return uart;
|
||||
}
|
||||
|
||||
void uartEnd(uart_t* uart, uint8_t txPin, uint8_t rxPin)
|
||||
void uartEnd(uart_t* uart)
|
||||
{
|
||||
if(uart == NULL) {
|
||||
return;
|
||||
}
|
||||
removeApbChangeCallback(uart, uart_on_apb_change);
|
||||
|
||||
|
||||
UART_MUTEX_LOCK();
|
||||
if(uart->queue != NULL) {
|
||||
vQueueDelete(uart->queue);
|
||||
uart->queue = NULL;
|
||||
}
|
||||
|
||||
uart->dev->conf0.val = 0;
|
||||
|
||||
uart_driver_delete(uart->num);
|
||||
UART_MUTEX_UNLOCK();
|
||||
|
||||
uartDetachRx(uart, rxPin);
|
||||
uartDetachTx(uart, txPin);
|
||||
}
|
||||
|
||||
size_t uartResizeRxBuffer(uart_t * uart, size_t new_size) {
|
||||
if(uart == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
UART_MUTEX_LOCK();
|
||||
if(uart->queue != NULL) {
|
||||
vQueueDelete(uart->queue);
|
||||
uart->queue = xQueueCreate(new_size, sizeof(uint8_t));
|
||||
if(uart->queue == NULL) {
|
||||
UART_MUTEX_UNLOCK();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
UART_MUTEX_UNLOCK();
|
||||
|
||||
return new_size;
|
||||
}
|
||||
|
||||
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)
|
||||
uart->dev->conf0.rxd_inv = 1;
|
||||
ESP_ERROR_CHECK(uart_set_line_inverse(uart->num, UART_SIGNAL_RXD_INV));
|
||||
else
|
||||
uart->dev->conf0.rxd_inv = 0;
|
||||
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 || uart->queue == NULL) {
|
||||
|
||||
if(uart == NULL) {
|
||||
return 0;
|
||||
}
|
||||
#ifdef UART_READ_RX_FIFO
|
||||
return (uxQueueMessagesWaiting(uart->queue) + uart->dev->status.rxfifo_cnt) ;
|
||||
#else
|
||||
return uxQueueMessagesWaiting(uart->queue);
|
||||
#endif
|
||||
|
||||
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;
|
||||
}
|
||||
return 0x7f - uart->dev->status.txfifo_cnt;
|
||||
UART_MUTEX_LOCK();
|
||||
uint32_t available = uart_ll_get_txfifo_len(UART_LL_GET_HW(uart->num));
|
||||
UART_MUTEX_UNLOCK();
|
||||
return available;
|
||||
}
|
||||
|
||||
#ifdef UART_READ_RX_FIFO
|
||||
void uartRxFifoToQueue(uart_t* uart)
|
||||
{
|
||||
uint8_t c;
|
||||
UART_MUTEX_LOCK();
|
||||
//disable interrupts
|
||||
uart->dev->int_ena.val = 0;
|
||||
uart->dev->int_clr.val = 0xffffffff;
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
while (uart->dev->status.rxfifo_cnt || (uart->dev->mem_rx_status.wr_addr != uart->dev->mem_rx_status.rd_addr)) {
|
||||
c = uart->dev->fifo.rw_byte;
|
||||
#else
|
||||
uint32_t fifo_reg = UART_FIFO_AHB_REG(uart->num);
|
||||
while (uart->dev->status.rxfifo_cnt) {
|
||||
c = ESP_REG(fifo_reg);
|
||||
#endif
|
||||
xQueueSend(uart->queue, &c, 0);
|
||||
}
|
||||
//enable interrupts
|
||||
uart->dev->int_ena.rxfifo_full = 1;
|
||||
uart->dev->int_ena.frm_err = 1;
|
||||
uart->dev->int_ena.rxfifo_tout = 1;
|
||||
uart->dev->int_clr.val = 0xffffffff;
|
||||
UART_MUTEX_UNLOCK();
|
||||
}
|
||||
#endif
|
||||
|
||||
uint8_t uartRead(uart_t* uart)
|
||||
{
|
||||
if(uart == NULL || uart->queue == NULL) {
|
||||
if(uart == NULL) {
|
||||
return 0;
|
||||
}
|
||||
uint8_t c;
|
||||
#ifdef UART_READ_RX_FIFO
|
||||
if ((uxQueueMessagesWaiting(uart->queue) == 0) && (uart->dev->status.rxfifo_cnt > 0))
|
||||
{
|
||||
uartRxFifoToQueue(uart);
|
||||
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;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if(xQueueReceive(uart->queue, &c, 0)) {
|
||||
return c;
|
||||
}
|
||||
return 0;
|
||||
UART_MUTEX_UNLOCK();
|
||||
return c;
|
||||
}
|
||||
|
||||
uint8_t uartPeek(uart_t* uart)
|
||||
{
|
||||
if(uart == NULL || uart->queue == NULL) {
|
||||
if(uart == NULL) {
|
||||
return 0;
|
||||
}
|
||||
uint8_t c;
|
||||
#ifdef UART_READ_RX_FIFO
|
||||
if ((uxQueueMessagesWaiting(uart->queue) == 0) && (uart->dev->status.rxfifo_cnt > 0))
|
||||
{
|
||||
uartRxFifoToQueue(uart);
|
||||
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;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if(xQueuePeek(uart->queue, &c, 0)) {
|
||||
return c;
|
||||
}
|
||||
return 0;
|
||||
UART_MUTEX_UNLOCK();
|
||||
return c;
|
||||
}
|
||||
|
||||
void uartWrite(uart_t* uart, uint8_t c)
|
||||
@ -422,39 +263,24 @@ void uartWrite(uart_t* uart, uint8_t c)
|
||||
return;
|
||||
}
|
||||
UART_MUTEX_LOCK();
|
||||
while(uart->dev->status.txfifo_cnt >= 0x7E);
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
uart->dev->fifo.rw_byte = c;
|
||||
#else
|
||||
ESP_REG(UART_FIFO_AHB_REG(uart->num)) = c;
|
||||
#endif
|
||||
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) {
|
||||
if(uart == NULL || data == NULL || !len) {
|
||||
return;
|
||||
}
|
||||
|
||||
UART_MUTEX_LOCK();
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32
|
||||
uint32_t fifo_reg = UART_FIFO_AHB_REG(uart->num);
|
||||
#endif
|
||||
while(len) {
|
||||
while(uart->dev->status.txfifo_cnt >= 0x7E);
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
uart->dev->fifo.rw_byte = *data++;
|
||||
#else
|
||||
ESP_REG(fifo_reg) = *data++;
|
||||
#endif
|
||||
len--;
|
||||
}
|
||||
uart_write_bytes(uart->num, data, len);
|
||||
UART_MUTEX_UNLOCK();
|
||||
}
|
||||
|
||||
void uartFlush(uart_t* uart)
|
||||
{
|
||||
uartFlushTxOnly(uart,true);
|
||||
uartFlushTxOnly(uart, true);
|
||||
}
|
||||
|
||||
void uartFlushTxOnly(uart_t* uart, bool txOnly)
|
||||
@ -462,28 +288,13 @@ void uartFlushTxOnly(uart_t* uart, bool txOnly)
|
||||
if(uart == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
UART_MUTEX_LOCK();
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
while(uart->dev->status.txfifo_cnt || uart->dev->status.st_utx_out);
|
||||
|
||||
if( !txOnly ){
|
||||
//Due to hardware issue, we can not use fifo_rst to reset uart fifo.
|
||||
//See description about UART_TXFIFO_RST and UART_RXFIFO_RST in <<esp32_technical_reference_manual>> v2.6 or later.
|
||||
ESP_ERROR_CHECK(uart_wait_tx_done(uart->num, portMAX_DELAY));
|
||||
|
||||
// we read the data out and make `fifo_len == 0 && rd_addr == wr_addr`.
|
||||
while(uart->dev->status.rxfifo_cnt != 0 || (uart->dev->mem_rx_status.wr_addr != uart->dev->mem_rx_status.rd_addr)) {
|
||||
READ_PERI_REG(UART_FIFO_REG(uart->num));
|
||||
}
|
||||
|
||||
xQueueReset(uart->queue);
|
||||
if ( !txOnly ) {
|
||||
ESP_ERROR_CHECK(uart_flush_input(uart->num));
|
||||
}
|
||||
#else
|
||||
while(uart->dev->status.txfifo_cnt);
|
||||
uart->dev->conf0.txfifo_rst = 1;
|
||||
uart->dev->conf0.txfifo_rst = 0;
|
||||
#endif
|
||||
|
||||
UART_MUTEX_UNLOCK();
|
||||
}
|
||||
|
||||
@ -493,100 +304,41 @@ void uartSetBaudRate(uart_t* uart, uint32_t baud_rate)
|
||||
return;
|
||||
}
|
||||
UART_MUTEX_LOCK();
|
||||
uart_ll_set_baudrate(uart->dev, baud_rate);
|
||||
uart_ll_set_baudrate(UART_LL_GET_HW(uart->num), baud_rate);
|
||||
UART_MUTEX_UNLOCK();
|
||||
}
|
||||
|
||||
static void uart_on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb)
|
||||
{
|
||||
uart_t* uart = (uart_t*)arg;
|
||||
if(ev_type == APB_BEFORE_CHANGE){
|
||||
UART_MUTEX_LOCK();
|
||||
//disabple interrupt
|
||||
uart->dev->int_ena.val = 0;
|
||||
uart->dev->int_clr.val = 0xffffffff;
|
||||
// read RX fifo
|
||||
uint8_t c;
|
||||
// BaseType_t xHigherPriorityTaskWoken;
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
while(uart->dev->status.rxfifo_cnt != 0 || (uart->dev->mem_rx_status.wr_addr != uart->dev->mem_rx_status.rd_addr)) {
|
||||
c = uart->dev->fifo.rw_byte;
|
||||
#else
|
||||
uint32_t fifo_reg = UART_FIFO_AHB_REG(uart->num);
|
||||
while(uart->dev->status.rxfifo_cnt != 0) {
|
||||
c = ESP_REG(fifo_reg);
|
||||
#endif
|
||||
if(uart->queue != NULL ) {
|
||||
xQueueSend(uart->queue, &c, 1); //&xHigherPriorityTaskWoken);
|
||||
}
|
||||
}
|
||||
UART_MUTEX_UNLOCK();
|
||||
|
||||
// wait TX empty
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
while(uart->dev->status.txfifo_cnt || uart->dev->status.st_utx_out);
|
||||
#else
|
||||
while(uart->dev->status.txfifo_cnt);
|
||||
#endif
|
||||
} else {
|
||||
//todo:
|
||||
// set baudrate
|
||||
UART_MUTEX_LOCK();
|
||||
uint32_t clk_div = (uart->dev->clk_div.div_int << 4) | (uart->dev->clk_div.div_frag & 0x0F);
|
||||
uint32_t baud_rate = ((old_apb<<4)/clk_div);
|
||||
clk_div = ((new_apb<<4)/baud_rate);
|
||||
uart->dev->clk_div.div_int = clk_div>>4 ;
|
||||
uart->dev->clk_div.div_frag = clk_div & 0xf;
|
||||
//enable interrupts
|
||||
uart->dev->int_ena.rxfifo_full = 1;
|
||||
uart->dev->int_ena.frm_err = 1;
|
||||
uart->dev->int_ena.rxfifo_tout = 1;
|
||||
uart->dev->int_clr.val = 0xffffffff;
|
||||
UART_MUTEX_UNLOCK();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t uartGetBaudRate(uart_t* uart)
|
||||
{
|
||||
if(uart == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t clk_div = (uart->dev->clk_div.div_int << 4) | (uart->dev->clk_div.div_frag & 0x0F);
|
||||
if(!clk_div) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ((getApbFrequency()<<4)/clk_div);
|
||||
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)
|
||||
{
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
while(((ESP_REG(0x01C+DR_REG_UART_BASE) >> UART_TXFIFO_CNT_S) & 0x7F) >= 0x7E);
|
||||
ESP_REG(DR_REG_UART_BASE) = c;
|
||||
#else
|
||||
while(UART0.status.txfifo_cnt == 0x7F);
|
||||
WRITE_PERI_REG(UART_FIFO_AHB_REG(0), c);
|
||||
#endif
|
||||
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)
|
||||
{
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
while(((ESP_REG(0x01C+DR_REG_UART1_BASE) >> UART_TXFIFO_CNT_S) & 0x7F) >= 0x7E);
|
||||
ESP_REG(DR_REG_UART1_BASE) = c;
|
||||
#else
|
||||
while(UART1.status.txfifo_cnt == 0x7F);
|
||||
WRITE_PERI_REG(UART_FIFO_AHB_REG(1), c);
|
||||
#endif
|
||||
while (uart_ll_get_txfifo_len(&UART1) == 0);
|
||||
uart_ll_write_txfifo(&UART1, (const uint8_t *) &c, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#if SOC_UART_NUM > 2
|
||||
static void ARDUINO_ISR_ATTR uart2_write_char(char c)
|
||||
{
|
||||
while(((ESP_REG(0x01C+DR_REG_UART2_BASE) >> UART_TXFIFO_CNT_S) & 0x7F) >= 0x7E);
|
||||
ESP_REG(DR_REG_UART2_BASE) = c;
|
||||
while (uart_ll_get_txfifo_len(&UART2) == 0);
|
||||
uart_ll_write_txfifo(&UART2, (const uint8_t *) &c, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -596,10 +348,12 @@ void uart_install_putc()
|
||||
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;
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#endif
|
||||
#if SOC_UART_NUM > 2
|
||||
case 2:
|
||||
ets_install_putc1((void (*)(char)) &uart2_write_char);
|
||||
break;
|
||||
@ -612,15 +366,11 @@ void uart_install_putc()
|
||||
|
||||
void uartSetDebug(uart_t* uart)
|
||||
{
|
||||
if(uart == NULL || uart->num >= UART_PORTS_NUM) {
|
||||
if(uart == NULL || uart->num >= SOC_UART_NUM) {
|
||||
s_uart_debug_nr = -1;
|
||||
//ets_install_putc1(NULL);
|
||||
//return;
|
||||
} else
|
||||
if(s_uart_debug_nr == uart->num) {
|
||||
return;
|
||||
} else
|
||||
s_uart_debug_nr = uart->num;
|
||||
} else {
|
||||
s_uart_debug_nr = uart->num;
|
||||
}
|
||||
uart_install_putc();
|
||||
}
|
||||
|
||||
@ -646,17 +396,19 @@ int log_printf(const char *format, ...)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
vsnprintf(temp, len+1, format, arg);
|
||||
#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);
|
||||
ets_printf("%s", temp);
|
||||
xSemaphoreGive(_uart_bus_array[s_uart_debug_nr].lock);
|
||||
} else {
|
||||
ets_printf("%s", temp);
|
||||
}
|
||||
#else
|
||||
#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)){
|
||||
@ -665,48 +417,126 @@ int log_printf(const char *format, ...)
|
||||
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)
|
||||
{
|
||||
while(uart->dev->rxd_cnt.edge_cnt < 30) { // UART_PULSE_NUM(uart_num)
|
||||
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();
|
||||
unsigned long ret = ((uart->dev->lowpulse.min_cnt + uart->dev->highpulse.min_cnt) >> 1) + 12;
|
||||
//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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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) return;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
uart->dev->auto_baud.glitch_filt = 0x08;
|
||||
uart->dev->auto_baud.en = 0;
|
||||
uart->dev->auto_baud.en = 1;
|
||||
if(uart == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
uart_dev_t *hw = UART_LL_GET_HW(uart->num);
|
||||
|
||||
#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;
|
||||
|
||||
#else
|
||||
hw->auto_baud.glitch_filt = 0x08;
|
||||
hw->auto_baud.en = 0;
|
||||
hw->auto_baud.en = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
unsigned long
|
||||
uartDetectBaudrate(uart_t *uart)
|
||||
{
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
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;
|
||||
uart_dev_t *hw = UART_LL_GET_HW(uart->num);
|
||||
|
||||
if(!uartStateDetectingBaudrate) {
|
||||
uart->dev->auto_baud.glitch_filt = 0x08;
|
||||
uart->dev->auto_baud.en = 0;
|
||||
uart->dev->auto_baud.en = 1;
|
||||
uartStartDetectBaudrate(uart);
|
||||
uartStateDetectingBaudrate = true;
|
||||
}
|
||||
|
||||
@ -714,11 +544,21 @@ uartDetectBaudrate(uart_t *uart)
|
||||
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));
|
||||
|
||||
uart->dev->auto_baud.en = 0;
|
||||
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32C3
|
||||
//hw->conf0.autobaud_en = 0;
|
||||
#else
|
||||
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};
|
||||
|
||||
@ -736,17 +576,7 @@ uartDetectBaudrate(uart_t *uart)
|
||||
|
||||
return default_rates[i];
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the status of the RX state machine, if the value is non-zero the state machine is active.
|
||||
*/
|
||||
bool uartRxActive(uart_t* uart) {
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
return uart->dev->status.st_urx_out != 0;
|
||||
#else
|
||||
log_e("ESP32-C3 baud rate detection is not supported.");
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ 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 queueLen, bool inverted, uint8_t rxfifo_full_thrhd);
|
||||
void uartEnd(uart_t* uart, uint8_t rxPin, uint8_t txPin);
|
||||
void uartEnd(uart_t* uart);
|
||||
|
||||
uint32_t uartAvailable(uart_t* uart);
|
||||
uint32_t uartAvailableForWrite(uart_t* uart);
|
||||
@ -68,17 +68,17 @@ void uartFlushTxOnly(uart_t* uart, bool txOnly );
|
||||
void uartSetBaudRate(uart_t* uart, uint32_t baud_rate);
|
||||
uint32_t uartGetBaudRate(uart_t* uart);
|
||||
|
||||
size_t uartResizeRxBuffer(uart_t* uart, size_t new_size);
|
||||
|
||||
void uartSetRxInvert(uart_t* uart, bool invert);
|
||||
|
||||
void uartSetDebug(uart_t* uart);
|
||||
int uartGetDebug();
|
||||
|
||||
bool uartIsDriverInstalled(uart_t* uart);
|
||||
void uartSetPins(uart_t* uart, uint8_t rxPin, uint8_t txPin);
|
||||
|
||||
void uartStartDetectBaudrate(uart_t *uart);
|
||||
unsigned long uartDetectBaudrate(uart_t *uart);
|
||||
|
||||
bool uartRxActive(uart_t* uart);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -31,6 +31,11 @@
|
||||
#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" {
|
||||
@ -85,6 +90,8 @@ void yield(void);
|
||||
#include "esp32-hal-psram.h"
|
||||
#include "esp32-hal-cpu.h"
|
||||
|
||||
void analogWrite(uint8_t pin, int value);
|
||||
|
||||
//returns chip temperature in Celsius
|
||||
float temperatureRead();
|
||||
|
||||
|
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
|
@ -12,7 +12,7 @@ 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;
|
||||
if (value_in < 0 || value_in >= decoding_size) return -1;
|
||||
return decoding[(int)value_in];
|
||||
}
|
||||
|
||||
|
@ -2,8 +2,11 @@
|
||||
#include "freertos/task.h"
|
||||
#include "esp_task_wdt.h"
|
||||
#include "Arduino.h"
|
||||
#if ARDUINO_SERIAL_PORT //Serial used for USB CDC
|
||||
#if (ARDUINO_USB_CDC_ON_BOOT|ARDUINO_USB_MSC_ON_BOOT|ARDUINO_USB_DFU_ON_BOOT)
|
||||
#include "USB.h"
|
||||
#if ARDUINO_USB_MSC_ON_BOOT
|
||||
#include "FirmwareMSC.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef ARDUINO_LOOP_STACK_SIZE
|
||||
@ -47,9 +50,17 @@ void loopTask(void *pvParameters)
|
||||
|
||||
extern "C" void app_main()
|
||||
{
|
||||
#if ARDUINO_SERIAL_PORT //Serial used for USB CDC
|
||||
USB.begin();
|
||||
#if ARDUINO_USB_CDC_ON_BOOT
|
||||
Serial.begin();
|
||||
#endif
|
||||
#if ARDUINO_USB_MSC_ON_BOOT
|
||||
MSC_Update.begin();
|
||||
#endif
|
||||
#if ARDUINO_USB_DFU_ON_BOOT
|
||||
USB.enableDFU();
|
||||
#endif
|
||||
#if ARDUINO_USB_ON_BOOT
|
||||
USB.begin();
|
||||
#endif
|
||||
loopTaskWDTEnabled = false;
|
||||
initArduino();
|
||||
|
@ -30,8 +30,7 @@
|
||||
#include "stdlib_noniso.h"
|
||||
#include "esp_system.h"
|
||||
|
||||
#if !CONFIG_DSP_ANSI && !CONFIG_DSP_OPTIMIZED
|
||||
void reverse(char* begin, char* end) {
|
||||
static void reverse(char* begin, char* end) {
|
||||
char *is = begin;
|
||||
char *ie = end - 1;
|
||||
while(is < ie) {
|
||||
@ -42,9 +41,6 @@ void reverse(char* begin, char* end) {
|
||||
--ie;
|
||||
}
|
||||
}
|
||||
#else
|
||||
void reverse(char* begin, char* end);
|
||||
#endif
|
||||
|
||||
char* ltoa(long value, char* result, int base) {
|
||||
if(base < 2 || base > 16) {
|
||||
|
@ -17,13 +17,11 @@
|
||||
//#include <limits.h>
|
||||
#include "wiring_private.h"
|
||||
#include "pins_arduino.h"
|
||||
|
||||
|
||||
extern uint32_t xthal_get_ccount();
|
||||
#include <hal/cpu_hal.h>
|
||||
|
||||
#define WAIT_FOR_PIN_STATE(state) \
|
||||
while (digitalRead(pin) != (state)) { \
|
||||
if (xthal_get_ccount() - start_cycle_count > timeout_cycles) { \
|
||||
if (cpu_hal_get_cycle_count() - start_cycle_count > timeout_cycles) { \
|
||||
return 0; \
|
||||
} \
|
||||
}
|
||||
@ -36,12 +34,12 @@ unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout)
|
||||
timeout = max_timeout_us;
|
||||
}
|
||||
const uint32_t timeout_cycles = microsecondsToClockCycles(timeout);
|
||||
const uint32_t start_cycle_count = xthal_get_ccount();
|
||||
const uint32_t start_cycle_count = cpu_hal_get_cycle_count();
|
||||
WAIT_FOR_PIN_STATE(!state);
|
||||
WAIT_FOR_PIN_STATE(state);
|
||||
const uint32_t pulse_start_cycle_count = xthal_get_ccount();
|
||||
const uint32_t pulse_start_cycle_count = cpu_hal_get_cycle_count();
|
||||
WAIT_FOR_PIN_STATE(!state);
|
||||
return clockCyclesToMicroseconds(xthal_get_ccount() - pulse_start_cycle_count);
|
||||
return clockCyclesToMicroseconds(cpu_hal_get_cycle_count() - pulse_start_cycle_count);
|
||||
}
|
||||
|
||||
unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout)
|
||||
|
BIN
docs/source/_static/arduino_i2c_master.png
Normal file
BIN
docs/source/_static/arduino_i2c_master.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 78 KiB |
BIN
docs/source/_static/arduino_i2c_slave.png
Normal file
BIN
docs/source/_static/arduino_i2c_slave.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 114 KiB |
384
docs/source/api/i2c.rst
Normal file
384
docs/source/api/i2c.rst
Normal file
@ -0,0 +1,384 @@
|
||||
###
|
||||
I2C
|
||||
###
|
||||
|
||||
About
|
||||
-----
|
||||
|
||||
I2C (Inter-Integrated Circuit) / TWI (Two-wire Interface) is a widely used serial communication to connect devices in a short distance. This is one of the most common peripherals used to connect sensors, EEPROMs, RTC, ADC, DAC, displays, OLED, and many other devices and microcontrollers.
|
||||
|
||||
This serial communication is considered as a low-speed bus, and multiple devices can be connected on the same two-wires bus, each with a unique 7-bits address (up to 128 devices). These two wires are called SDA (serial data line) and SCL (serial clock line).
|
||||
|
||||
.. note:: The SDA and SCL lines require pull-up resistors. See the device datasheet for more details about the resistors' values and the operating voltage.
|
||||
|
||||
I2C Modes
|
||||
*********
|
||||
|
||||
The I2C can be used in two different modes:
|
||||
|
||||
* **I2C Master Mode**
|
||||
* In this mode, the ESP32 generates the clock signal and initiates the communication with the slave device.
|
||||
|
||||
.. figure:: ../_static/arduino_i2c_master.png
|
||||
:align: center
|
||||
:width: 720
|
||||
:figclass: align-center
|
||||
|
||||
* **I2C Slave Mode**
|
||||
* The slave mode, the clock is generated by the master device and responds to the master if the destination address is the same as the destination.
|
||||
|
||||
.. figure:: ../_static/arduino_i2c_slave.png
|
||||
:align: center
|
||||
:width: 520
|
||||
:figclass: align-center
|
||||
|
||||
Arduino-ESP32 I2C API
|
||||
---------------------
|
||||
|
||||
The ESP32 I2C library is based on the `Arduino Wire Library`_ and implements a few more APIs, described in this documentation.
|
||||
|
||||
I2C Common API
|
||||
**************
|
||||
|
||||
Here are the common functions used for master and slave modes.
|
||||
|
||||
begin
|
||||
^^^^^
|
||||
|
||||
This function is used to start the peripheral using the default configuration.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
bool begin();
|
||||
|
||||
This function will return ``true`` if the peripheral was initialized correctly.
|
||||
|
||||
setPins
|
||||
^^^^^^^
|
||||
|
||||
This function is used to define the ``SDA`` and ``SCL`` pins.
|
||||
|
||||
.. note:: Call this function before ``begin`` to change the pins from the default ones.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
bool setPins(int sdaPin, int sclPin);
|
||||
|
||||
* ``sdaPin`` sets the GPIO to be used as the I2C peripheral data line.
|
||||
|
||||
* ``sclPin`` sets the GPIO to be used as the I2C peripheral clock line.
|
||||
|
||||
The default pins may vary from board to board. On the *Generic ESP32* the default I2C pins are:
|
||||
|
||||
* ``sdaPin`` **GPIO21**
|
||||
|
||||
* ``sclPin`` **GPIO22**
|
||||
|
||||
This function will return ``true`` if the peripheral was configured correctly.
|
||||
|
||||
setClock
|
||||
^^^^^^^^
|
||||
|
||||
Use this function to set the bus clock. The default value will be used if this function is not used.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
bool setClock(uint32_t frequency);
|
||||
|
||||
* ``frequency`` sets the bus frequency clock.
|
||||
|
||||
This function will return ``true`` if the clock was configured correctly.
|
||||
|
||||
getClock
|
||||
^^^^^^^^
|
||||
|
||||
Use this function to get the bus clock.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
uint32_t getClock();
|
||||
|
||||
This function will return the current frequency configuration.
|
||||
|
||||
setTimeOut
|
||||
^^^^^^^^^^
|
||||
|
||||
Set the bus timeout given in milliseconds. The default value is 50ms.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
void setTimeOut(uint16_t timeOutMillis);
|
||||
|
||||
* ``timeOutMillis`` sets the timeout in ms.
|
||||
|
||||
getTimeOut
|
||||
^^^^^^^^^^
|
||||
|
||||
Get the bus timeout in milliseconds.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
uint16_t getTimeOut();
|
||||
|
||||
This function will return the current timeout configuration.
|
||||
|
||||
.. _i2c write:
|
||||
|
||||
write
|
||||
^^^^^
|
||||
|
||||
This function writes data to the buffer.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
size_t write(uint8_t);
|
||||
|
||||
or
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
size_t write(const uint8_t *, size_t);
|
||||
|
||||
The return will be the size of the data added to the buffer.
|
||||
|
||||
.. _i2c end:
|
||||
|
||||
end
|
||||
^^^
|
||||
|
||||
This function will finish the communication and release all the allocated resources. After calling ``end`` you need to use ``begin`` again in order to initialize the I2C driver again.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
bool end();
|
||||
|
||||
|
||||
I2C Master Mode
|
||||
***************
|
||||
|
||||
This mode is used to initiate communication to the slave.
|
||||
|
||||
Basic Usage
|
||||
^^^^^^^^^^^
|
||||
|
||||
To start using I2C master mode on the Arduino, the first step is to include the ``Wire.h`` header to the sketch.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
#include "Wire.h"
|
||||
|
||||
Now, we can start the peripheral configuration by calling ``begin`` function.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
Wire.begin();
|
||||
|
||||
By using ``begin`` without any arguments, all the settings will be done by using the default values. To set the values by your own, see the function description. This function is described here: `i2c begin`_
|
||||
|
||||
After calling ``begin``, we can start the transmission by calling ``beginTransmission`` and passing the I2C slave address:
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
Wire.beginTransmission(I2C_DEV_ADDR);
|
||||
|
||||
To write some bytes to the slave, use the ``write`` function.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
Wire.write(x);
|
||||
|
||||
You can pass different data types using ``write`` function. This function is described here: `i2c write`_
|
||||
|
||||
.. note:: The ``write`` function does not write directly to the slave device but adds to the I2C buffer. To do so, you need to use the ``endTransmission`` function to send the buffered bytes to the slave device.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
Wire.endTransmission(true);
|
||||
|
||||
After calling ``endTransmission``, the data stored in the I2C buffer will be transmitted to the slave device.
|
||||
|
||||
Now you can request a reading from the slave device. The ``requestFrom`` will ask for a readout to the selected device by giving the address and the size.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
Wire.requestFrom(I2C_DEV_ADDR, SIZE);
|
||||
|
||||
and the ``readBytes`` will read it.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
Wire.readBytes(temp, error);
|
||||
|
||||
.. _i2c begin:
|
||||
|
||||
I2C Master APIs
|
||||
***************
|
||||
|
||||
Here are the I2C master APIs. These function are intended to be used only for master mode.
|
||||
|
||||
begin
|
||||
^^^^^
|
||||
|
||||
In master mode, the ``begin`` function can be used by passing the **pins** and **bus frequency**. Use this function only for the master mode.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
bool begin(int sdaPin, int sclPin, uint32_t frequency)
|
||||
|
||||
Alternatively, you can use the ``begin`` function without any argument to use all default values.
|
||||
|
||||
This function will return ``true`` if the peripheral was initialized correctly.
|
||||
|
||||
beginTransmission
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
This function is used to star a communication process with the slave device. Call this function by passing the slave ``address`` before writing the message to the buffer.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
void beginTransmission(uint16_t address)
|
||||
|
||||
endTransmission
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
After writing to the buffer using `i2c write`_, use the function ``endTransmission`` to send the message to the slave device address defined on the ``beginTransmission`` function.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
uint8_t endTransmission(bool sendStop);
|
||||
|
||||
* ``sendStop`` enables **(true)** or disables **(false)** the stop signal *(only used in master mode)*.
|
||||
|
||||
Calling the this function without ``sendStop`` is equivalent to ``sendStop = true``.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
uint8_t endTransmission(void);
|
||||
|
||||
This function will return the error code.
|
||||
|
||||
requestFrom
|
||||
^^^^^^^^^^^
|
||||
|
||||
To read from the slave device, use the ``requestFrom`` function.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
uint8_t requestFrom(uint16_t address, uint8_t size, bool sendStop)
|
||||
|
||||
* ``address`` set the device address.
|
||||
|
||||
* ``size`` define the size to be requested.
|
||||
|
||||
* ``sendStop`` enables (true) or disables (false) the stop signal.
|
||||
|
||||
This function will return the number of bytes read from the device.
|
||||
|
||||
Example Application - WireMaster.ino
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Here is an example of how to use the I2C in Master Mode.
|
||||
|
||||
.. literalinclude:: ../../../libraries/Wire/examples/WireMaster/WireMaster.ino
|
||||
:language: arduino
|
||||
|
||||
|
||||
I2C Slave Mode
|
||||
**************
|
||||
|
||||
This mode is used to accept communication from the master.
|
||||
|
||||
Basic Usage
|
||||
^^^^^^^^^^^
|
||||
|
||||
To start using I2C as slave mode on the Arduino, the first step is to include the ``Wire.h`` header to the scketch.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
#include "Wire.h"
|
||||
|
||||
Before calling ``begin`` we must create two callback functions to handle the communication with the master device.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
Wire.onReceive(onReceive);
|
||||
|
||||
and
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
Wire.onRequest(onRequest);
|
||||
|
||||
The ``onReceive`` will handle the request from the master device uppon a slave read request and the ``onRequest`` will handle the answer to the master.
|
||||
|
||||
Now, we can start the peripheral configuration by calling ``begin`` function with the device address.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
Wire.begin((uint8_t)I2C_DEV_ADDR);
|
||||
|
||||
By using ``begin`` without any arguments, all the settings will be done by using the default values. To set the values by your own, see the function description. This function is described here: `i2c begin`_
|
||||
|
||||
|
||||
**For ESP32 only!**
|
||||
|
||||
Use the function ``slaveWrite`` in order to pre-write to the slave response buffer. This is used only for the ESP32 in order to add the slave capability on the chip and keep compatability with Arduino.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
Wire.slaveWrite((uint8_t *)message, strlen(message));
|
||||
|
||||
I2C Slave APIs
|
||||
**************
|
||||
|
||||
Here are the I2C slave APIs. These function are intended to be used only for slave mode.
|
||||
|
||||
begin
|
||||
^^^^^
|
||||
|
||||
In slave mode, the ``begin`` function must be used by passing the **slave address**. You can also define the **pins** and the **bus frequency**.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
bool Wire.begin(uint8_t addr, int sdaPin, int sclPin, uint32_t frequency)
|
||||
|
||||
This function will return ``true`` if the peripheral was initialized correctly.
|
||||
|
||||
onReceive
|
||||
^^^^^^^^^
|
||||
|
||||
The ``onReceive`` function is used to define the callback for the data received from the master.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
void onReceive( void (*)(int) );
|
||||
|
||||
onRequest
|
||||
^^^^^^^^^
|
||||
|
||||
The ``onRequest`` function is used to define the callback for the data to be send to the master.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
void onRequest( void (*)(void) );
|
||||
|
||||
slaveWrite
|
||||
^^^^^^^^^^
|
||||
|
||||
The ``slaveWrite`` function writes on the slave response buffer before receiving the response message. This function is only used for adding the slave compatability for the ESP32.
|
||||
|
||||
.. warning:: This function is only required for the ESP32. You **don't** need to use for ESP32-S2 and ESP32-C3.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
size_t slaveWrite(const uint8_t *, size_t);
|
||||
|
||||
Example Application - WireSlave.ino
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Here is an example of how to use the I2C in Slave Mode.
|
||||
|
||||
.. literalinclude:: ../../../libraries/Wire/examples/WireSlave/WireSlave.ino
|
||||
:language: arduino
|
||||
|
||||
.. _Arduino Wire Library: https://www.arduino.cc/en/reference/wire
|
@ -26,7 +26,7 @@ a Wi-Fi network.
|
||||
:width: 520
|
||||
:figclass: align-center
|
||||
|
||||
This mode can be used for serving a HTTP or HTTPS server inside the ESP32, for example.
|
||||
This mode can be used for serving an HTTP or HTTPS server inside the ESP32, for example.
|
||||
|
||||
Working as STA
|
||||
**************
|
||||
@ -38,12 +38,34 @@ The STA mode is used to connect the ESP32 to a Wi-Fi network, provided by an Acc
|
||||
:width: 520
|
||||
:figclass: align-center
|
||||
|
||||
If you need to connect your project to the Internet, this is the mode you are looking for.
|
||||
This is the mode to be used if you want to connect your project to the Internet.
|
||||
|
||||
API Description
|
||||
---------------
|
||||
|
||||
Here is the description about the WiFi API.
|
||||
Here is the description of the WiFi API.
|
||||
|
||||
Common API
|
||||
----------
|
||||
|
||||
Here are the common APIs that are used for both modes, AP and STA.
|
||||
|
||||
useStaticBuffers
|
||||
****************
|
||||
|
||||
This function is used to set the memory allocation mode for the Wi-Fi buffers.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
static void useStaticBuffers(bool bufferMode);
|
||||
|
||||
* Set ``true`` to use the Wi-Fi buffers memory allocation as **static**.
|
||||
* Set ``false`` to set the buffers memory allocation to **dynamic**.
|
||||
|
||||
The use of dynamic allocation is recommended to save memory and reduce resources usage. However, the dynamic performs slightly slower than the static allocation.
|
||||
Use static allocation if you want to have more performance and if your application is multi-tasking.
|
||||
|
||||
By default, the memory allocation will be set to **dynamic** if this function is not being used.
|
||||
|
||||
WiFiAP
|
||||
------
|
||||
|
@ -81,16 +81,22 @@ Add here the third party boards, listed by vendors.
|
||||
creating an `issue on GitHub <https://github.com/espressif/arduino-esp32/issues>`_ and directly
|
||||
link/mention the vendor in the issue description.
|
||||
|
||||
LOLIN
|
||||
*****
|
||||
|
||||
* |board_lolin_d32|
|
||||
* |board_lolin_d32_pro|
|
||||
|
||||
Generic Vendor
|
||||
**************
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
Generic Board Name <generic>
|
||||
|
||||
.. note::
|
||||
Create one file per board or one file with multiple boards. Do not add board information/description on this file.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
Generic Board Name <generic>
|
||||
|
||||
.. note::
|
||||
Create one file per board or one file with multiple boards. Do not add board information/description on this file.
|
||||
|
||||
Resources
|
||||
---------
|
||||
@ -104,3 +110,11 @@ Resources
|
||||
.. _ESP32 Datasheet: https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf
|
||||
.. _ESP32-S2 Datasheet: https://www.espressif.com/sites/default/files/documentation/esp32-s2_datasheet_en.pdf
|
||||
.. _ESP32-C3 Datasheet: https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf
|
||||
|
||||
.. |board_lolin_d32| raw:: html
|
||||
|
||||
<a href="https://www.wemos.cc/en/latest/d32/d32.html" target="_blank">LOLIN D32</a>
|
||||
|
||||
.. |board_lolin_d32_pro| raw:: html
|
||||
|
||||
<a href="https://www.wemos.cc/en/latest/d32/d32_pro.html" target="_blank">LOLIN D32 Pro</a>
|
@ -56,7 +56,7 @@ exclude_patterns = []
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'default'
|
||||
html_logo = 'logo_espressif.png'
|
||||
html_logo = '_static/logo_espressif.png'
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
|
@ -10,6 +10,8 @@ For a simplified method, see `lib-builder <lib_builder>`_.
|
||||
Installation
|
||||
------------
|
||||
|
||||
.. note:: Latest Arduino Core ESP32 version is now compatible with [ESP-IDF v4.4](https://github.com/espressif/esp-idf/tree/release/v4.4). Please consider this compability when using Arduino as component in ESP-IDF.
|
||||
|
||||
- Download and install `ESP-IDF <https://github.com/espressif/esp-idf>`_.
|
||||
- Create blank idf project (from one of the examples).
|
||||
- In the project folder, create a new folder called `components` and clone this repository inside the new created folder.
|
||||
|
@ -35,8 +35,8 @@ Here are the ESP32 series supported by the Arduino-ESP32 project:
|
||||
SoC Stable Development Datasheet
|
||||
======== ====== =========== ===================================
|
||||
ESP32 Yes Yes `ESP32 Datasheet`_
|
||||
ESP32-S2 No Yes `ESP32-S2 Datasheet`_
|
||||
ESP32-C3 No Yes `ESP32-C3 Datasheet`_
|
||||
ESP32-S2 Yes Yes `ESP32-S2 Datasheet`_
|
||||
ESP32-C3 Yes Yes `ESP32-C3 Datasheet`_
|
||||
ESP32-S3 No No Not Available Yet
|
||||
======== ====== =========== ===================================
|
||||
|
||||
|
@ -166,9 +166,7 @@ Debian/Ubuntu
|
||||
mkdir -p ~/Arduino/hardware/espressif && \
|
||||
cd ~/Arduino/hardware/espressif && \
|
||||
git clone https://github.com/espressif/arduino-esp32.git esp32 && \
|
||||
cd esp32 && \
|
||||
git submodule update --init --recursive && \
|
||||
cd tools && \
|
||||
cd esp32/tools && \
|
||||
python3 get.py
|
||||
|
||||
- Restart Arduino IDE.
|
||||
@ -181,9 +179,7 @@ Debian/Ubuntu
|
||||
mkdir -p espressif && \
|
||||
cd espressif && \
|
||||
git clone https://github.com/espressif/arduino-esp32.git esp32 && \
|
||||
cd esp32 && \
|
||||
git submodule update --init --recursive && \
|
||||
cd tools && \
|
||||
cd esp32/tools && \
|
||||
python3 get.py
|
||||
|
||||
Fedora
|
||||
@ -203,9 +199,7 @@ Fedora
|
||||
mkdir -p ~/Arduino/hardware/espressif && \
|
||||
cd ~/Arduino/hardware/espressif && \
|
||||
git clone https://github.com/espressif/arduino-esp32.git esp32 && \
|
||||
cd esp32 && \
|
||||
git submodule update --init --recursive && \
|
||||
cd tools && \
|
||||
cd esp32/tools && \
|
||||
python get.py
|
||||
|
||||
- Restart Arduino IDE.
|
||||
@ -228,9 +222,7 @@ openSUSE
|
||||
mkdir -p ~/Arduino/hardware/espressif && \
|
||||
cd ~/Arduino/hardware/espressif && \
|
||||
git clone https://github.com/espressif/arduino-esp32.git esp32 && \
|
||||
cd esp32 && \
|
||||
git submodule update --init --recursive && \
|
||||
cd tools && \
|
||||
cd esp32/tools && \
|
||||
python get.py
|
||||
|
||||
- Restart Arduino IDE.
|
||||
@ -246,10 +238,8 @@ macOS
|
||||
|
||||
mkdir -p ~/Documents/Arduino/hardware/espressif && \
|
||||
cd ~/Documents/Arduino/hardware/espressif && \
|
||||
git clone https://github.com/espressif/arduino-esp32.git esp32 --depth 1 && \
|
||||
cd esp32 && \
|
||||
git submodule update --init --recursive --depth 1 && \
|
||||
cd tools && \
|
||||
git clone https://github.com/espressif/arduino-esp32.git esp32 && \
|
||||
cd esp32/tools && \
|
||||
python get.py
|
||||
|
||||
Where ``~/Documents/Arduino`` represents your sketch book location as per "Arduino" > "Preferences" > "Sketchbook location" (in the IDE once started). Adjust the command above accordingly.
|
||||
|
@ -2,16 +2,88 @@
|
||||
Libraries
|
||||
#########
|
||||
|
||||
Here is where the Libraries API's descriptions are located.
|
||||
Here is where the Libraries API's descriptions are located:
|
||||
|
||||
Supported Peripherals
|
||||
---------------------
|
||||
|
||||
Currently, the Arduino ESP32 supports the following peripherals with Arduino APIs.
|
||||
|
||||
+---------------+---------------+---------------+---------------+-------------------------------+
|
||||
| Peripheral | ESP32 | ESP32-S2 | ESP32-C3 | Comments |
|
||||
+===============+===============+===============+===============+===============================+
|
||||
| ADC | Yes | Yes | Yes | |
|
||||
+---------------+---------------+---------------+---------------+-------------------------------+
|
||||
| Bluetooth | Yes | Not Supported | Not Supported | Bluetooth Classic |
|
||||
+---------------+---------------+---------------+---------------+-------------------------------+
|
||||
| BLE | Yes | Not Supported | Yes | |
|
||||
+---------------+---------------+---------------+---------------+-------------------------------+
|
||||
| DAC | Yes | Yes | Not Supported | |
|
||||
+---------------+---------------+---------------+---------------+-------------------------------+
|
||||
| Ethernet | Yes | Not Supported | Not Supported | (*) |
|
||||
+---------------+---------------+---------------+---------------+-------------------------------+
|
||||
| GPIO | Yes | Yes | Yes | |
|
||||
+---------------+---------------+---------------+---------------+-------------------------------+
|
||||
| Hall Sensor | Yes | Not Supported | Not Supported | |
|
||||
+---------------+---------------+---------------+---------------+-------------------------------+
|
||||
| I2C | Yes | Yes | Yes | |
|
||||
+---------------+---------------+---------------+---------------+-------------------------------+
|
||||
| I2S | No | No | No | WIP |
|
||||
+---------------+---------------+---------------+---------------+-------------------------------+
|
||||
| LEDC | Yes | Yes | Yes | |
|
||||
+---------------+---------------+---------------+---------------+-------------------------------+
|
||||
| Motor PWM | No | Not Supported | Not Supported | |
|
||||
+---------------+---------------+---------------+---------------+-------------------------------+
|
||||
| Pulse Counter | No | No | No | |
|
||||
+---------------+---------------+---------------+---------------+-------------------------------+
|
||||
| RMT | Yes | Yes | Yes | |
|
||||
+---------------+---------------+---------------+---------------+-------------------------------+
|
||||
| SDIO | No | No | No | |
|
||||
+---------------+---------------+---------------+---------------+-------------------------------+
|
||||
| SPI | Yes | Yes | Yes | |
|
||||
+---------------+---------------+---------------+---------------+-------------------------------+
|
||||
| Timer | Yes | Yes | Yes | |
|
||||
+---------------+---------------+---------------+---------------+-------------------------------+
|
||||
| Temp. Sensor | Not Supported | Yes | Yes | |
|
||||
+---------------+---------------+---------------+---------------+-------------------------------+
|
||||
| Touch | Yes | Yes | Not Supported | |
|
||||
+---------------+---------------+---------------+---------------+-------------------------------+
|
||||
| TWAI | No | No | No | |
|
||||
+---------------+---------------+---------------+---------------+-------------------------------+
|
||||
| UART | Yes | Yes | Yes | |
|
||||
+---------------+---------------+---------------+---------------+-------------------------------+
|
||||
| USB | Not Supported | Yes | Yes | ESP32-C3 only CDC/JTAG |
|
||||
+---------------+---------------+---------------+---------------+-------------------------------+
|
||||
| Wi-Fi | Yes | Yes | Yes | |
|
||||
+---------------+---------------+---------------+---------------+-------------------------------+
|
||||
|
||||
Notes
|
||||
^^^^^
|
||||
|
||||
(*) SPI Ethernet is supported by all ESP32 families and RMII only for ESP32.
|
||||
|
||||
.. note:: Some peripherals are not available for all ESP32 families. To see more details about it, see the corresponding SoC at `Product Selector <https://products.espressif.com>`_ page.
|
||||
|
||||
Datasheet
|
||||
^^^^^^^^^
|
||||
|
||||
* `ESP32 <https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf>`_
|
||||
* `ESP32-S2 <https://www.espressif.com/sites/default/files/documentation/esp32-s2_datasheet_en.pdf>`_
|
||||
* `ESP32-C3 <https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf>`_
|
||||
|
||||
APIs
|
||||
----
|
||||
|
||||
The Arduino ESP32 offers some unique APIs, described in this section:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: API's:
|
||||
|
||||
Bluetooth <api/bluetooth>
|
||||
Deep Sleep <api/deepsleep>
|
||||
ESPNOW <api/espnow>
|
||||
GPIO <api/gpio>
|
||||
I2C <api/i2c>
|
||||
RainMaker <api/rainmaker>
|
||||
Reset Reason <api/reset_reason>
|
||||
Wi-Fi <api/wifi>
|
||||
|
@ -1,5 +1,5 @@
|
||||
###############
|
||||
Troubleshotting
|
||||
Troubleshooting
|
||||
###############
|
||||
|
||||
Common Issues
|
||||
@ -12,16 +12,71 @@ Here are some of the most common issues around the ESP32 development using Ardui
|
||||
Installing
|
||||
----------
|
||||
|
||||
Here are the common issues during the installation.
|
||||
|
||||
Building
|
||||
--------
|
||||
|
||||
Missing Python: "python": executable file not found in $PATH
|
||||
************************************************************
|
||||
|
||||
You are trying to build your sketch using Ubuntu and this message appears:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
"exec: "python": executable file not found in $PATH
|
||||
Error compiling for board ESP32 Dev Module"
|
||||
|
||||
Solution
|
||||
^^^^^^^^
|
||||
|
||||
To avoid this error, you can install the ``python-is-python3`` package to create the symbolic links.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo apt install python-is-python3
|
||||
|
||||
If you are not using Ubuntu, you can check if you have the Python correctly installed or the presence of the symbolic links/environment variables.
|
||||
|
||||
Flashing
|
||||
--------
|
||||
|
||||
* The board is not flashing.
|
||||
Why is my board not flashing/uploading when I try to upload my sketch?
|
||||
**********************************************************************
|
||||
|
||||
To be able to upload the sketch via serial interface, the ESP32 must be in the download mode. The download mode allows you to upload the sketch over the serial port and to get into it, you need to keep the **GPIO0** in LOW while a resetting (**EN** pin) cycle.
|
||||
If you are trying to upload a new sketch and your board is not responding, there are some possible reasons.
|
||||
|
||||
Possible fatal error message from the Arduino IDE:
|
||||
|
||||
*A fatal error occurred: Failed to connect to ESP32: Timed out waiting for packet header*
|
||||
|
||||
Solution
|
||||
^^^^^^^^
|
||||
|
||||
Here are some steps that you can try to:
|
||||
|
||||
* Check your USB cable and try a new one.
|
||||
* Change the USB port.
|
||||
* Check your power supply.
|
||||
* In some instances, you must keep **GPIO0** LOW during the uploading process via the serial interface.
|
||||
* Hold down the **“BOOT”** button in your ESP32 board while uploading/flashing.
|
||||
|
||||
In some development boards, you can try adding the reset delay circuit, as described in the *Power-on Sequence* section on the `ESP32 Hardware Design Guidelines <https://www.espressif.com/sites/default/files/documentation/esp32_hardware_design_guidelines_en.pdf>`_ in order to get into the download mode automatically.
|
||||
|
||||
Hardware
|
||||
--------
|
||||
|
||||
* Power Source
|
||||
* Bad USB cable or charging only cables
|
||||
Why is my computer not detecting my board?
|
||||
**************************************************
|
||||
|
||||
If your board is not being detected after connecting to the USB, you can try to:
|
||||
|
||||
Solution
|
||||
^^^^^^^^
|
||||
|
||||
* Check if the USB driver is missing. - `USB Driver Download Link <https://www.silabs.com/developers/usb-to-uart-bridge-vcp-drivers>`_
|
||||
* Check your USB cable and try a new one.
|
||||
* Change the USB port.
|
||||
* Check your power supply.
|
||||
* Check if the board is damaged or defective.
|
||||
|
113
docs/source/tutorials/blink.rst
Normal file
113
docs/source/tutorials/blink.rst
Normal file
@ -0,0 +1,113 @@
|
||||
##########################
|
||||
Blink Interactive Tutorial
|
||||
##########################
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
This is the interactive blink tutorial using `Wokwi`_. For this tutorial, you don't need the ESP32 board or the Arduino toolchain.
|
||||
|
||||
.. note:: If you don't want to use this tutorial with the simulation, you can copy and paste the :ref:`blink_example_code` from `Wokwi`_ editor and use it on the `Arduino IDE`_ or `PlatformIO`_.
|
||||
|
||||
About this Tutorial
|
||||
-------------------
|
||||
|
||||
This tutorial is the most basic for any get started. In this tutorial, we will show how to set a GPIO pin as an output to drive a LED to blink each 1 second.
|
||||
|
||||
Step by step
|
||||
------------
|
||||
|
||||
In order to make this simple blink tutorial, you'll need to do the following steps.
|
||||
|
||||
1. **Define the GPIO for the LED.**
|
||||
|
||||
.. code-block::
|
||||
|
||||
#define LED 2
|
||||
|
||||
This ``#define LED 2`` will be used to set the GPIO2 as the ``LED`` output pin.
|
||||
|
||||
2. **Setup.**
|
||||
|
||||
Inside the ``setup()`` function, we need to add all things we want to run once during the startup.
|
||||
Here we'll add the ``pinMode`` function to set the pin as output.
|
||||
|
||||
.. code-block::
|
||||
|
||||
void setup() {
|
||||
pinMode(LED, OUTPUT);
|
||||
}
|
||||
|
||||
The first argument is the GPIO number, already defined and the second is the mode, here defined as an output.
|
||||
|
||||
3. **Main Loop.**
|
||||
|
||||
After the ``setup``, the code runs the ``loop`` function infinitely. Here we will handle the GPIO in order to get the LED blinking.
|
||||
|
||||
.. code-block::
|
||||
|
||||
void loop() {
|
||||
digitalWrite(LED, HIGH);
|
||||
delay(100);
|
||||
digitalWrite(LED, LOW);
|
||||
delay(100);
|
||||
}
|
||||
|
||||
The first function is the ``digitalWrite()`` with two arguments:
|
||||
|
||||
* GPIO: Set the GPIO pin. Here defined by our ``LED`` connected to the GPIO2.
|
||||
* State: Set the GPIO state as HIGH (ON) or LOW (OFF).
|
||||
|
||||
This first ``digitalWrite`` we will set the LED ON.
|
||||
|
||||
After the ``digitalWrite``, we will set a ``delay`` function in order to wait for some time, defined in milliseconds.
|
||||
|
||||
Now we can set the GPIO to ``LOW`` to turn the LED off and ``delay`` for more few milliseconds to get the LED blinking.
|
||||
|
||||
4. **Run the code.**
|
||||
|
||||
To run this code, you'll need a development board and the Arduino toolchain installed on your computer. If you don't have both, you can use the simulator to test and edit the code.
|
||||
|
||||
Simulation
|
||||
----------
|
||||
|
||||
This simulator is provided by `Wokwi`_ and you can test the blink code and play with some modifications to learn more about this example.
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<iframe src="https://wokwi.com/arduino/projects/305566932847821378?embed=1" width="100%" height="400" border="0"></iframe>
|
||||
|
||||
Change the parameters, like the delay period, to test the code right on your browser. You can add more LEDs, change the GPIO, and more.
|
||||
|
||||
.. _blink_example_code:
|
||||
|
||||
Example Code
|
||||
------------
|
||||
|
||||
Here is the full blink code.
|
||||
|
||||
.. code-block::
|
||||
|
||||
#define LED 2
|
||||
|
||||
void setup() {
|
||||
pinMode(LED, OUTPUT);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
digitalWrite(LED, HIGH);
|
||||
delay(100);
|
||||
digitalWrite(LED, LOW);
|
||||
delay(100);
|
||||
}
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
* `ESP32 Datasheet`_ (Datasheet)
|
||||
* `Wokwi`_ (Wokwi Website)
|
||||
|
||||
.. _ESP32 Datasheet: https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf
|
||||
.. _Wokwi: https://wokwi.com/
|
||||
.. _PlatformIO: https://docs.espressif.com/projects/arduino-esp32/en/latest/installing.html#platformio
|
||||
.. _Arduino IDE: https://docs.espressif.com/projects/arduino-esp32/en/latest/installing.html#installing-using-boards-manager
|
@ -6,6 +6,7 @@ Tutorials
|
||||
:maxdepth: 2
|
||||
:caption: Tutorials:
|
||||
|
||||
Blink <blink>
|
||||
Basic <basic>
|
||||
DFU <dfu>
|
||||
GPIO Matrix and Pin Mux <io_mux>
|
||||
|
@ -191,7 +191,7 @@ uint8_t* BLECharacteristic::getData() {
|
||||
* @brief Retrieve the current length of the data of the characteristic.
|
||||
* @return Amount of databytes of the characteristic.
|
||||
*/
|
||||
uint8_t BLECharacteristic::getLength() {
|
||||
size_t BLECharacteristic::getLength() {
|
||||
return m_value.getLength();
|
||||
} // getLength
|
||||
|
||||
|
@ -62,7 +62,7 @@ public:
|
||||
BLEUUID getUUID();
|
||||
std::string getValue();
|
||||
uint8_t* getData();
|
||||
uint8_t getLength();
|
||||
size_t getLength();
|
||||
|
||||
void indicate();
|
||||
void notify(bool is_notification = true);
|
||||
|
@ -347,7 +347,7 @@ gatts_event_handler BLEDevice::m_customGattsHandler = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef CLASSIC_BT_ENABLED
|
||||
#ifndef CONFIG_BT_CLASSIC_ENABLED
|
||||
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
|
||||
#endif
|
||||
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
||||
@ -357,7 +357,7 @@ gatts_event_handler BLEDevice::m_customGattsHandler = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef CLASSIC_BT_ENABLED
|
||||
#ifndef CONFIG_BT_CLASSIC_ENABLED
|
||||
errRc = esp_bt_controller_enable(ESP_BT_MODE_BLE);
|
||||
if (errRc != ESP_OK) {
|
||||
log_e("esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
|
@ -157,7 +157,9 @@ void BLEServer::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t
|
||||
|
||||
case ESP_GATTS_MTU_EVT:
|
||||
updatePeerMTU(param->mtu.conn_id, param->mtu.mtu);
|
||||
m_pServerCallbacks->onMtuChanged(this, param);
|
||||
if (m_pServerCallbacks != nullptr) {
|
||||
m_pServerCallbacks->onMtuChanged(this, param);
|
||||
}
|
||||
break;
|
||||
|
||||
// ESP_GATTS_CONNECT_EVT
|
||||
|
@ -11,27 +11,27 @@
|
||||
#include "EEPROM.h"
|
||||
|
||||
// Instantiate eeprom objects with parameter/argument names and sizes
|
||||
EEPROMClass NAMES("eeprom0", 0x500);
|
||||
EEPROMClass HEIGHT("eeprom1", 0x200);
|
||||
EEPROMClass AGE("eeprom2", 0x100);
|
||||
EEPROMClass NAMES("eeprom0");
|
||||
EEPROMClass HEIGHT("eeprom1");
|
||||
EEPROMClass AGE("eeprom2");
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(1000);
|
||||
Serial.println("Testing EEPROMClass\n");
|
||||
if (!NAMES.begin(NAMES.length())) {
|
||||
if (!NAMES.begin(0x500)) {
|
||||
Serial.println("Failed to initialise NAMES");
|
||||
Serial.println("Restarting...");
|
||||
delay(1000);
|
||||
ESP.restart();
|
||||
}
|
||||
if (!HEIGHT.begin(HEIGHT.length())) {
|
||||
if (!HEIGHT.begin(0x200)) {
|
||||
Serial.println("Failed to initialise HEIGHT");
|
||||
Serial.println("Restarting...");
|
||||
delay(1000);
|
||||
ESP.restart();
|
||||
}
|
||||
if (!AGE.begin(AGE.length())) {
|
||||
if (!AGE.begin(0x100)) {
|
||||
Serial.println("Failed to initialise AGE");
|
||||
Serial.println("Restarting...");
|
||||
delay(1000);
|
||||
|
@ -34,7 +34,6 @@ EEPROMClass::EEPROMClass(void)
|
||||
, _size(0)
|
||||
, _dirty(false)
|
||||
, _name("eeprom")
|
||||
, _user_defined_size(0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -45,17 +44,15 @@ EEPROMClass::EEPROMClass(uint32_t sector)
|
||||
, _size(0)
|
||||
, _dirty(false)
|
||||
, _name("eeprom")
|
||||
, _user_defined_size(0)
|
||||
{
|
||||
}
|
||||
|
||||
EEPROMClass::EEPROMClass(const char* name, uint32_t user_defined_size)
|
||||
EEPROMClass::EEPROMClass(const char* name)
|
||||
: _handle(0)
|
||||
, _data(0)
|
||||
, _size(0)
|
||||
, _dirty(false)
|
||||
, _name(name)
|
||||
, _user_defined_size(user_defined_size)
|
||||
{
|
||||
}
|
||||
|
||||
@ -133,7 +130,7 @@ bool EEPROMClass::begin(size_t size) {
|
||||
|
||||
_data = (uint8_t*) malloc(size);
|
||||
if(!_data) {
|
||||
log_e("Not enough memory for %d bytes in EEPROM");
|
||||
log_e("Not enough memory for %d bytes in EEPROM", size);
|
||||
return false;
|
||||
}
|
||||
_size = size;
|
||||
@ -215,7 +212,7 @@ uint8_t * EEPROMClass::getDataPtr() {
|
||||
*/
|
||||
uint16_t EEPROMClass::length ()
|
||||
{
|
||||
return _user_defined_size;
|
||||
return _size;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -35,7 +35,7 @@ typedef uint32_t nvs_handle;
|
||||
class EEPROMClass {
|
||||
public:
|
||||
EEPROMClass(uint32_t sector);
|
||||
EEPROMClass(const char* name, uint32_t user_defined_size);
|
||||
EEPROMClass(const char* name);
|
||||
EEPROMClass(void);
|
||||
~EEPROMClass(void);
|
||||
|
||||
@ -112,7 +112,6 @@ class EEPROMClass {
|
||||
size_t _size;
|
||||
bool _dirty;
|
||||
const char* _name;
|
||||
uint32_t _user_defined_size;
|
||||
};
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_EEPROM)
|
||||
|
@ -14,6 +14,7 @@
|
||||
//#define CAMERA_MODEL_M5STACK_V2_PSRAM // M5Camera version B Has PSRAM
|
||||
//#define CAMERA_MODEL_M5STACK_WIDE // Has PSRAM
|
||||
//#define CAMERA_MODEL_M5STACK_ESP32CAM // No PSRAM
|
||||
//#define CAMERA_MODEL_M5STACK_UNITCAM // No PSRAM
|
||||
//#define CAMERA_MODEL_AI_THINKER // Has PSRAM
|
||||
//#define CAMERA_MODEL_TTGO_T_JOURNAL // No PSRAM
|
||||
|
||||
|
@ -113,6 +113,25 @@
|
||||
#define HREF_GPIO_NUM 26
|
||||
#define PCLK_GPIO_NUM 21
|
||||
|
||||
#elif defined(CAMERA_MODEL_M5STACK_UNITCAM)
|
||||
#define PWDN_GPIO_NUM -1
|
||||
#define RESET_GPIO_NUM 15
|
||||
#define XCLK_GPIO_NUM 27
|
||||
#define SIOD_GPIO_NUM 25
|
||||
#define SIOC_GPIO_NUM 23
|
||||
|
||||
#define Y9_GPIO_NUM 19
|
||||
#define Y8_GPIO_NUM 36
|
||||
#define Y7_GPIO_NUM 18
|
||||
#define Y6_GPIO_NUM 39
|
||||
#define Y5_GPIO_NUM 5
|
||||
#define Y4_GPIO_NUM 34
|
||||
#define Y3_GPIO_NUM 35
|
||||
#define Y2_GPIO_NUM 32
|
||||
#define VSYNC_GPIO_NUM 22
|
||||
#define HREF_GPIO_NUM 26
|
||||
#define PCLK_GPIO_NUM 21
|
||||
|
||||
#elif defined(CAMERA_MODEL_AI_THINKER)
|
||||
#define PWDN_GPIO_NUM 32
|
||||
#define RESET_GPIO_NUM -1
|
||||
|
9
libraries/Ethernet/library.properties
Normal file
9
libraries/Ethernet/library.properties
Normal file
@ -0,0 +1,9 @@
|
||||
name=Ethernet
|
||||
version=2.0.0
|
||||
author=Hristo Gochkov
|
||||
maintainer=Hristo Gochkov <hristo@espressif.com>
|
||||
sentence=Enables network connection (local and Internet) using the ESP32 Ethernet.
|
||||
paragraph=With this library you can instantiate Servers, Clients and send/receive UDP packets through Ethernet. The IP address can be assigned statically or through a DHCP. The library can also manage DNS.
|
||||
category=Communication
|
||||
url=
|
||||
architectures=esp32
|
@ -237,12 +237,6 @@ bool ETHClass::begin(uint8_t phy_addr, int power, int mdc, int mdio, eth_phy_typ
|
||||
esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH();
|
||||
esp_netif_t *eth_netif = esp_netif_new(&cfg);
|
||||
|
||||
if(esp_eth_set_default_handlers(eth_netif) != ESP_OK){
|
||||
log_e("esp_eth_set_default_handlers failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
esp_eth_mac_t *eth_mac = NULL;
|
||||
#if CONFIG_ETH_SPI_ETHERNET_DM9051
|
||||
if(type == ETH_PHY_DM9051){
|
||||
@ -288,7 +282,12 @@ bool ETHClass::begin(uint8_t phy_addr, int power, int mdc, int mdio, eth_phy_typ
|
||||
break;
|
||||
#endif
|
||||
case ETH_PHY_KSZ8081:
|
||||
#if ESP_IDF_VERSION > ESP_IDF_VERSION_VAL(4,3,0)
|
||||
eth_phy = esp_eth_phy_new_ksz8081(&phy_config);
|
||||
#else
|
||||
log_e("unsupported ethernet type 'ETH_PHY_KSZ8081'");
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -369,6 +368,10 @@ bool ETHClass::begin(uint8_t phy_addr, int power, int mdc, int mdio, eth_phy_typ
|
||||
log_e("esp_eth_init error: %d", err);
|
||||
}
|
||||
#endif
|
||||
// holds a few microseconds to let DHCP start and enter into a good state
|
||||
// FIX ME -- adresses issue https://github.com/espressif/arduino-esp32/issues/5733
|
||||
delay(50);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -397,7 +400,8 @@ bool ETHClass::config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, I
|
||||
if(err != ERR_OK){
|
||||
log_e("STA IP could not be configured! Error: %d", err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(info.ip.addr){
|
||||
staticIP = true;
|
||||
} else {
|
@ -148,6 +148,10 @@ void setup(){
|
||||
Serial.println("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
Serial.println("Contacting Time Server");
|
||||
/*
|
||||
Note: Bundled Arduino lwip supports only ONE ntp server, 2nd and 3rd options are silently ignored
|
||||
see CONFIG_LWIP_DHCP_MAX_NTP_SERVERS define in ./tools/sdk/esp32/sdkconfig
|
||||
*/
|
||||
configTime(3600*timezone, daysavetime*3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
|
||||
struct tm tmstruct ;
|
||||
delay(2000);
|
||||
|
@ -168,7 +168,7 @@ size_t F_Fat::freeBytes()
|
||||
|
||||
bool F_Fat::exists(const char* path)
|
||||
{
|
||||
File f = open(path, "r");
|
||||
File f = open(path, "r",false);
|
||||
return (f == true) && !f.isDirectory();
|
||||
}
|
||||
|
||||
|
@ -186,18 +186,18 @@ void File::rewindDirectory(void)
|
||||
_p->rewindDirectory();
|
||||
}
|
||||
|
||||
File FS::open(const String& path, const char* mode)
|
||||
File FS::open(const String& path, const char* mode, const bool create)
|
||||
{
|
||||
return open(path.c_str(), mode);
|
||||
return open(path.c_str(), mode, create);
|
||||
}
|
||||
|
||||
File FS::open(const char* path, const char* mode)
|
||||
File FS::open(const char* path, const char* mode, const bool create)
|
||||
{
|
||||
if (!_impl) {
|
||||
return File();
|
||||
}
|
||||
|
||||
return File(_impl->open(path, mode));
|
||||
return File(_impl->open(path, mode, create));
|
||||
}
|
||||
|
||||
bool FS::exists(const char* path)
|
||||
|
@ -89,8 +89,8 @@ class FS
|
||||
public:
|
||||
FS(FSImplPtr impl) : _impl(impl) { }
|
||||
|
||||
File open(const char* path, const char* mode = FILE_READ);
|
||||
File open(const String& path, const char* mode = FILE_READ);
|
||||
File open(const char* path, const char* mode = FILE_READ, const bool create = false);
|
||||
File open(const String& path, const char* mode = FILE_READ, const bool create = false);
|
||||
|
||||
bool exists(const char* path);
|
||||
bool exists(const String& path);
|
||||
|
@ -53,7 +53,7 @@ protected:
|
||||
public:
|
||||
FSImpl() : _mountpoint(NULL) { }
|
||||
virtual ~FSImpl() { }
|
||||
virtual FileImplPtr open(const char* path, const char* mode) = 0;
|
||||
virtual FileImplPtr open(const char* path, const char* mode, const bool create) = 0;
|
||||
virtual bool exists(const char* path) = 0;
|
||||
virtual bool rename(const char* pathFrom, const char* pathTo) = 0;
|
||||
virtual bool remove(const char* path) = 0;
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
using namespace fs;
|
||||
|
||||
FileImplPtr VFSImpl::open(const char* fpath, const char* mode)
|
||||
FileImplPtr VFSImpl::open(const char* fpath, const char* mode, const bool create)
|
||||
{
|
||||
if(!_mountpoint) {
|
||||
log_e("File system is not mounted");
|
||||
@ -37,7 +37,7 @@ FileImplPtr VFSImpl::open(const char* fpath, const char* mode)
|
||||
sprintf(temp,"%s%s", _mountpoint, fpath);
|
||||
|
||||
struct stat st;
|
||||
//file lound
|
||||
//file found
|
||||
if(!stat(temp, &st)) {
|
||||
free(temp);
|
||||
if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) {
|
||||
@ -47,12 +47,6 @@ FileImplPtr VFSImpl::open(const char* fpath, const char* mode)
|
||||
return FileImplPtr();
|
||||
}
|
||||
|
||||
//file not found but mode permits creation
|
||||
if(mode && mode[0] != 'r') {
|
||||
free(temp);
|
||||
return std::make_shared<VFSFileImpl>(this, fpath, mode);
|
||||
}
|
||||
|
||||
//try to open this as directory (might be mount point)
|
||||
DIR * d = opendir(temp);
|
||||
if(d) {
|
||||
@ -61,7 +55,51 @@ FileImplPtr VFSImpl::open(const char* fpath, const char* mode)
|
||||
return std::make_shared<VFSFileImpl>(this, fpath, mode);
|
||||
}
|
||||
|
||||
log_e("%s does not exist", temp);
|
||||
//file not found but mode permits file creation without folder creation
|
||||
if((mode && mode[0] != 'r') && (!create)){
|
||||
free(temp);
|
||||
return std::make_shared<VFSFileImpl>(this, fpath, mode);
|
||||
}
|
||||
|
||||
////file not found but mode permits file creation and folder creation
|
||||
if((mode && mode[0] != 'r') && create){
|
||||
|
||||
char *token;
|
||||
char *folder = (char *)malloc(strlen(fpath));
|
||||
|
||||
int start_index = 0;
|
||||
int end_index = 0;
|
||||
|
||||
token = strchr(fpath+1,'/');
|
||||
end_index = (token-fpath);
|
||||
|
||||
while (token != NULL)
|
||||
{
|
||||
memcpy(folder,fpath + start_index, end_index-start_index);
|
||||
folder[end_index-start_index] = '\0';
|
||||
|
||||
if(!VFSImpl::mkdir(folder))
|
||||
{
|
||||
log_e("Creating folder: %s failed!",folder);
|
||||
return FileImplPtr();
|
||||
}
|
||||
|
||||
token=strchr(token+1,'/');
|
||||
if(token != NULL)
|
||||
{
|
||||
end_index = (token-fpath);
|
||||
memset(folder, 0, strlen(folder));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
free(folder);
|
||||
free(temp);
|
||||
return std::make_shared<VFSFileImpl>(this, fpath, mode);
|
||||
|
||||
}
|
||||
|
||||
log_e("%s does not exist, no permits for creation", temp);
|
||||
free(temp);
|
||||
return FileImplPtr();
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ protected:
|
||||
friend class VFSFileImpl;
|
||||
|
||||
public:
|
||||
FileImplPtr open(const char* path, const char* mode) override;
|
||||
FileImplPtr open(const char* path, const char* mode, const bool create) override;
|
||||
bool exists(const char* path) override;
|
||||
bool rename(const char* pathFrom, const char* pathTo) override;
|
||||
bool remove(const char* path) override;
|
||||
|
@ -261,6 +261,10 @@ bool HTTPClient::beginInternal(String url, const char* expectedProtocol)
|
||||
url.remove(0, (index + 3)); // remove http:// or https://
|
||||
|
||||
index = url.indexOf('/');
|
||||
if (index == -1) {
|
||||
index = url.length();
|
||||
url += '/';
|
||||
}
|
||||
String host = url.substring(0, index);
|
||||
url.remove(0, index); // remove host part
|
||||
|
||||
@ -459,6 +463,17 @@ void HTTPClient::setAuthorization(const char * auth)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set the Authorization type for the http request
|
||||
* @param authType const char *
|
||||
*/
|
||||
void HTTPClient::setAuthorizationType(const char * authType)
|
||||
{
|
||||
if(authType) {
|
||||
_authorizationType = authType;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set the timeout (ms) for establishing a connection to the server
|
||||
* @param connectTimeout int32_t
|
||||
@ -1174,7 +1189,9 @@ bool HTTPClient::sendHeader(const char * type)
|
||||
|
||||
if(_base64Authorization.length()) {
|
||||
_base64Authorization.replace("\n", "");
|
||||
header += F("Authorization: Basic ");
|
||||
header += F("Authorization: ");
|
||||
header += _authorizationType;
|
||||
header += " ";
|
||||
header += _base64Authorization;
|
||||
header += "\r\n";
|
||||
}
|
||||
@ -1264,6 +1281,8 @@ int HTTPClient::handleHeaderResponse()
|
||||
log_d("Transfer-Encoding: %s", transferEncoding.c_str());
|
||||
if(transferEncoding.equalsIgnoreCase("chunked")) {
|
||||
_transferEncoding = HTTPC_TE_CHUNKED;
|
||||
} else if(transferEncoding.equalsIgnoreCase("identity")) {
|
||||
_transferEncoding = HTTPC_TE_IDENTITY;
|
||||
} else {
|
||||
return HTTPC_ERROR_ENCODING;
|
||||
}
|
||||
|
@ -171,6 +171,7 @@ public:
|
||||
void setUserAgent(const String& userAgent);
|
||||
void setAuthorization(const char * user, const char * password);
|
||||
void setAuthorization(const char * auth);
|
||||
void setAuthorizationType(const char * authType);
|
||||
void setConnectTimeout(int32_t connectTimeout);
|
||||
void setTimeout(uint16_t timeout);
|
||||
|
||||
@ -251,6 +252,7 @@ protected:
|
||||
String _headers;
|
||||
String _userAgent = "ESP32HTTPClient";
|
||||
String _base64Authorization;
|
||||
String _authorizationType = "Basic";
|
||||
|
||||
/// Response handling
|
||||
RequestArgument* _currentHeaders = nullptr;
|
||||
|
@ -17,6 +17,10 @@ WiFiMulti WiFiMulti;
|
||||
|
||||
// Set time via NTP, as required for x.509 validation
|
||||
void setClock() {
|
||||
/*
|
||||
Note: Bundled Arduino lwip supports only ONE ntp server, 2nd and 3rd options are silently ignored
|
||||
see CONFIG_LWIP_DHCP_MAX_NTP_SERVERS define in ./tools/sdk/esp32/sdkconfig
|
||||
*/
|
||||
configTime(0, 0, "pool.ntp.org", "time.nist.gov"); // UTC
|
||||
|
||||
Serial.print(F("Waiting for NTP time sync: "));
|
||||
|
@ -160,6 +160,10 @@ void setup(){
|
||||
Serial.println("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
Serial.println("Contacting Time Server");
|
||||
/*
|
||||
Note: Bundled Arduino lwip supports only ONE ntp server, 2nd and 3rd options are silently ignored
|
||||
see CONFIG_LWIP_DHCP_MAX_NTP_SERVERS define in ./tools/sdk/esp32/sdkconfig
|
||||
*/
|
||||
configTime(3600*timezone, daysavetime*3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
|
||||
struct tm tmstruct ;
|
||||
delay(2000);
|
||||
|
@ -45,7 +45,7 @@ LittleFSImpl::LittleFSImpl()
|
||||
|
||||
bool LittleFSImpl::exists(const char* path)
|
||||
{
|
||||
File f = open(path, "r");
|
||||
File f = open(path, "r",false);
|
||||
return (f == true);
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
// 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
|
||||
@ -43,7 +44,7 @@ bool Preferences::begin(const char * name, bool readOnly, const char* partition_
|
||||
}
|
||||
err = nvs_open_from_partition(partition_label, name, readOnly ? NVS_READONLY : NVS_READWRITE, &_handle);
|
||||
} else {
|
||||
err = nvs_open(name, readOnly?NVS_READONLY:NVS_READWRITE, &_handle);
|
||||
err = nvs_open(name, readOnly ? NVS_READONLY : NVS_READWRITE, &_handle);
|
||||
}
|
||||
if(err){
|
||||
log_e("nvs_open failed: %s", nvs_error(err));
|
||||
@ -74,6 +75,11 @@ bool Preferences::clear(){
|
||||
log_e("nvs_erase_all fail: %s", nvs_error(err));
|
||||
return false;
|
||||
}
|
||||
err = nvs_commit(_handle);
|
||||
if(err){
|
||||
log_e("nvs_commit fail: %s", nvs_error(err));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -90,6 +96,11 @@ bool Preferences::remove(const char * key){
|
||||
log_e("nvs_erase_key fail: %s %s", key, nvs_error(err));
|
||||
return false;
|
||||
}
|
||||
err = nvs_commit(_handle);
|
||||
if(err){
|
||||
log_e("nvs_commit fail: %s %s", key, nvs_error(err));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -164,6 +164,10 @@ void setup(){
|
||||
Serial.println("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
Serial.println("Contacting Time Server");
|
||||
/*
|
||||
Note: Bundled Arduino lwip supports only ONE ntp server, 2nd and 3rd options are silently ignored
|
||||
see CONFIG_LWIP_DHCP_MAX_NTP_SERVERS define in ./tools/sdk/esp32/sdkconfig
|
||||
*/
|
||||
configTime(3600*timezone, daysavetime*3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
|
||||
struct tm tmstruct ;
|
||||
delay(2000);
|
||||
|
@ -164,6 +164,10 @@ void setup(){
|
||||
Serial.println("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
Serial.println("Contacting Time Server");
|
||||
/*
|
||||
Note: Bundled Arduino lwip supports only ONE ntp server, 2nd and 3rd options are silently ignored
|
||||
see CONFIG_LWIP_DHCP_MAX_NTP_SERVERS define in ./tools/sdk/esp32/sdkconfig
|
||||
*/
|
||||
configTime(3600*timezone, daysavetime*3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
|
||||
struct tm tmstruct ;
|
||||
delay(2000);
|
||||
|
@ -36,7 +36,7 @@ SDMMCFS::SDMMCFS(FSImplPtr impl)
|
||||
: FS(impl), _card(NULL)
|
||||
{}
|
||||
|
||||
bool SDMMCFS::begin(const char * mountpoint, bool mode1bit, bool format_if_mount_failed)
|
||||
bool SDMMCFS::begin(const char * mountpoint, bool mode1bit, bool format_if_mount_failed, int sdmmc_frequency)
|
||||
{
|
||||
if(_card) {
|
||||
return true;
|
||||
@ -46,7 +46,7 @@ bool SDMMCFS::begin(const char * mountpoint, bool mode1bit, bool format_if_mount
|
||||
sdmmc_host_t host;
|
||||
host.flags = SDMMC_HOST_FLAG_4BIT;
|
||||
host.slot = SDMMC_HOST_SLOT_1;
|
||||
host.max_freq_khz = SDMMC_FREQ_DEFAULT;
|
||||
host.max_freq_khz = sdmmc_frequency;
|
||||
host.io_voltage = 3.3f;
|
||||
host.init = &sdmmc_host_init;
|
||||
host.set_bus_width = &sdmmc_host_set_bus_width;
|
||||
@ -54,11 +54,10 @@ bool SDMMCFS::begin(const char * mountpoint, bool mode1bit, bool format_if_mount
|
||||
host.set_bus_ddr_mode = &sdmmc_host_set_bus_ddr_mode;
|
||||
host.set_card_clk = &sdmmc_host_set_card_clk;
|
||||
host.do_transaction = &sdmmc_host_do_transaction;
|
||||
host.deinit_p = &sdspi_host_remove_device;
|
||||
host.deinit = &sdmmc_host_deinit;
|
||||
host.io_int_enable = &sdmmc_host_io_int_enable;
|
||||
host.io_int_wait = &sdmmc_host_io_int_wait;
|
||||
host.command_timeout_ms = 0;
|
||||
host.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
|
||||
#ifdef BOARD_HAS_1BIT_SDMMC
|
||||
mode1bit = true;
|
||||
#endif
|
||||
|
@ -21,6 +21,13 @@
|
||||
#include "driver/sdmmc_types.h"
|
||||
#include "sd_defines.h"
|
||||
|
||||
// If reading/writing to the SD card is unstable,
|
||||
// you can define BOARD_MAX_SDMMC_FREQ with lower value (Ex. SDMMC_FREQ_DEFAULT)
|
||||
// in pins_arduino.h for your board variant.
|
||||
#ifndef BOARD_MAX_SDMMC_FREQ
|
||||
#define BOARD_MAX_SDMMC_FREQ SDMMC_FREQ_HIGHSPEED
|
||||
#endif
|
||||
|
||||
namespace fs
|
||||
{
|
||||
|
||||
@ -31,7 +38,7 @@ protected:
|
||||
|
||||
public:
|
||||
SDMMCFS(FSImplPtr impl);
|
||||
bool begin(const char * mountpoint="/sdcard", bool mode1bit=false, bool format_if_mount_failed=false);
|
||||
bool begin(const char * mountpoint="/sdcard", bool mode1bit=false, bool format_if_mount_failed=false, int sdmmc_frequency=BOARD_MAX_SDMMC_FREQ);
|
||||
void end();
|
||||
sdcard_type_t cardType();
|
||||
uint64_t cardSize();
|
||||
|
@ -76,36 +76,24 @@ void setup() {
|
||||
|
||||
//set up slave select pins as outputs as the Arduino API
|
||||
//doesn't handle automatically pulling SS low
|
||||
pinMode(VSPI_SS, OUTPUT); //VSPI SS
|
||||
pinMode(HSPI_SS, OUTPUT); //HSPI SS
|
||||
pinMode(vspi->pinSS(), OUTPUT); //VSPI SS
|
||||
pinMode(hspi->pinSS(), OUTPUT); //HSPI SS
|
||||
|
||||
}
|
||||
|
||||
// the loop function runs over and over again until power down or reset
|
||||
void loop() {
|
||||
//use the SPI buses
|
||||
vspiCommand();
|
||||
hspiCommand();
|
||||
spiCommand(vspi, 0b01010101); // junk data to illustrate usage
|
||||
spiCommand(hspi, 0b11001100);
|
||||
delay(100);
|
||||
}
|
||||
|
||||
void vspiCommand() {
|
||||
byte data = 0b01010101; // junk data to illustrate usage
|
||||
|
||||
void spiCommand(SPIClass *spi, byte data) {
|
||||
//use it as you would the regular arduino SPI API
|
||||
vspi->beginTransaction(SPISettings(spiClk, MSBFIRST, SPI_MODE0));
|
||||
digitalWrite(VSPI_SS, LOW); //pull SS slow to prep other end for transfer
|
||||
vspi->transfer(data);
|
||||
digitalWrite(VSPI_SS, HIGH); //pull ss high to signify end of data transfer
|
||||
vspi->endTransaction();
|
||||
}
|
||||
|
||||
void hspiCommand() {
|
||||
byte stuff = 0b11001100;
|
||||
|
||||
hspi->beginTransaction(SPISettings(spiClk, MSBFIRST, SPI_MODE0));
|
||||
digitalWrite(HSPI_SS, LOW);
|
||||
hspi->transfer(stuff);
|
||||
digitalWrite(HSPI_SS, HIGH);
|
||||
hspi->endTransaction();
|
||||
spi->beginTransaction(SPISettings(spiClk, MSBFIRST, SPI_MODE0));
|
||||
digitalWrite(spi->pinSS(), LOW); //pull SS slow to prep other end for transfer
|
||||
spi->transfer(data);
|
||||
digitalWrite(spi->pinSS(), HIGH); //pull ss high to signify end of data transfer
|
||||
spi->endTransaction();
|
||||
}
|
||||
|
@ -83,6 +83,7 @@ public:
|
||||
void writePattern(const uint8_t * data, uint8_t size, uint32_t repeat);
|
||||
|
||||
spi_t * bus(){ return _spi; }
|
||||
int8_t pinSS() { return _ss; }
|
||||
};
|
||||
|
||||
extern SPIClass SPI;
|
||||
|
@ -148,6 +148,10 @@ void setup(){
|
||||
Serial.println("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
Serial.println("Contacting Time Server");
|
||||
/*
|
||||
Note: Bundled Arduino lwip supports only ONE ntp server, 2nd and 3rd options are silently ignored
|
||||
see CONFIG_LWIP_DHCP_MAX_NTP_SERVERS define in ./tools/sdk/esp32/sdkconfig
|
||||
*/
|
||||
configTime(3600*timezone, daysavetime*3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
|
||||
struct tm tmstruct ;
|
||||
delay(2000);
|
||||
|
@ -39,7 +39,7 @@ SPIFFSImpl::SPIFFSImpl()
|
||||
|
||||
bool SPIFFSImpl::exists(const char* path)
|
||||
{
|
||||
File f = open(path, "r");
|
||||
File f = open(path, "r",false);
|
||||
return (f == true) && !f.isDirectory();
|
||||
}
|
||||
|
||||
|
213
libraries/USB/examples/CompositeDevice/CompositeDevice.ino
Normal file
213
libraries/USB/examples/CompositeDevice/CompositeDevice.ino
Normal file
@ -0,0 +1,213 @@
|
||||
#include "USB.h"
|
||||
#include "USBHIDMouse.h"
|
||||
#include "USBHIDKeyboard.h"
|
||||
#include "USBHIDGamepad.h"
|
||||
#include "USBHIDConsumerControl.h"
|
||||
#include "USBHIDSystemControl.h"
|
||||
#include "USBHIDVendor.h"
|
||||
#include "FirmwareMSC.h"
|
||||
|
||||
#if !ARDUINO_USB_MSC_ON_BOOT
|
||||
FirmwareMSC MSC_Update;
|
||||
#endif
|
||||
#if ARDUINO_USB_CDC_ON_BOOT
|
||||
#define HWSerial Serial0
|
||||
#define USBSerial Serial
|
||||
#else
|
||||
#define HWSerial Serial
|
||||
USBCDC USBSerial;
|
||||
#endif
|
||||
|
||||
USBHID HID;
|
||||
USBHIDKeyboard Keyboard;
|
||||
USBHIDMouse Mouse;
|
||||
USBHIDGamepad Gamepad;
|
||||
USBHIDConsumerControl ConsumerControl;
|
||||
USBHIDSystemControl SystemControl;
|
||||
USBHIDVendor Vendor;
|
||||
|
||||
const int buttonPin = 0;
|
||||
int previousButtonState = HIGH;
|
||||
|
||||
static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data){
|
||||
if(event_base == ARDUINO_USB_EVENTS){
|
||||
arduino_usb_event_data_t * data = (arduino_usb_event_data_t*)event_data;
|
||||
switch (event_id){
|
||||
case ARDUINO_USB_STARTED_EVENT:
|
||||
HWSerial.println("USB PLUGGED");
|
||||
break;
|
||||
case ARDUINO_USB_STOPPED_EVENT:
|
||||
HWSerial.println("USB UNPLUGGED");
|
||||
break;
|
||||
case ARDUINO_USB_SUSPEND_EVENT:
|
||||
HWSerial.printf("USB SUSPENDED: remote_wakeup_en: %u\n", data->suspend.remote_wakeup_en);
|
||||
break;
|
||||
case ARDUINO_USB_RESUME_EVENT:
|
||||
HWSerial.println("USB RESUMED");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if(event_base == ARDUINO_USB_CDC_EVENTS){
|
||||
arduino_usb_cdc_event_data_t * data = (arduino_usb_cdc_event_data_t*)event_data;
|
||||
switch (event_id){
|
||||
case ARDUINO_USB_CDC_CONNECTED_EVENT:
|
||||
HWSerial.println("CDC CONNECTED");
|
||||
break;
|
||||
case ARDUINO_USB_CDC_DISCONNECTED_EVENT:
|
||||
HWSerial.println("CDC DISCONNECTED");
|
||||
break;
|
||||
case ARDUINO_USB_CDC_LINE_STATE_EVENT:
|
||||
HWSerial.printf("CDC LINE STATE: dtr: %u, rts: %u\n", data->line_state.dtr, data->line_state.rts);
|
||||
break;
|
||||
case ARDUINO_USB_CDC_LINE_CODING_EVENT:
|
||||
HWSerial.printf("CDC LINE CODING: bit_rate: %u, data_bits: %u, stop_bits: %u, parity: %u\n", data->line_coding.bit_rate, data->line_coding.data_bits, data->line_coding.stop_bits, data->line_coding.parity);
|
||||
break;
|
||||
case ARDUINO_USB_CDC_RX_EVENT:
|
||||
HWSerial.printf("CDC RX [%u]:", data->rx.len);
|
||||
{
|
||||
uint8_t buf[data->rx.len];
|
||||
size_t len = USBSerial.read(buf, data->rx.len);
|
||||
HWSerial.write(buf, len);
|
||||
}
|
||||
HWSerial.println();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if(event_base == ARDUINO_FIRMWARE_MSC_EVENTS){
|
||||
arduino_firmware_msc_event_data_t * data = (arduino_firmware_msc_event_data_t*)event_data;
|
||||
switch (event_id){
|
||||
case ARDUINO_FIRMWARE_MSC_START_EVENT:
|
||||
HWSerial.println("MSC Update Start");
|
||||
break;
|
||||
case ARDUINO_FIRMWARE_MSC_WRITE_EVENT:
|
||||
//HWSerial.printf("MSC Update Write %u bytes at offset %u\n", data->write.size, data->write.offset);
|
||||
HWSerial.print(".");
|
||||
break;
|
||||
case ARDUINO_FIRMWARE_MSC_END_EVENT:
|
||||
HWSerial.printf("\nMSC Update End: %u bytes\n", data->end.size);
|
||||
break;
|
||||
case ARDUINO_FIRMWARE_MSC_ERROR_EVENT:
|
||||
HWSerial.printf("MSC Update ERROR! Progress: %u bytes\n", data->error.size);
|
||||
break;
|
||||
case ARDUINO_FIRMWARE_MSC_POWER_EVENT:
|
||||
HWSerial.printf("MSC Update Power: power: %u, start: %u, eject: %u\n", data->power.power_condition, data->power.start, data->power.load_eject);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if(event_base == ARDUINO_USB_HID_EVENTS){
|
||||
arduino_usb_hid_event_data_t * data = (arduino_usb_hid_event_data_t*)event_data;
|
||||
switch (event_id){
|
||||
case ARDUINO_USB_HID_SET_PROTOCOL_EVENT:
|
||||
HWSerial.printf("HID SET PROTOCOL: %s\n", data->set_protocol.protocol?"REPORT":"BOOT");
|
||||
break;
|
||||
case ARDUINO_USB_HID_SET_IDLE_EVENT:
|
||||
HWSerial.printf("HID SET IDLE: %u\n", data->set_idle.idle_rate);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if(event_base == ARDUINO_USB_HID_KEYBOARD_EVENTS){
|
||||
arduino_usb_hid_keyboard_event_data_t * data = (arduino_usb_hid_keyboard_event_data_t*)event_data;
|
||||
switch (event_id){
|
||||
case ARDUINO_USB_HID_KEYBOARD_LED_EVENT:
|
||||
HWSerial.printf("HID KEYBOARD LED: NumLock:%u, CapsLock:%u, ScrollLock:%u\n", data->numlock, data->capslock, data->scrolllock);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if(event_base == ARDUINO_USB_HID_VENDOR_EVENTS){
|
||||
arduino_usb_hid_vendor_event_data_t * data = (arduino_usb_hid_vendor_event_data_t*)event_data;
|
||||
switch (event_id){
|
||||
case ARDUINO_USB_HID_VENDOR_GET_FEATURE_EVENT:
|
||||
HWSerial.printf("HID VENDOR GET FEATURE: len:%u\n", data->len);
|
||||
for(uint16_t i=0; i<data->len; i++){
|
||||
HWSerial.write(data->buffer[i]?data->buffer[i]:'.');
|
||||
}
|
||||
HWSerial.println();
|
||||
break;
|
||||
case ARDUINO_USB_HID_VENDOR_SET_FEATURE_EVENT:
|
||||
HWSerial.printf("HID VENDOR SET FEATURE: len:%u\n", data->len);
|
||||
for(uint16_t i=0; i<data->len; i++){
|
||||
HWSerial.write(data->buffer[i]?data->buffer[i]:'.');
|
||||
}
|
||||
HWSerial.println();
|
||||
break;
|
||||
case ARDUINO_USB_HID_VENDOR_OUTPUT_EVENT:
|
||||
HWSerial.printf("HID VENDOR OUTPUT: len:%u\n", data->len);
|
||||
for(uint16_t i=0; i<data->len; i++){
|
||||
HWSerial.write(Vendor.read());
|
||||
}
|
||||
HWSerial.println();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
HWSerial.begin(115200);
|
||||
HWSerial.setDebugOutput(true);
|
||||
|
||||
USB.onEvent(usbEventCallback);
|
||||
USBSerial.onEvent(usbEventCallback);
|
||||
MSC_Update.onEvent(usbEventCallback);
|
||||
HID.onEvent(usbEventCallback);
|
||||
Keyboard.onEvent(usbEventCallback);
|
||||
Vendor.onEvent(usbEventCallback);
|
||||
|
||||
USBSerial.begin();
|
||||
MSC_Update.begin();
|
||||
Vendor.begin();
|
||||
Mouse.begin();
|
||||
Keyboard.begin();
|
||||
Gamepad.begin();
|
||||
ConsumerControl.begin();
|
||||
SystemControl.begin();
|
||||
USB.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
int buttonState = digitalRead(buttonPin);
|
||||
if (HID.ready() && buttonState != previousButtonState) {
|
||||
previousButtonState = buttonState;
|
||||
if (buttonState == LOW) {
|
||||
HWSerial.println("Button Pressed");
|
||||
USBSerial.println("Button Pressed");
|
||||
Vendor.println("Button Pressed");
|
||||
Mouse.move(10,10);
|
||||
Keyboard.pressRaw(HID_KEY_CAPS_LOCK);
|
||||
Gamepad.leftStick(100,100);
|
||||
ConsumerControl.press(CONSUMER_CONTROL_VOLUME_INCREMENT);
|
||||
//SystemControl.press(SYSTEM_CONTROL_POWER_OFF);
|
||||
} else {
|
||||
Keyboard.releaseRaw(HID_KEY_CAPS_LOCK);
|
||||
Gamepad.leftStick(0,0);
|
||||
ConsumerControl.release();
|
||||
//SystemControl.release();
|
||||
Vendor.println("Button Released");
|
||||
USBSerial.println("Button Released");
|
||||
HWSerial.println("Button Released");
|
||||
}
|
||||
delay(100);
|
||||
}
|
||||
|
||||
while(HWSerial.available()){
|
||||
size_t l = HWSerial.available();
|
||||
uint8_t b[l];
|
||||
l = HWSerial.read(b, l);
|
||||
USBSerial.write(b, l);
|
||||
if(HID.ready()){
|
||||
Vendor.write(b,l);
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user