forked from espressif/arduino-esp32
Compare commits
179 Commits
Author | SHA1 | Date | |
---|---|---|---|
caef4006af | |||
44fbde0189 | |||
8c88ecbf77 | |||
5724275cd8 | |||
1c94c38dbb | |||
7cf162346a | |||
c66c7fe27e | |||
7ba11cc1ae | |||
c3d41c9b54 | |||
d6934a5289 | |||
6b90627b21 | |||
063119ac87 | |||
7b96374ea6 | |||
82ec74a072 | |||
5940d89e67 | |||
bb09615391 | |||
c2c8d18992 | |||
39b9e1e533 | |||
40a5c1e461 | |||
2981bde88f | |||
6d400df952 | |||
7bb30b3cf8 | |||
4b638de19d | |||
2463f57246 | |||
f29f4485b5 | |||
8a8f87d3a0 | |||
082491d552 | |||
794de50bf4 | |||
5e7db8dfac | |||
fb00b51f99 | |||
5dc8fb83c9 | |||
cb25fe8c7e | |||
f5b04b9197 | |||
8c5d18dd85 | |||
fa03966fcf | |||
3750b14d74 | |||
cebac569de | |||
d0e73bd269 | |||
26dddc5f94 | |||
c87ede88df | |||
c90dffef44 | |||
1945fae795 | |||
6e0b57784f | |||
399f4ecbb3 | |||
a5002c86a0 | |||
c9916c463f | |||
bd2be80b54 | |||
6f1f394680 | |||
5de09a9a49 | |||
b94b38c9d1 | |||
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 |
54
.github/ISSUE_TEMPLATE/Feature-request.yml
vendored
Normal file
54
.github/ISSUE_TEMPLATE/Feature-request.yml
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
name: Feature request
|
||||
description: Suggest an idea for this project
|
||||
labels: ["Type: Feature request"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
* We welcome any ideas or feature requests! It is helpful if you can explain exactly why the feature would be useful.
|
||||
* There are usually some outstanding feature requests in the [existing issues list](https://github.com/espressif/arduino-esp32/issues?q=is%3Aopen+is%3Aissue+label%3A%22Type%3A+Feature+request%22), feel free to add comments to them.
|
||||
* If you would like to contribute, please read the [contributions guide](https://docs.espressif.com/projects/arduino-esp32/en/latest/contributing.html).
|
||||
- type: input
|
||||
id: Area
|
||||
attributes:
|
||||
label: Related area
|
||||
description: Please briefly explain the area of your Feature Request.
|
||||
placeholder: eg. Board support, specific Peripheral, BT, Wifi...
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: HW
|
||||
attributes:
|
||||
label: Hardware specification
|
||||
description: Please provide if your proposal depends on specific Hardware.
|
||||
placeholder: eg. Support for ESP32 DevKitC, ESP32-C3 DevKitM...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: problem-related
|
||||
attributes:
|
||||
label: Is your feature request related to a problem?
|
||||
description: Please provide a clear and concise description of what the problem is. Add relevant issue link.
|
||||
placeholder: ex. I'm facing the issue/missing function...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: solution
|
||||
attributes:
|
||||
label: Describe the solution you'd like
|
||||
description: Please provide a clear and concise description of what you want to happen.
|
||||
placeholder: ex. When using this function...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Describe alternatives you've considered
|
||||
description: Please provide a clear and concise description of any alternative solutions or features you've considered.
|
||||
placeholder: ex. Choosing other approach wouldn't work, because...
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Please add any other context or screenshots about the feature request here.
|
||||
placeholder: ex. This would work only when ...
|
130
.github/ISSUE_TEMPLATE/Issue-report.yml
vendored
Normal file
130
.github/ISSUE_TEMPLATE/Issue-report.yml
vendored
Normal file
@ -0,0 +1,130 @@
|
||||
name: Issue report
|
||||
description: Report any problem here
|
||||
labels: ["Status: Awaiting triage"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
* Before reporting a new issue please check and search in [List of existing issues](https://github.com/espressif/arduino-esp32/issues?q=is%3Aissue)
|
||||
* Please check [Online Documentation](https://docs.espressif.com/projects/arduino-esp32/en/latest/index.html)
|
||||
* Take a look on [Troubleshooting guide](https://docs.espressif.com/projects/arduino-esp32/en/latest/troubleshooting.html)
|
||||
* If still experiencing the issue, please provide as many details as possible below about your hardware, computer setup and code.
|
||||
- type: input
|
||||
id: Board
|
||||
attributes:
|
||||
label: Board
|
||||
description: On which Board does this issue occur?
|
||||
placeholder: eg. ESP32 Dev Module, ESP32-S2, LilyGo TTGO LoRa32...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: devboard
|
||||
attributes:
|
||||
label: Device Description
|
||||
description: What development board or other hardware is the chip attached to?
|
||||
placeholder: ex. DevKitC, plain module on breadboard, etc. If your hardware is custom or unusual, please attach a photo.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: other-hw
|
||||
attributes:
|
||||
label: Hardware Configuration
|
||||
description: Is anything else attached to the development board?
|
||||
placeholder: ex. GPIO 18 & 19 are connected to I2C devices.
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: version
|
||||
attributes:
|
||||
label: Version
|
||||
description: What version of Arduino ESP32 are you running? If possible, consider updating to the latest version.
|
||||
options:
|
||||
- latest master
|
||||
- v2.0.1
|
||||
- v2.0.0
|
||||
- v1.0.6
|
||||
- other
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: IDE
|
||||
attributes:
|
||||
label: IDE Name
|
||||
description: What IDE are you using?
|
||||
placeholder: eg. Arduino IDE, PlatformIO, IDF component...
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: os
|
||||
attributes:
|
||||
label: Operating System
|
||||
description: On which OS does this issue occur?
|
||||
placeholder: ex. macOS 12.1, Windows 10...
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: Flash
|
||||
attributes:
|
||||
label: Flash frequency
|
||||
description: What flash frequency is used?
|
||||
placeholder: eg. 40Mhz
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: PSRAM
|
||||
attributes:
|
||||
label: PSRAM enabled
|
||||
description: Is PSRAM enabled?
|
||||
options:
|
||||
- 'yes'
|
||||
- 'no'
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: Upload
|
||||
attributes:
|
||||
label: Upload speed
|
||||
description: What upload speed is used?
|
||||
placeholder: eg. 115200
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: Description
|
||||
attributes:
|
||||
label: Description
|
||||
description: Please describe your problem here and expected behaviour
|
||||
placeholder: ex. Can't connect/weird behaviour/wrong function/missing parameter..
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: sketch
|
||||
attributes:
|
||||
label: Sketch
|
||||
description: Please provide your sketch/code which was run
|
||||
placeholder: ex. related part of the code
|
||||
render: cpp
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: Debug
|
||||
attributes:
|
||||
label: Debug Message
|
||||
description: Please provide a debug message or error message. If you have a Guru Meditation Error or Backtrace, please decode it with [ExceptionDecoder](https://github.com/me-no-dev/EspExceptionDecoder)
|
||||
placeholder: Enable Core debug level - Debug on tools menu of Arduino IDE, then put the serial output here.
|
||||
render: plain
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: other-remarks
|
||||
attributes:
|
||||
label: Other Steps to Reproduce
|
||||
description: Is there any other information you can think of which will help us reproduce this problem? Any additional info can be added as well.
|
||||
placeholder: ex. I also tried on other OS, HW...it works correctly on that setup.
|
||||
- type: checkboxes
|
||||
id: confirmation
|
||||
attributes:
|
||||
label: I have checked existing issues, online documentation and the Troubleshooting Guide
|
||||
description: You agree to check all the resources above before opening a new issue.
|
||||
options:
|
||||
- label: I confirm I have checked existing issues, online documentation and Troubleshooting guide.
|
||||
required: true
|
54
.github/ISSUE_TEMPLATE/bug_report.md
vendored
54
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,54 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Please fill in the bug report carefully
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Make your question, not a Statement, inclusive. Include all pertinent information:
|
||||
|
||||
What you are trying to do?
|
||||
Describe your system( Hardware, computer, O/S, core version, environment).
|
||||
Describe what is failing.
|
||||
Show the shortest possible code that will duplicate the error.
|
||||
Show the EXACT error message(it doesn't work is not enough).
|
||||
All of this work on your part shows us that you have worked to solve YOUR problem. The more complete your issue posting is, the more likely someone will volunteer their time to help you.
|
||||
|
||||
If you have a Guru Meditation Error or Backtrace, ***please decode it***:
|
||||
https://github.com/me-no-dev/EspExceptionDecoder
|
||||
|
||||
----------------------------- Remove above -----------------------------
|
||||
|
||||
|
||||
### Hardware:
|
||||
Board: ?ESP32 Dev Module? ?node32? ?ttgo_lora?
|
||||
Core Installation version: ?1.0.0? ?1.0.1-rc4? ?1.0.1? ?1.0.1-git? ?1.0.2? ?1.0.3?
|
||||
IDE name: ?Arduino IDE? ?Platform.io? ?IDF component?
|
||||
Flash Frequency: ?40Mhz?
|
||||
PSRAM enabled: ?no? ?yes?
|
||||
Upload Speed: ?115200?
|
||||
Computer OS: ?Windows 10? ?Mac OSX? ?Ubuntu?
|
||||
|
||||
### Description:
|
||||
Describe your problem here
|
||||
|
||||
|
||||
### Sketch: (leave the backquotes for [code formatting](https://help.github.com/articles/creating-and-highlighting-code-blocks/))
|
||||
```cpp
|
||||
|
||||
//Change the code below by your sketch
|
||||
#include <Arduino.h>
|
||||
|
||||
void setup() {
|
||||
}
|
||||
|
||||
void loop() {
|
||||
}
|
||||
```
|
||||
|
||||
### Debug Messages:
|
||||
```
|
||||
Enable Core debug level: Debug on tools menu of Arduino IDE, then put the serial output here
|
||||
```
|
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Arduino ESP32 Gitter Channel
|
||||
url: https://gitter.im/espressif/arduino-esp32
|
||||
about: Community channel for questions and help
|
||||
- name: ESP32 Forum - Arduino
|
||||
url: https://esp32.com/viewforum.php?f=19
|
||||
about: Official Forum for questions
|
18
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
18
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
*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..)
|
||||
3. [ ] Please check [Contributing guide](https://docs.espressif.com/projects/arduino-esp32/en/latest/contributing.html)
|
||||
|
||||
*This entire section above can be deleted if all items are checked.*
|
||||
|
||||
-----------
|
||||
## Summary
|
||||
Please describe your proposed PR and what it contains.
|
||||
|
||||
## Impact
|
||||
Please describe impact of your PR and it's function.
|
||||
|
||||
## Related links
|
||||
Please provide links to related issue, PRs etc.
|
4
.github/scripts/check-cmakelists.sh
vendored
4
.github/scripts/check-cmakelists.sh
vendored
@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# This script is for Travis. It checks all non-examples source files in libraries/ and cores/ are listed in
|
||||
# This script is used in the CI workflow. It checks all non-examples source files in libraries/ and cores/ are listed in
|
||||
# CMakeLists.txt for the cmake-based IDF component
|
||||
#
|
||||
# If you see an error running this script, edit CMakeLists.txt and add any new source files into your PR
|
||||
@ -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"
|
||||
|
0
.github/scripts/on-pages.sh
vendored
Normal file → Executable file
0
.github/scripts/on-pages.sh
vendored
Normal file → Executable file
13
.github/scripts/on-push.sh
vendored
13
.github/scripts/on-push.sh
vendored
@ -2,18 +2,7 @@
|
||||
|
||||
set -e
|
||||
|
||||
if [ ! -z "$TRAVIS_TAG" ]; then
|
||||
echo "Skipping Test: Tagged build"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ ! -z "$GITHUB_WORKSPACE" ]; then
|
||||
export TRAVIS_BUILD_DIR="$GITHUB_WORKSPACE"
|
||||
export TRAVIS_REPO_SLUG="$GITHUB_REPOSITORY"
|
||||
elif [ ! -z "$TRAVIS_BUILD_DIR" ]; then
|
||||
export GITHUB_WORKSPACE="$TRAVIS_BUILD_DIR"
|
||||
export GITHUB_REPOSITORY="$TRAVIS_REPO_SLUG"
|
||||
else
|
||||
if [ -z "$GITHUB_WORKSPACE" ]; then
|
||||
export GITHUB_WORKSPACE="$PWD"
|
||||
export GITHUB_REPOSITORY="espressif/arduino-esp32"
|
||||
fi
|
||||
|
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
|
2
.github/workflows/gh-pages.yml
vendored
2
.github/workflows/gh-pages.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
||||
name: Build GitHub Pages
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
- name: Copy Files
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
12
.github/workflows/push.yml
vendored
12
.github/workflows/push.yml
vendored
@ -18,8 +18,8 @@ jobs:
|
||||
chunk: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/setup-python@v1
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Build Sketches
|
||||
@ -34,8 +34,8 @@ jobs:
|
||||
os: [windows-latest, macOS-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/setup-python@v1
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Build Sketches
|
||||
@ -50,8 +50,8 @@ jobs:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/setup-python@v1
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Build Sketches
|
||||
|
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@ -10,10 +10,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-python@v1
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Build Release
|
||||
|
28
.github/workflows/test_selfhosted_runner.yml
vendored
Normal file
28
.github/workflows/test_selfhosted_runner.yml
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
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
|
||||
if: github.repository == 'espressif/arduino-esp32'
|
||||
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
|
||||
|
55
.travis.yml
55
.travis.yml
@ -1,55 +0,0 @@
|
||||
sudo: false
|
||||
|
||||
language: python
|
||||
|
||||
os:
|
||||
- linux
|
||||
|
||||
git:
|
||||
depth: false
|
||||
|
||||
before_install:
|
||||
- git submodule update --init --recursive
|
||||
|
||||
stages:
|
||||
- build
|
||||
- deploy
|
||||
|
||||
jobs:
|
||||
include:
|
||||
|
||||
- name: "Build Arduino 0"
|
||||
if: tag IS blank AND (type = pull_request OR (type = push AND branch = master))
|
||||
stage: build
|
||||
script: $TRAVIS_BUILD_DIR/.github/scripts/on-push.sh 0 10
|
||||
|
||||
- name: "Build Arduino 1"
|
||||
if: tag IS blank AND (type = pull_request OR (type = push AND branch = master))
|
||||
stage: build
|
||||
script: $TRAVIS_BUILD_DIR/.github/scripts/on-push.sh 1 10
|
||||
|
||||
- name: "Build Arduino 2"
|
||||
if: tag IS blank AND (type = pull_request OR (type = push AND branch = master))
|
||||
stage: build
|
||||
script: $TRAVIS_BUILD_DIR/.github/scripts/on-push.sh 2 10
|
||||
|
||||
- name: "Build Arduino 3"
|
||||
if: tag IS blank AND (type = pull_request OR (type = push AND branch = master))
|
||||
stage: build
|
||||
script: $TRAVIS_BUILD_DIR/.github/scripts/on-push.sh 3 10
|
||||
|
||||
- name: "Build PlatformIO"
|
||||
if: tag IS blank AND (type = pull_request OR (type = push AND branch = master))
|
||||
stage: build
|
||||
script: $TRAVIS_BUILD_DIR/.github/scripts/on-push.sh 1 1
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: change
|
||||
on_failure: change
|
||||
webhooks:
|
||||
urls:
|
||||
- https://webhooks.gitter.im/e/cb057279c430d91a47a8
|
||||
on_success: change # options: [always|never|change] default: always
|
||||
on_failure: always # options: [always|never|change] default: always
|
||||
on_start: never # options: [always|never|change] default: always
|
@ -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})
|
||||
|
||||
|
25
README.md
25
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
|
||||
|
||||
@ -38,14 +27,18 @@ You can use [Arduino-ESP32 Online Documentation](https://docs.espressif.com/proj
|
||||
* [FAQ](https://docs.espressif.com/projects/arduino-esp32/en/latest/faq.html)
|
||||
* [Troubleshooting](https://docs.espressif.com/projects/arduino-esp32/en/latest/troubleshooting.html)
|
||||
|
||||
### Supported Chips
|
||||
|
||||
Visit the [supported chips](https://docs.espressif.com/projects/arduino-esp32/en/latest/getting_started.html#supported-soc-s) documentation to see the list of current supported ESP32 SoCs.
|
||||
|
||||
### Decoding exceptions
|
||||
|
||||
You can use [EspExceptionDecoder](https://github.com/me-no-dev/EspExceptionDecoder) to get meaningful call trace.
|
||||
|
||||
### Issue/Bug report template
|
||||
Before reporting an issue, make sure you've searched for similar one that was already created. Also make sure to go through all the issues labelled as [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.
|
||||
Finally, if you are sure no one else had the issue, follow the **Issue template** or **Feature request template** while reporting any [new Issue](https://github.com/espressif/arduino-esp32/issues/new/choose).
|
||||
|
||||
### Contributing
|
||||
|
||||
|
2418
boards.txt
2418
boards.txt
File diff suppressed because it is too large
Load Diff
@ -179,6 +179,9 @@ uint16_t makeWord(uint8_t h, uint8_t l);
|
||||
|
||||
#define word(...) makeWord(__VA_ARGS__)
|
||||
|
||||
size_t getArduinoLoopTaskStackSize(void);
|
||||
#define SET_LOOP_TASK_STACK_SIZE(sz) size_t getArduinoLoopTaskStackSize() { return sz;}
|
||||
|
||||
unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L);
|
||||
unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L);
|
||||
|
||||
|
@ -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
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include <Arduino.h>
|
||||
#include <MD5Builder.h>
|
||||
|
||||
uint8_t hex_char_to_byte(uint8_t c)
|
||||
static uint8_t hex_char_to_byte(uint8_t c)
|
||||
{
|
||||
return (c >= 'a' && c <= 'f') ? (c - ((uint8_t)'a' - 0xa)) :
|
||||
(c >= 'A' && c <= 'F') ? (c - ((uint8_t)'A' - 0xA)) :
|
||||
@ -28,13 +28,13 @@ uint8_t hex_char_to_byte(uint8_t c)
|
||||
|
||||
void MD5Builder::begin(void)
|
||||
{
|
||||
memset(_buf, 0x00, 16);
|
||||
MD5Init(&_ctx);
|
||||
memset(_buf, 0x00, ESP_ROM_MD5_DIGEST_LEN);
|
||||
esp_rom_md5_init(&_ctx);
|
||||
}
|
||||
|
||||
void MD5Builder::add(uint8_t * data, uint16_t len)
|
||||
{
|
||||
MD5Update(&_ctx, data, len);
|
||||
esp_rom_md5_update(&_ctx, data, len);
|
||||
}
|
||||
|
||||
void MD5Builder::addHexString(const char * data)
|
||||
@ -82,7 +82,7 @@ bool MD5Builder::addStream(Stream & stream, const size_t maxLen)
|
||||
}
|
||||
|
||||
// Update MD5 with buffer payload
|
||||
MD5Update(&_ctx, buf, numBytesRead);
|
||||
esp_rom_md5_update(&_ctx, buf, numBytesRead);
|
||||
|
||||
// update available number of bytes
|
||||
maxLengthLeft -= numBytesRead;
|
||||
@ -94,24 +94,24 @@ bool MD5Builder::addStream(Stream & stream, const size_t maxLen)
|
||||
|
||||
void MD5Builder::calculate(void)
|
||||
{
|
||||
MD5Final(_buf, &_ctx);
|
||||
esp_rom_md5_final(_buf, &_ctx);
|
||||
}
|
||||
|
||||
void MD5Builder::getBytes(uint8_t * output)
|
||||
{
|
||||
memcpy(output, _buf, 16);
|
||||
memcpy(output, _buf, ESP_ROM_MD5_DIGEST_LEN);
|
||||
}
|
||||
|
||||
void MD5Builder::getChars(char * output)
|
||||
{
|
||||
for(uint8_t i = 0; i < 16; i++) {
|
||||
for(uint8_t i = 0; i < ESP_ROM_MD5_DIGEST_LEN; i++) {
|
||||
sprintf(output + (i * 2), "%02x", _buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
String MD5Builder::toString(void)
|
||||
{
|
||||
char out[33];
|
||||
char out[(ESP_ROM_MD5_DIGEST_LEN * 2) + 1];
|
||||
getChars(out);
|
||||
return String(out);
|
||||
}
|
||||
|
@ -23,25 +23,13 @@
|
||||
#include <Stream.h>
|
||||
|
||||
#include "esp_system.h"
|
||||
#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+
|
||||
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||
#include "esp32/rom/md5_hash.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "esp32s2/rom/md5_hash.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#include "esp32c3/rom/md5_hash.h"
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
#else // ESP32 Before IDF 4.0
|
||||
#include "rom/md5_hash.h"
|
||||
#endif
|
||||
#include "esp_rom_md5.h"
|
||||
|
||||
class MD5Builder
|
||||
{
|
||||
private:
|
||||
struct MD5Context _ctx;
|
||||
uint8_t _buf[16];
|
||||
md5_context_t _ctx;
|
||||
uint8_t _buf[ESP_ROM_MD5_DIGEST_LEN];
|
||||
public:
|
||||
void begin(void);
|
||||
void add(uint8_t * data, uint16_t len);
|
||||
|
@ -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);
|
||||
|
@ -62,7 +62,7 @@ public:
|
||||
|
||||
cbuf *next;
|
||||
|
||||
private:
|
||||
protected:
|
||||
inline char* wrap_if_bufend(char* ptr) const
|
||||
{
|
||||
return (ptr == _bufend) ? _buf : ptr;
|
||||
|
@ -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){
|
||||
|
@ -13,51 +13,37 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include "esp32-hal.h"
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#include "soc/rtc_io_reg.h"
|
||||
#define DAC1 25
|
||||
#define DAC2 26
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "soc/rtc_io_reg.h"
|
||||
#define DAC1 17
|
||||
#define DAC2 18
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef SOC_DAC_SUPPORTED
|
||||
#define NODAC
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
|
||||
#ifndef NODAC
|
||||
#include "esp_attr.h"
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
#include "soc/rtc_io_periph.h"
|
||||
#include "soc/sens_reg.h"
|
||||
#include "soc/sens_struct.h"
|
||||
#include "driver/dac.h"
|
||||
#include "soc/dac_channel.h"
|
||||
#include "driver/dac_common.h"
|
||||
|
||||
void ARDUINO_ISR_ATTR __dacWrite(uint8_t pin, uint8_t value)
|
||||
{
|
||||
if(pin < DAC1 || pin > DAC2){
|
||||
if(pin < DAC_CHANNEL_1_GPIO_NUM || pin > DAC_CHANNEL_2_GPIO_NUM){
|
||||
return;//not dac pin
|
||||
}
|
||||
pinMode(pin, ANALOG);
|
||||
uint8_t channel = pin - DAC1;
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
CLEAR_PERI_REG_MASK(SENS_SAR_DAC_CTRL1_REG, SENS_SW_TONE_EN);
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
SENS.sar_dac_ctrl1.dac_clkgate_en = 1;
|
||||
#endif
|
||||
RTCIO.pad_dac[channel].dac_xpd_force = 1;
|
||||
RTCIO.pad_dac[channel].xpd_dac = 1;
|
||||
if (channel == 0) {
|
||||
SENS.sar_dac_ctrl2.dac_cw_en1 = 0;
|
||||
} else if (channel == 1) {
|
||||
SENS.sar_dac_ctrl2.dac_cw_en2 = 0;
|
||||
|
||||
uint8_t channel = pin - DAC_CHANNEL_1_GPIO_NUM;
|
||||
dac_output_enable(channel);
|
||||
dac_output_voltage(channel, value);
|
||||
|
||||
}
|
||||
|
||||
void ARDUINO_ISR_ATTR __dacDisable(uint8_t pin)
|
||||
{
|
||||
if(pin < DAC_CHANNEL_1_GPIO_NUM || pin > DAC_CHANNEL_2_GPIO_NUM){
|
||||
return;//not dac pin
|
||||
}
|
||||
RTCIO.pad_dac[channel].dac = value;
|
||||
|
||||
uint8_t channel = pin - DAC_CHANNEL_1_GPIO_NUM;
|
||||
dac_output_disable(channel);
|
||||
}
|
||||
|
||||
extern void dacWrite(uint8_t pin, uint8_t value) __attribute__ ((weak, alias("__dacWrite")));
|
||||
extern void dacDisable(uint8_t pin) __attribute__ ((weak, alias("__dacDisable")));
|
||||
|
||||
#endif
|
||||
|
@ -28,6 +28,7 @@ extern "C" {
|
||||
#include "driver/gpio.h"
|
||||
|
||||
void dacWrite(uint8_t pin, uint8_t value);
|
||||
void dacDisable(uint8_t pin);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -37,7 +37,13 @@
|
||||
#include "soc/periph_defs.h"
|
||||
#include "soc/rtc_io_reg.h"
|
||||
#define GPIO_FUNC 1
|
||||
#else
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#include "esp32c3/rom/ets_sys.h"
|
||||
#include "esp32c3/rom/gpio.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "soc/periph_defs.h"
|
||||
#define USE_ESP_IDF_GPIO 1
|
||||
#else
|
||||
#define USE_ESP_IDF_GPIO 1
|
||||
#endif
|
||||
#else // ESP32 Before IDF 4.0
|
||||
@ -143,6 +149,29 @@ const DRAM_ATTR esp32_gpioMux_t esp32_gpioMux[SOC_GPIO_PIN_COUNT]={
|
||||
{0xb8, -1, -1, -1},//SCL?
|
||||
{0xbc, -1, -1, -1},//INPUT ONLY
|
||||
{0, -1, -1, -1}
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
{0x04, -1, 0, -1}, // ADC1_CH0
|
||||
{0x08, -1, 1, -1}, // ADC1_CH1
|
||||
{0x0c, -1, 2, -1}, // ADC1_CH2 | FSPIQ
|
||||
{0x10, -1, 3, -1}, // ADC1_CH3
|
||||
{0x14, -1, 4, -1}, // MTMS | ADC1_CH4 | FSPIHD
|
||||
{0x18, -1, 5, -1}, // MTDI | ADC2_CH0 | FSPIWP
|
||||
{0x1c, -1, -1, -1}, // MTCK | FSPICLK
|
||||
{0x20, -1, -1, -1}, // MTDO | FSPID
|
||||
{0x24, -1, -1, -1}, //
|
||||
{0x28, -1, -1, -1}, //
|
||||
{0x2c, -1, -1, -1}, // FSPICSO
|
||||
{0x30, -1, -1, -1}, //
|
||||
{0x34, -1, -1, -1}, // SPIHD
|
||||
{0x38, -1, -1, -1}, // SPIWP
|
||||
{0x3c, -1, -1, -1}, // SPICSO
|
||||
{0x40, -1, -1, -1}, // SPICLK
|
||||
{0x44, -1, -1, -1}, // SPID
|
||||
{0x48, -1, -1, -1}, // SPIQ
|
||||
{0x4c, -1, -1, -1}, // USB-
|
||||
{0x50, -1, -1, -1}, // USB+
|
||||
{0x54, -1, -1, -1}, // U0RXD
|
||||
{0x58, -1, -1, -1}, // U0TXD
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -481,4 +510,3 @@ extern int digitalRead(uint8_t pin) __attribute__ ((weak, alias("__digitalRead")
|
||||
extern void attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode) __attribute__ ((weak, alias("__attachInterrupt")));
|
||||
extern void attachInterruptArg(uint8_t pin, voidFuncPtrArg handler, void * arg, int mode) __attribute__ ((weak, alias("__attachInterruptArg")));
|
||||
extern void detachInterrupt(uint8_t pin) __attribute__ ((weak, alias("__detachInterrupt")));
|
||||
|
||||
|
848
cores/esp32/esp32-hal-i2c-slave.c
Normal file
848
cores/esp32/esp32-hal-i2c-slave.c
Normal 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, I2C_LL_MAX_TIMEOUT);
|
||||
i2c_slave_set_frequency(i2c, frequency);
|
||||
|
||||
if (!i2c_slave_check_line_state(sda, scl)) {
|
||||
log_e("bad pin state");
|
||||
ret = ESP_FAIL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
i2c_slave_attach_gpio(i2c, sda, scl);
|
||||
|
||||
if (i2c_ll_is_bus_busy(i2c->dev)) {
|
||||
log_w("Bus busy, reinit");
|
||||
ret = ESP_FAIL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
i2c_ll_disable_intr_mask(i2c->dev, I2C_LL_INTR_MASK);
|
||||
i2c_ll_clr_intsts_mask(i2c->dev, I2C_LL_INTR_MASK);
|
||||
i2c_ll_set_fifo_mode(i2c->dev, true);
|
||||
|
||||
if (!i2c->intr_handle) {
|
||||
uint32_t flags = ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED;
|
||||
if(i2c->num == 0) {
|
||||
ret = esp_intr_alloc(ETS_I2C_EXT0_INTR_SOURCE, flags, &i2c_slave_isr_handler, i2c, &i2c->intr_handle);
|
||||
#if SOC_I2C_NUM > 1
|
||||
} else {
|
||||
ret = esp_intr_alloc(ETS_I2C_EXT1_INTR_SOURCE, flags, &i2c_slave_isr_handler, i2c, &i2c->intr_handle);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
log_e("install interrupt handler Failed=%d", ret);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_ll_txfifo_rst(i2c->dev);
|
||||
i2c_ll_rxfifo_rst(i2c->dev);
|
||||
i2c_ll_slave_enable_rx_it(i2c->dev);
|
||||
i2c_ll_set_stretch(i2c->dev, 0x3FF);
|
||||
i2c_ll_update(i2c->dev);
|
||||
I2C_SLAVE_MUTEX_UNLOCK();
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
i2c_slave_free_resources(i2c);
|
||||
I2C_SLAVE_MUTEX_UNLOCK();
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2cSlaveDeinit(uint8_t num){
|
||||
if(num >= SOC_I2C_NUM){
|
||||
log_e("Invalid port num: %u", num);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
i2c_slave_struct_t * i2c = &_i2c_bus_array[num];
|
||||
if(!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
Normal file
35
cores/esp32/esp32-hal-i2c-slave.h
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "stdint.h"
|
||||
#include "stddef.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
typedef void (*i2c_slave_request_cb_t) (uint8_t num, void * arg);
|
||||
typedef void (*i2c_slave_receive_cb_t) (uint8_t num, uint8_t * data, size_t len, bool stop, void * arg);
|
||||
esp_err_t i2cSlaveAttachCallbacks(uint8_t num, i2c_slave_request_cb_t request_callback, i2c_slave_receive_cb_t receive_callback, void * arg);
|
||||
|
||||
esp_err_t i2cSlaveInit(uint8_t num, int sda, int scl, uint16_t slaveID, uint32_t frequency, size_t rx_len, size_t tx_len);
|
||||
esp_err_t i2cSlaveDeinit(uint8_t num);
|
||||
size_t i2cSlaveWrite(uint8_t num, const uint8_t *buf, uint32_t len, uint32_t timeout_ms);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
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
|
||||
}
|
||||
|
@ -13,45 +13,25 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include "esp32-hal.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp32-hal-matrix.h"
|
||||
#include "soc/ledc_reg.h"
|
||||
#include "soc/ledc_struct.h"
|
||||
#include "driver/periph_ctrl.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "driver/ledc.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"
|
||||
#define LAST_CHAN (15)
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "soc/dport_reg.h"
|
||||
#include "esp32s2/rom/ets_sys.h"
|
||||
#define LAST_CHAN (7)
|
||||
#define LEDC_DIV_NUM_HSTIMER0_V LEDC_CLK_DIV_LSTIMER0_V
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#include "esp32c3/rom/ets_sys.h"
|
||||
#define LAST_CHAN (7)
|
||||
#define LEDC_DIV_NUM_HSTIMER0_V LEDC_CLK_DIV_LSTIMER0_V
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
#else // ESP32 Before IDF 4.0
|
||||
#include "rom/ets_sys.h"
|
||||
#endif
|
||||
|
||||
#if CONFIG_DISABLE_HAL_LOCKS
|
||||
#define LEDC_MUTEX_LOCK()
|
||||
#define LEDC_MUTEX_UNLOCK()
|
||||
#ifdef SOC_LEDC_SUPPORT_HS_MODE
|
||||
#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM<<1)
|
||||
#else
|
||||
#define LEDC_MUTEX_LOCK() do {} while (xSemaphoreTake(_ledc_sys_lock, portMAX_DELAY) != pdPASS)
|
||||
#define LEDC_MUTEX_UNLOCK() xSemaphoreGive(_ledc_sys_lock)
|
||||
xSemaphoreHandle _ledc_sys_lock = NULL;
|
||||
#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM)
|
||||
#endif
|
||||
|
||||
//Use XTAL clock if possible to avoid timer frequency error when setting APB clock < 80 Mhz
|
||||
//Need to be fixed in ESP-IDF
|
||||
#ifdef SOC_LEDC_SUPPORT_XTAL_CLOCK
|
||||
#define LEDC_DEFAULT_CLK LEDC_USE_XTAL_CLK
|
||||
#else
|
||||
#define LEDC_DEFAULT_CLK LEDC_AUTO_CLK
|
||||
#endif
|
||||
|
||||
#define LEDC_MAX_BIT_WIDTH SOC_LEDC_TIMER_BIT_WIDE_NUM
|
||||
|
||||
/*
|
||||
* LEDC Chan to Group/Channel/Timer Mapping
|
||||
** ledc: 0 => Group: 0, Channel: 0, Timer: 0
|
||||
@ -71,199 +51,55 @@ xSemaphoreHandle _ledc_sys_lock = NULL;
|
||||
** ledc: 14 => Group: 1, Channel: 6, Timer: 3
|
||||
** ledc: 15 => Group: 1, Channel: 7, Timer: 3
|
||||
*/
|
||||
#define LEDC_CHAN(g,c) LEDC.channel_group[(g)].channel[(c)]
|
||||
#define LEDC_TIMER(g,t) LEDC.timer_group[(g)].timer[(t)]
|
||||
|
||||
static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){
|
||||
if(ev_type == APB_AFTER_CHANGE && old_apb != new_apb){
|
||||
uint16_t iarg = *(uint16_t*)arg;
|
||||
uint8_t chan = 0;
|
||||
old_apb /= 1000000;
|
||||
new_apb /= 1000000;
|
||||
while(iarg){ // run though all active channels, adjusting timing configurations
|
||||
if(iarg & 1) {// this channel is active
|
||||
uint8_t group=(chan/8), timer=((chan/2)%4);
|
||||
if(LEDC_TIMER(group, timer).conf.tick_sel){
|
||||
LEDC_MUTEX_LOCK();
|
||||
uint32_t old_div = LEDC_TIMER(group, timer).conf.clock_divider;
|
||||
uint32_t div_num = (new_apb * old_div) / old_apb;
|
||||
if(div_num > LEDC_DIV_NUM_HSTIMER0_V){
|
||||
div_num = ((REF_CLK_FREQ /1000000) * old_div) / old_apb;
|
||||
if(div_num > LEDC_DIV_NUM_HSTIMER0_V) {
|
||||
div_num = LEDC_DIV_NUM_HSTIMER0_V;//lowest clock possible
|
||||
}
|
||||
LEDC_TIMER(group, timer).conf.tick_sel = 0;
|
||||
} else if(div_num < 256) {
|
||||
div_num = 256;//highest clock possible
|
||||
}
|
||||
LEDC_TIMER(group, timer).conf.clock_divider = div_num;
|
||||
LEDC_MUTEX_UNLOCK();
|
||||
}
|
||||
else {
|
||||
log_d("using REF_CLK chan=%d",chan);
|
||||
}
|
||||
}
|
||||
iarg = iarg >> 1;
|
||||
chan++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//uint32_t frequency = (80MHz or 1MHz)/((div_num / 256.0)*(1 << bit_num));
|
||||
static void _ledcSetupTimer(uint8_t chan, uint32_t div_num, uint8_t bit_num, bool apb_clk)
|
||||
{
|
||||
uint8_t group=(chan/8), timer=((chan/2)%4);
|
||||
static bool tHasStarted = false;
|
||||
static uint16_t _activeChannels = 0;
|
||||
if(!tHasStarted) {
|
||||
tHasStarted = true;
|
||||
periph_module_enable(PERIPH_LEDC_MODULE);
|
||||
LEDC.conf.apb_clk_sel = 1;//LS use apb clock
|
||||
addApbChangeCallback((void*)&_activeChannels, _on_apb_change);
|
||||
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
_ledc_sys_lock = xSemaphoreCreateMutex();
|
||||
#endif
|
||||
}
|
||||
LEDC_MUTEX_LOCK();
|
||||
LEDC_TIMER(group, timer).conf.clock_divider = div_num;//18 bit (10.8) This register is used to configure parameter for divider in timer the least significant eight bits represent the decimal part.
|
||||
LEDC_TIMER(group, timer).conf.duty_resolution = bit_num;//5 bit This register controls the range of the counter in timer. the counter range is [0 2**bit_num] the max bit width for counter is 20.
|
||||
LEDC_TIMER(group, timer).conf.tick_sel = apb_clk;//apb clock
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
if(group) {
|
||||
#endif
|
||||
LEDC_TIMER(group, timer).conf.low_speed_update = 1;//This bit is only useful for low speed timer channels, reserved for high speed timers
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
}
|
||||
#endif
|
||||
LEDC_TIMER(group, timer).conf.pause = 0;
|
||||
LEDC_TIMER(group, timer).conf.rst = 1;//This bit is used to reset timer the counter will be 0 after reset.
|
||||
LEDC_TIMER(group, timer).conf.rst = 0;
|
||||
LEDC_MUTEX_UNLOCK();
|
||||
_activeChannels |= (1 << chan); // mark as active for APB callback
|
||||
}
|
||||
|
||||
//max div_num 0x3FFFF (262143)
|
||||
//max bit_num 0x1F (31)
|
||||
static double _ledcSetupTimerFreq(uint8_t chan, double freq, uint8_t bit_num)
|
||||
{
|
||||
uint64_t clk_freq = getApbFrequency();
|
||||
clk_freq <<= 8;//div_num is 8 bit decimal
|
||||
uint32_t div_num = (clk_freq >> bit_num) / freq;
|
||||
bool apb_clk = true;
|
||||
if(div_num > LEDC_DIV_NUM_HSTIMER0_V) {
|
||||
clk_freq /= 80;
|
||||
div_num = (clk_freq >> bit_num) / freq;
|
||||
if(div_num > LEDC_DIV_NUM_HSTIMER0_V) {
|
||||
div_num = LEDC_DIV_NUM_HSTIMER0_V;//lowest clock possible
|
||||
}
|
||||
apb_clk = false;
|
||||
} else if(div_num < 256) {
|
||||
div_num = 256;//highest clock possible
|
||||
}
|
||||
_ledcSetupTimer(chan, div_num, bit_num, apb_clk);
|
||||
//log_i("Fin: %f, Fclk: %uMhz, bits: %u, DIV: %u, Fout: %f",
|
||||
// freq, apb_clk?80:1, bit_num, div_num, (clk_freq >> bit_num) / (double)div_num);
|
||||
return (clk_freq >> bit_num) / (double)div_num;
|
||||
}
|
||||
|
||||
static double _ledcTimerRead(uint8_t chan)
|
||||
{
|
||||
uint32_t div_num;
|
||||
uint8_t bit_num;
|
||||
bool apb_clk;
|
||||
uint8_t group=(chan/8), timer=((chan/2)%4);
|
||||
LEDC_MUTEX_LOCK();
|
||||
div_num = LEDC_TIMER(group, timer).conf.clock_divider;//18 bit (10.8) This register is used to configure parameter for divider in timer the least significant eight bits represent the decimal part.
|
||||
bit_num = LEDC_TIMER(group, timer).conf.duty_resolution;//5 bit This register controls the range of the counter in timer. the counter range is [0 2**bit_num] the max bit width for counter is 20.
|
||||
apb_clk = LEDC_TIMER(group, timer).conf.tick_sel;//apb clock
|
||||
LEDC_MUTEX_UNLOCK();
|
||||
uint64_t clk_freq = 1000000;
|
||||
if(apb_clk) {
|
||||
clk_freq = getApbFrequency();
|
||||
}
|
||||
clk_freq <<= 8;//div_num is 8 bit decimal
|
||||
return (clk_freq >> bit_num) / (double)div_num;
|
||||
}
|
||||
|
||||
static void _ledcSetupChannel(uint8_t chan, uint8_t idle_level)
|
||||
{
|
||||
uint8_t group=(chan/8), channel=(chan%8), timer=((chan/2)%4);
|
||||
LEDC_MUTEX_LOCK();
|
||||
LEDC_CHAN(group, channel).conf0.timer_sel = timer;//2 bit Selects the timer to attach 0-3
|
||||
LEDC_CHAN(group, channel).conf0.idle_lv = idle_level;//1 bit This bit is used to control the output value when channel is off.
|
||||
LEDC_CHAN(group, channel).hpoint.hpoint = 0;//20 bit The output value changes to high when timer selected by channel has reached hpoint
|
||||
LEDC_CHAN(group, channel).conf1.duty_inc = 1;//1 bit This register is used to increase the duty of output signal or decrease the duty of output signal for high speed channel
|
||||
LEDC_CHAN(group, channel).conf1.duty_num = 1;//10 bit This register is used to control the number of increased or decreased times for channel
|
||||
LEDC_CHAN(group, channel).conf1.duty_cycle = 1;//10 bit This register is used to increase or decrease the duty every duty_cycle cycles for channel
|
||||
LEDC_CHAN(group, channel).conf1.duty_scale = 0;//10 bit This register controls the increase or decrease step scale for channel.
|
||||
LEDC_CHAN(group, channel).duty.duty = 0;
|
||||
LEDC_CHAN(group, channel).conf0.sig_out_en = 0;//This is the output enable control bit for channel
|
||||
LEDC_CHAN(group, channel).conf1.duty_start = 0;//When duty_num duty_cycle and duty_scale has been configured. these register won't take effect until set duty_start. this bit is automatically cleared by hardware.
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
if(group) {
|
||||
#endif
|
||||
LEDC_CHAN(group, channel).conf0.low_speed_update = 1;
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
} else {
|
||||
LEDC_CHAN(group, channel).conf0.clk_en = 0;
|
||||
}
|
||||
#endif
|
||||
LEDC_MUTEX_UNLOCK();
|
||||
}
|
||||
uint8_t channels_resolution[LEDC_CHANNELS] = {0};
|
||||
|
||||
double ledcSetup(uint8_t chan, double freq, uint8_t bit_num)
|
||||
{
|
||||
if(chan > LAST_CHAN) {
|
||||
if(chan >= LEDC_CHANNELS){
|
||||
log_e("No more LEDC channels available! You can have maximum %u", LEDC_CHANNELS);
|
||||
return 0;
|
||||
}
|
||||
double res_freq = _ledcSetupTimerFreq(chan, freq, bit_num);
|
||||
_ledcSetupChannel(chan, LOW);
|
||||
return res_freq;
|
||||
uint8_t group=(chan/8), timer=((chan/2)%4);
|
||||
|
||||
ledc_timer_config_t ledc_timer = {
|
||||
.speed_mode = group,
|
||||
.timer_num = timer,
|
||||
.duty_resolution = bit_num,
|
||||
.freq_hz = freq,
|
||||
.clk_cfg = LEDC_DEFAULT_CLK
|
||||
};
|
||||
ledc_timer_config(&ledc_timer);
|
||||
channels_resolution[chan] = bit_num;
|
||||
|
||||
return ledc_get_freq(group,timer);
|
||||
}
|
||||
|
||||
void ledcWrite(uint8_t chan, uint32_t duty)
|
||||
{
|
||||
if(chan > LAST_CHAN) {
|
||||
if(chan >= LEDC_CHANNELS){
|
||||
return;
|
||||
}
|
||||
uint8_t group=(chan/8), channel=(chan%8);
|
||||
LEDC_MUTEX_LOCK();
|
||||
LEDC_CHAN(group, channel).duty.duty = duty << 4;//25 bit (21.4)
|
||||
if(duty) {
|
||||
LEDC_CHAN(group, channel).conf0.sig_out_en = 1;//This is the output enable control bit for channel
|
||||
LEDC_CHAN(group, channel).conf1.duty_start = 1;//When duty_num duty_cycle and duty_scale has been configured. these register won't take effect until set duty_start. this bit is automatically cleared by hardware.
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
if(group) {
|
||||
#endif
|
||||
LEDC_CHAN(group, channel).conf0.low_speed_update = 1;
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
} else {
|
||||
LEDC_CHAN(group, channel).conf0.clk_en = 1;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
LEDC_CHAN(group, channel).conf0.sig_out_en = 0;//This is the output enable control bit for channel
|
||||
LEDC_CHAN(group, channel).conf1.duty_start = 0;//When duty_num duty_cycle and duty_scale has been configured. these register won't take effect until set duty_start. this bit is automatically cleared by hardware.
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
if(group) {
|
||||
#endif
|
||||
LEDC_CHAN(group, channel).conf0.low_speed_update = 1;
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
} else {
|
||||
LEDC_CHAN(group, channel).conf0.clk_en = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
//Fixing if all bits in resolution is set = LEDC FULL ON
|
||||
uint32_t max_duty = (1 << channels_resolution[chan]) - 1;
|
||||
|
||||
if(duty == max_duty){
|
||||
duty = max_duty + 1;
|
||||
}
|
||||
LEDC_MUTEX_UNLOCK();
|
||||
|
||||
ledc_set_duty(group, channel, duty);
|
||||
ledc_update_duty(group, channel);
|
||||
}
|
||||
|
||||
uint32_t ledcRead(uint8_t chan)
|
||||
{
|
||||
if(chan > LAST_CHAN) {
|
||||
if(chan >= LEDC_CHANNELS){
|
||||
return 0;
|
||||
}
|
||||
return LEDC.channel_group[chan/8].channel[chan%8].duty.duty >> 4;
|
||||
uint8_t group=(chan/8), channel=(chan%8);
|
||||
return ledc_get_duty(group,channel);
|
||||
}
|
||||
|
||||
double ledcReadFreq(uint8_t chan)
|
||||
@ -271,19 +107,33 @@ double ledcReadFreq(uint8_t chan)
|
||||
if(!ledcRead(chan)){
|
||||
return 0;
|
||||
}
|
||||
return _ledcTimerRead(chan);
|
||||
uint8_t group=(chan/8), timer=((chan/2)%4);
|
||||
return ledc_get_freq(group,timer);
|
||||
}
|
||||
|
||||
double ledcWriteTone(uint8_t chan, double freq)
|
||||
{
|
||||
if(chan > LAST_CHAN) {
|
||||
if(chan >= LEDC_CHANNELS){
|
||||
return 0;
|
||||
}
|
||||
if(!freq) {
|
||||
if(!freq){
|
||||
ledcWrite(chan, 0);
|
||||
return 0;
|
||||
}
|
||||
double res_freq = _ledcSetupTimerFreq(chan, freq, 10);
|
||||
|
||||
uint8_t group=(chan/8), timer=((chan/2)%4);
|
||||
|
||||
ledc_timer_config_t ledc_timer = {
|
||||
.speed_mode = group,
|
||||
.timer_num = timer,
|
||||
.duty_resolution = 10,
|
||||
.freq_hz = freq,
|
||||
.clk_cfg = LEDC_DEFAULT_CLK
|
||||
};
|
||||
ledc_timer_config(&ledc_timer);
|
||||
channels_resolution[chan] = 10;
|
||||
|
||||
double res_freq = ledc_get_freq(group,timer);
|
||||
ledcWrite(chan, 0x1FF);
|
||||
return res_freq;
|
||||
}
|
||||
@ -303,15 +153,21 @@ double ledcWriteNote(uint8_t chan, note_t note, uint8_t octave){
|
||||
|
||||
void ledcAttachPin(uint8_t pin, uint8_t chan)
|
||||
{
|
||||
if(chan > LAST_CHAN) {
|
||||
if(chan >= LEDC_CHANNELS){
|
||||
return;
|
||||
}
|
||||
pinMode(pin, OUTPUT);
|
||||
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32C3
|
||||
pinMatrixOutAttach(pin, LEDC_LS_SIG_OUT0_IDX + chan, false, false);
|
||||
#else
|
||||
pinMatrixOutAttach(pin, ((chan/8)?LEDC_LS_SIG_OUT0_IDX:LEDC_HS_SIG_OUT0_IDX) + (chan%8), false, false);
|
||||
#endif
|
||||
uint8_t group=(chan/8), channel=(chan%8), timer=((chan/2)%4);
|
||||
|
||||
ledc_channel_config_t ledc_channel = {
|
||||
.speed_mode = group,
|
||||
.channel = channel,
|
||||
.timer_sel = timer,
|
||||
.intr_type = LEDC_INTR_DISABLE,
|
||||
.gpio_num = pin,
|
||||
.duty = 0,
|
||||
.hpoint = 0
|
||||
};
|
||||
ledc_channel_config(&ledc_channel);
|
||||
}
|
||||
|
||||
void ledcDetachPin(uint8_t pin)
|
||||
@ -321,9 +177,38 @@ void ledcDetachPin(uint8_t pin)
|
||||
|
||||
double ledcChangeFrequency(uint8_t chan, double freq, uint8_t bit_num)
|
||||
{
|
||||
if (chan > 15) {
|
||||
if(chan >= LEDC_CHANNELS){
|
||||
return 0;
|
||||
}
|
||||
double res_freq = _ledcSetupTimerFreq(chan, freq, bit_num);
|
||||
return res_freq;
|
||||
uint8_t group=(chan/8), timer=((chan/2)%4);
|
||||
|
||||
ledc_timer_config_t ledc_timer = {
|
||||
.speed_mode = group,
|
||||
.timer_num = timer,
|
||||
.duty_resolution = bit_num,
|
||||
.freq_hz = freq,
|
||||
.clk_cfg = LEDC_DEFAULT_CLK
|
||||
};
|
||||
ledc_timer_config(&ledc_timer);
|
||||
channels_resolution[chan] = bit_num;
|
||||
|
||||
return ledc_get_freq(group,timer);
|
||||
}
|
||||
|
||||
static int8_t pin_to_channel[SOC_GPIO_PIN_COUNT] = { 0 };
|
||||
static int cnt_channel = LEDC_CHANNELS;
|
||||
void analogWrite(uint8_t pin, int value) {
|
||||
// Use ledc hardware for internal pins
|
||||
if (pin < SOC_GPIO_PIN_COUNT) {
|
||||
if (pin_to_channel[pin] == 0) {
|
||||
if (!cnt_channel) {
|
||||
log_e("No more analogWrite channels available! You can have maximum %u", LEDC_CHANNELS);
|
||||
return;
|
||||
}
|
||||
pin_to_channel[pin] = cnt_channel--;
|
||||
ledcAttachPin(pin, cnt_channel);
|
||||
ledcSetup(cnt_channel, 1000, 8);
|
||||
}
|
||||
ledcWrite(pin_to_channel[pin] - 1, value);
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -25,6 +25,9 @@ extern "C" {
|
||||
#define RMT_FLAG_ERROR (4)
|
||||
#define RMT_FLAGS_ALL (RMT_FLAG_TX_DONE | RMT_FLAG_RX_DONE | RMT_FLAG_ERROR)
|
||||
|
||||
#define RMT_TX_MODE true
|
||||
#define RMT_RX_MODE false
|
||||
|
||||
struct rmt_obj_s;
|
||||
|
||||
typedef enum {
|
||||
@ -54,6 +57,13 @@ typedef struct {
|
||||
};
|
||||
} rmt_data_t;
|
||||
|
||||
|
||||
/**
|
||||
* Prints object information
|
||||
*
|
||||
*/
|
||||
void _rmtDumpStatus(rmt_obj_t* rmt);
|
||||
|
||||
/**
|
||||
* Initialize the object
|
||||
*
|
||||
@ -69,10 +79,17 @@ float rmtSetTick(rmt_obj_t* rmt, float tick);
|
||||
/**
|
||||
* Sending data in one-go mode or continual mode
|
||||
* (more data being send while updating buffers in interrupts)
|
||||
*
|
||||
* Non-Blocking mode - returns right after executing
|
||||
*/
|
||||
bool rmtWrite(rmt_obj_t* rmt, rmt_data_t* data, size_t size);
|
||||
|
||||
/**
|
||||
* Sending data in one-go mode or continual mode
|
||||
* (more data being send while updating buffers in interrupts)
|
||||
* Blocking mode - only returns when data has been sent
|
||||
*/
|
||||
bool rmtWriteBlocking(rmt_obj_t* rmt, rmt_data_t* data, size_t size);
|
||||
|
||||
/**
|
||||
* Loop data up to the reserved memsize continuously
|
||||
*
|
||||
|
@ -12,38 +12,14 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
|
||||
#include "esp32-hal.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp32-hal-matrix.h"
|
||||
#include "soc/gpio_sd_reg.h"
|
||||
#include "soc/gpio_sd_struct.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "driver/sigmadelta.h"
|
||||
|
||||
#include "esp_system.h"
|
||||
#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+
|
||||
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||
#include "esp32/rom/ets_sys.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "esp32s2/rom/ets_sys.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#include "esp32c3/rom/ets_sys.h"
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
#else // ESP32 Before IDF 4.0
|
||||
#include "rom/ets_sys.h"
|
||||
#endif
|
||||
|
||||
|
||||
#if CONFIG_DISABLE_HAL_LOCKS
|
||||
#define SD_MUTEX_LOCK()
|
||||
#define SD_MUTEX_UNLOCK()
|
||||
#else
|
||||
#define SD_MUTEX_LOCK() do {} while (xSemaphoreTake(_sd_sys_lock, portMAX_DELAY) != pdPASS)
|
||||
#define SD_MUTEX_UNLOCK() xSemaphoreGive(_sd_sys_lock)
|
||||
xSemaphoreHandle _sd_sys_lock;
|
||||
#endif
|
||||
static uint8_t duty_set[SOC_SIGMADELTA_CHANNEL_NUM] = {0};
|
||||
static uint32_t prescaler_set[SOC_SIGMADELTA_CHANNEL_NUM] = {0};
|
||||
|
||||
static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){
|
||||
if(old_apb == new_apb){
|
||||
@ -51,82 +27,63 @@ static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb
|
||||
}
|
||||
uint32_t iarg = (uint32_t)arg;
|
||||
uint8_t channel = iarg;
|
||||
if(ev_type == APB_BEFORE_CHANGE){
|
||||
SIGMADELTA.cg.clk_en = 0;
|
||||
} else {
|
||||
if(ev_type == APB_AFTER_CHANGE){
|
||||
old_apb /= 1000000;
|
||||
new_apb /= 1000000;
|
||||
SD_MUTEX_LOCK();
|
||||
uint32_t old_prescale = SIGMADELTA.channel[channel].prescale + 1;
|
||||
SIGMADELTA.channel[channel].prescale = ((new_apb * old_prescale) / old_apb) - 1;
|
||||
SIGMADELTA.cg.clk_en = 0;
|
||||
SIGMADELTA.cg.clk_en = 1;
|
||||
SD_MUTEX_UNLOCK();
|
||||
uint32_t old_prescale = prescaler_set[channel] + 1;
|
||||
uint32_t new_prescale = ((new_apb * old_prescale) / old_apb) - 1;
|
||||
sigmadelta_set_prescale(channel,new_prescale);
|
||||
prescaler_set[channel] = new_prescale;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t sigmaDeltaSetup(uint8_t channel, uint32_t freq) //chan 0-7 freq 1220-312500
|
||||
uint32_t sigmaDeltaSetup(uint8_t pin, uint8_t channel, uint32_t freq) //chan 0-x according to SOC, freq 1220-312500
|
||||
{
|
||||
if(channel > 7) {
|
||||
if(channel >= SOC_SIGMADELTA_CHANNEL_NUM){
|
||||
return 0;
|
||||
}
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
static bool tHasStarted = false;
|
||||
if(!tHasStarted) {
|
||||
tHasStarted = true;
|
||||
_sd_sys_lock = xSemaphoreCreateMutex();
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t apb_freq = getApbFrequency();
|
||||
uint32_t prescale = (apb_freq/(freq*256)) - 1;
|
||||
if(prescale > 0xFF) {
|
||||
prescale = 0xFF;
|
||||
}
|
||||
SD_MUTEX_LOCK();
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32
|
||||
SIGMADELTA.misc.function_clk_en = 1;
|
||||
#endif
|
||||
SIGMADELTA.channel[channel].prescale = prescale;
|
||||
SIGMADELTA.cg.clk_en = 0;
|
||||
SIGMADELTA.cg.clk_en = 1;
|
||||
SD_MUTEX_UNLOCK();
|
||||
|
||||
sigmadelta_config_t sigmadelta_cfg = {
|
||||
.channel = channel,
|
||||
.sigmadelta_prescale = prescale,
|
||||
.sigmadelta_duty = 0,
|
||||
.sigmadelta_gpio = pin,
|
||||
};
|
||||
sigmadelta_config(&sigmadelta_cfg);
|
||||
|
||||
prescaler_set[channel] = prescale;
|
||||
uint32_t iarg = channel;
|
||||
addApbChangeCallback((void*)iarg, _on_apb_change);
|
||||
|
||||
return apb_freq/((prescale + 1) * 256);
|
||||
}
|
||||
|
||||
void sigmaDeltaWrite(uint8_t channel, uint8_t duty) //chan 0-7 duty 8 bit
|
||||
void sigmaDeltaWrite(uint8_t channel, uint8_t duty) //chan 0-x according to SOC duty 8 bit
|
||||
{
|
||||
if(channel > 7) {
|
||||
if(channel >= SOC_SIGMADELTA_CHANNEL_NUM){
|
||||
return;
|
||||
}
|
||||
duty -= 128;
|
||||
SD_MUTEX_LOCK();
|
||||
SIGMADELTA.channel[channel].duty = duty;
|
||||
SD_MUTEX_UNLOCK();
|
||||
duty -= 128;
|
||||
|
||||
sigmadelta_set_duty(channel,duty);
|
||||
duty_set[channel] = duty;
|
||||
}
|
||||
|
||||
uint8_t sigmaDeltaRead(uint8_t channel) //chan 0-7
|
||||
uint8_t sigmaDeltaRead(uint8_t channel) //chan 0-x according to SOC
|
||||
{
|
||||
if(channel > 7) {
|
||||
if(channel >= SOC_SIGMADELTA_CHANNEL_NUM){
|
||||
return 0;
|
||||
}
|
||||
SD_MUTEX_LOCK();
|
||||
uint8_t duty = SIGMADELTA.channel[channel].duty + 128;
|
||||
SD_MUTEX_UNLOCK();
|
||||
return duty;
|
||||
}
|
||||
|
||||
void sigmaDeltaAttachPin(uint8_t pin, uint8_t channel) //channel 0-7
|
||||
{
|
||||
if(channel > 7) {
|
||||
return;
|
||||
}
|
||||
pinMode(pin, OUTPUT);
|
||||
pinMatrixOutAttach(pin, GPIO_SD0_OUT_IDX + channel, false, false);
|
||||
return duty_set[channel]+128;
|
||||
}
|
||||
|
||||
void sigmaDeltaDetachPin(uint8_t pin)
|
||||
{
|
||||
pinMatrixOutDetach(pin, false, false);
|
||||
}
|
||||
}
|
@ -23,10 +23,9 @@ extern "C" {
|
||||
#include <stdbool.h>
|
||||
|
||||
//channel 0-7 freq 1220-312500 duty 0-255
|
||||
uint32_t sigmaDeltaSetup(uint8_t channel, uint32_t freq);
|
||||
uint32_t sigmaDeltaSetup(uint8_t pin, uint8_t channel, uint32_t freq);
|
||||
void sigmaDeltaWrite(uint8_t channel, uint8_t duty);
|
||||
uint8_t sigmaDeltaRead(uint8_t channel);
|
||||
void sigmaDeltaAttachPin(uint8_t pin, uint8_t channel);
|
||||
void sigmaDeltaDetachPin(uint8_t pin);
|
||||
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -13,333 +13,227 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include "esp32-hal-timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#include "freertos/xtensa_api.h"
|
||||
#include "soc/dport_reg.h"
|
||||
#endif
|
||||
#include "freertos/task.h"
|
||||
#include "soc/timer_group_struct.h"
|
||||
#include "esp_attr.h"
|
||||
#include "driver/periph_ctrl.h"
|
||||
#include "driver/timer.h"
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#include "esp_system.h"
|
||||
#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+
|
||||
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||
#include "esp32/rom/ets_sys.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "esp32s2/rom/ets_sys.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "soc/periph_defs.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#include "esp32c3/rom/ets_sys.h"
|
||||
#include "esp_intr_alloc.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 "esp_intr.h"
|
||||
#endif
|
||||
typedef union {
|
||||
struct {
|
||||
uint32_t reserved0: 10;
|
||||
uint32_t alarm_en: 1; /*When set alarm is enabled*/
|
||||
uint32_t level_int_en: 1; /*When set level type interrupt will be generated during alarm*/
|
||||
uint32_t edge_int_en: 1; /*When set edge type interrupt will be generated during alarm*/
|
||||
uint32_t divider: 16; /*Timer clock (T0/1_clk) pre-scale value.*/
|
||||
uint32_t autoreload: 1; /*When set timer 0/1 auto-reload at alarming is enabled*/
|
||||
uint32_t increase: 1; /*When set timer 0/1 time-base counter increment. When cleared timer 0 time-base counter decrement.*/
|
||||
uint32_t enable: 1; /*When set timer 0/1 time-base counter is enabled*/
|
||||
};
|
||||
uint32_t val;
|
||||
} timer_cfg_t;
|
||||
|
||||
#define HWTIMER_LOCK() portENTER_CRITICAL(timer->lock)
|
||||
#define HWTIMER_UNLOCK() portEXIT_CRITICAL(timer->lock)
|
||||
#define NUM_OF_TIMERS SOC_TIMER_GROUP_TOTAL_TIMERS
|
||||
|
||||
typedef volatile struct {
|
||||
union {
|
||||
struct {
|
||||
uint32_t reserved0: 10;
|
||||
uint32_t alarm_en: 1; /*When set alarm is enabled*/
|
||||
uint32_t level_int_en: 1; /*When set level type interrupt will be generated during alarm*/
|
||||
uint32_t edge_int_en: 1; /*When set edge type interrupt will be generated during alarm*/
|
||||
uint32_t divider: 16; /*Timer clock (T0/1_clk) pre-scale value.*/
|
||||
uint32_t autoreload: 1; /*When set timer 0/1 auto-reload at alarming is enabled*/
|
||||
uint32_t increase: 1; /*When set timer 0/1 time-base counter increment. When cleared timer 0 time-base counter decrement.*/
|
||||
uint32_t enable: 1; /*When set timer 0/1 time-base counter is enabled*/
|
||||
};
|
||||
uint32_t val;
|
||||
} config;
|
||||
uint32_t cnt_low; /*Register to store timer 0/1 time-base counter current value lower 32 bits.*/
|
||||
uint32_t cnt_high; /*Register to store timer 0 time-base counter current value higher 32 bits.*/
|
||||
uint32_t update; /*Write any value will trigger a timer 0 time-base counter value update (timer 0 current value will be stored in registers above)*/
|
||||
uint32_t alarm_low; /*Timer 0 time-base counter value lower 32 bits that will trigger the alarm*/
|
||||
uint32_t alarm_high; /*Timer 0 time-base counter value higher 32 bits that will trigger the alarm*/
|
||||
uint32_t load_low; /*Lower 32 bits of the value that will load into timer 0 time-base counter*/
|
||||
uint32_t load_high; /*higher 32 bits of the value that will load into timer 0 time-base counter*/
|
||||
uint32_t reload; /*Write any value will trigger timer 0 time-base counter reload*/
|
||||
} hw_timer_reg_t;
|
||||
typedef struct {
|
||||
int timer_group;
|
||||
int timer_idx;
|
||||
int alarm_interval;
|
||||
bool auto_reload;
|
||||
} timer_info_t;
|
||||
|
||||
typedef struct hw_timer_s {
|
||||
hw_timer_reg_t * dev;
|
||||
uint8_t num;
|
||||
uint8_t group;
|
||||
uint8_t timer;
|
||||
portMUX_TYPE lock;
|
||||
typedef struct hw_timer_s
|
||||
{
|
||||
uint8_t group;
|
||||
uint8_t num;
|
||||
} hw_timer_t;
|
||||
|
||||
static hw_timer_t hw_timer[4] = {
|
||||
{(hw_timer_reg_t *)(DR_REG_TIMERGROUP0_BASE),0,0,0,portMUX_INITIALIZER_UNLOCKED},
|
||||
{(hw_timer_reg_t *)(DR_REG_TIMERGROUP0_BASE + 0x0024),1,0,1,portMUX_INITIALIZER_UNLOCKED},
|
||||
{(hw_timer_reg_t *)(DR_REG_TIMERGROUP0_BASE + 0x1000),2,1,0,portMUX_INITIALIZER_UNLOCKED},
|
||||
{(hw_timer_reg_t *)(DR_REG_TIMERGROUP0_BASE + 0x1024),3,1,1,portMUX_INITIALIZER_UNLOCKED}
|
||||
// Works for all chips
|
||||
static hw_timer_t timer_dev[4] = {
|
||||
{0,0}, {1,0}, {1,0}, {1,1}
|
||||
};
|
||||
|
||||
typedef void (*voidFuncPtr)(void);
|
||||
static voidFuncPtr __timerInterruptHandlers[4] = {0,0,0,0};
|
||||
// NOTE: (in IDF 5.0 there wont be need to know groups/numbers
|
||||
// timer_init() will list thru all timers and return free timer handle)
|
||||
|
||||
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
|
||||
while(i--){
|
||||
hw_timer_reg_t * dev = hw_timer[i].dev;
|
||||
if((status & (1 << i)) && dev->config.autoreload){
|
||||
dev->config.alarm_en = 1;
|
||||
}
|
||||
}
|
||||
i = 4;
|
||||
//call callbacks
|
||||
while(i--){
|
||||
if(__timerInterruptHandlers[i] && (status & (1 << i))){
|
||||
__timerInterruptHandlers[i]();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t timerRead(hw_timer_t *timer){
|
||||
timer->dev->update = 1;
|
||||
uint64_t h = timer->dev->cnt_high;
|
||||
uint64_t l = timer->dev->cnt_low;
|
||||
return (h << 32) | l;
|
||||
uint64_t inline timerRead(hw_timer_t *timer){
|
||||
|
||||
uint64_t value;
|
||||
timer_get_counter_value(timer->group, timer->num,&value);
|
||||
return value;
|
||||
}
|
||||
|
||||
uint64_t timerAlarmRead(hw_timer_t *timer){
|
||||
uint64_t h = timer->dev->alarm_high;
|
||||
uint64_t l = timer->dev->alarm_low;
|
||||
return (h << 32) | l;
|
||||
uint64_t value;
|
||||
timer_get_alarm_value(timer->group, timer->num, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
void timerWrite(hw_timer_t *timer, uint64_t val){
|
||||
timer->dev->load_high = (uint32_t) (val >> 32);
|
||||
timer->dev->load_low = (uint32_t) (val);
|
||||
timer->dev->reload = 1;
|
||||
timer_set_counter_value(timer->group, timer->num, val);
|
||||
}
|
||||
|
||||
void timerAlarmWrite(hw_timer_t *timer, uint64_t alarm_value, bool autoreload){
|
||||
timer->dev->alarm_high = (uint32_t) (alarm_value >> 32);
|
||||
timer->dev->alarm_low = (uint32_t) alarm_value;
|
||||
timer->dev->config.autoreload = autoreload;
|
||||
timer_set_alarm_value(timer->group, timer->num, alarm_value);
|
||||
timerSetAutoReload(timer,autoreload);
|
||||
}
|
||||
|
||||
void timerSetConfig(hw_timer_t *timer, uint32_t config){
|
||||
timer->dev->config.val = config;
|
||||
timer_cfg_t cfg;
|
||||
cfg.val = config;
|
||||
timer_set_alarm(timer->group, timer->num, cfg.alarm_en);
|
||||
timerSetDivider(timer,cfg.divider);
|
||||
timerSetAutoReload(timer,cfg.autoreload);
|
||||
timerSetCountUp(timer, cfg.increase);
|
||||
|
||||
if (cfg.enable) {
|
||||
timerStart(timer);
|
||||
}
|
||||
else{
|
||||
timerStop(timer);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t timerGetConfig(hw_timer_t *timer){
|
||||
return timer->dev->config.val;
|
||||
timer_config_t timer_cfg;
|
||||
timer_get_config(timer->group, timer->num,&timer_cfg);
|
||||
|
||||
//Translate to default uint32_t
|
||||
timer_cfg_t cfg;
|
||||
cfg.alarm_en = timer_cfg.alarm_en;
|
||||
cfg.autoreload = timer_cfg.auto_reload;
|
||||
cfg.divider = timer_cfg.divider;
|
||||
cfg.edge_int_en = timer_cfg.intr_type;
|
||||
cfg.level_int_en = !timer_cfg.intr_type;
|
||||
cfg.enable = timer_cfg.counter_en;
|
||||
cfg.increase = timer_cfg.counter_dir;
|
||||
|
||||
return cfg.val;
|
||||
}
|
||||
|
||||
void timerSetCountUp(hw_timer_t *timer, bool countUp){
|
||||
timer->dev->config.increase = countUp;
|
||||
timer_set_counter_mode(timer->group, timer->num,countUp);
|
||||
}
|
||||
|
||||
bool timerGetCountUp(hw_timer_t *timer){
|
||||
return timer->dev->config.increase;
|
||||
timer_cfg_t config;
|
||||
config.val = timerGetConfig(timer);
|
||||
return config.increase;
|
||||
}
|
||||
|
||||
void timerSetAutoReload(hw_timer_t *timer, bool autoreload){
|
||||
timer->dev->config.autoreload = autoreload;
|
||||
timer_set_auto_reload(timer->group, timer->num,autoreload);
|
||||
}
|
||||
|
||||
bool timerGetAutoReload(hw_timer_t *timer){
|
||||
return timer->dev->config.autoreload;
|
||||
timer_cfg_t config;
|
||||
config.val= timerGetConfig(timer);
|
||||
return config.autoreload;
|
||||
}
|
||||
|
||||
void timerSetDivider(hw_timer_t *timer, uint16_t divider){//2 to 65536
|
||||
if(!divider){
|
||||
divider = 0xFFFF;
|
||||
} else if(divider == 1){
|
||||
divider = 2;
|
||||
// Set divider from 2 to 65535
|
||||
void timerSetDivider(hw_timer_t *timer, uint16_t divider){
|
||||
if(divider < 2)
|
||||
{
|
||||
log_e("Timer divider must be set in range of 2 to 65535");
|
||||
return;
|
||||
}
|
||||
int timer_en = timer->dev->config.enable;
|
||||
timer->dev->config.enable = 0;
|
||||
timer->dev->config.divider = divider;
|
||||
timer->dev->config.enable = timer_en;
|
||||
timer_set_divider(timer->group, timer->num,divider);
|
||||
}
|
||||
|
||||
uint16_t timerGetDivider(hw_timer_t *timer){
|
||||
return timer->dev->config.divider;
|
||||
timer_cfg_t config;
|
||||
config.val = timerGetConfig(timer);
|
||||
return config.divider;
|
||||
}
|
||||
|
||||
void timerStart(hw_timer_t *timer){
|
||||
timer->dev->config.enable = 1;
|
||||
timer_start(timer->group, timer->num);
|
||||
}
|
||||
|
||||
void timerStop(hw_timer_t *timer){
|
||||
timer->dev->config.enable = 0;
|
||||
timer_pause(timer->group, timer->num);
|
||||
}
|
||||
|
||||
void timerRestart(hw_timer_t *timer){
|
||||
timer->dev->config.enable = 0;
|
||||
timer->dev->reload = 1;
|
||||
timer->dev->config.enable = 1;
|
||||
timerWrite(timer,0);
|
||||
}
|
||||
|
||||
bool timerStarted(hw_timer_t *timer){
|
||||
return timer->dev->config.enable;
|
||||
timer_cfg_t config;
|
||||
config.val = timerGetConfig(timer);
|
||||
return config.enable;
|
||||
}
|
||||
|
||||
void timerAlarmEnable(hw_timer_t *timer){
|
||||
timer->dev->config.alarm_en = 1;
|
||||
timer_set_alarm(timer->group, timer->num,true);
|
||||
}
|
||||
|
||||
void timerAlarmDisable(hw_timer_t *timer){
|
||||
timer->dev->config.alarm_en = 0;
|
||||
timer_set_alarm(timer->group, timer->num,false);
|
||||
}
|
||||
|
||||
bool timerAlarmEnabled(hw_timer_t *timer){
|
||||
return timer->dev->config.alarm_en;
|
||||
timer_cfg_t config;
|
||||
config.val = timerGetConfig(timer);
|
||||
return config.alarm_en;
|
||||
}
|
||||
|
||||
static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){
|
||||
hw_timer_t * timer = (hw_timer_t *)arg;
|
||||
if(ev_type == APB_BEFORE_CHANGE){
|
||||
timer->dev->config.enable = 0;
|
||||
timerStop(timer);
|
||||
} else {
|
||||
old_apb /= 1000000;
|
||||
new_apb /= 1000000;
|
||||
timer->dev->config.divider = (new_apb * timer->dev->config.divider) / old_apb;
|
||||
timer->dev->config.enable = 1;
|
||||
uint16_t divider = (new_apb * timerGetDivider(timer)) / old_apb;
|
||||
timerSetDivider(timer,divider);
|
||||
timerStart(timer);
|
||||
}
|
||||
}
|
||||
|
||||
hw_timer_t * timerBegin(uint8_t num, uint16_t divider, bool countUp){
|
||||
if(num > 3){
|
||||
if(num >= NUM_OF_TIMERS)
|
||||
{
|
||||
log_e("Timer dont have that timer number.");
|
||||
return NULL;
|
||||
}
|
||||
hw_timer_t * timer = &hw_timer[num];
|
||||
if(timer->group) {
|
||||
periph_module_enable(PERIPH_TIMG1_MODULE);
|
||||
} else {
|
||||
periph_module_enable(PERIPH_TIMG0_MODULE);
|
||||
}
|
||||
timer->dev->config.enable = 0;
|
||||
if(timer->group) {
|
||||
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);
|
||||
#endif
|
||||
} else {
|
||||
TIMERG0.int_ena.val &= ~BIT(timer->timer);
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
TIMERG0.int_clr_timers.val |= BIT(timer->timer);
|
||||
#else
|
||||
TIMERG0.int_clr.val = BIT(timer->timer);
|
||||
#endif
|
||||
}
|
||||
#ifdef TIMER_GROUP_SUPPORTS_XTAL_CLOCK
|
||||
timer->dev->config.use_xtal = 0;
|
||||
#endif
|
||||
timerSetDivider(timer, divider);
|
||||
timerSetCountUp(timer, countUp);
|
||||
timerSetAutoReload(timer, false);
|
||||
timerAttachInterrupt(timer, NULL, false);
|
||||
timerWrite(timer, 0);
|
||||
timer->dev->config.enable = 1;
|
||||
|
||||
hw_timer_t * timer = &timer_dev[num]; //Get Timer group/num from 0-3 number
|
||||
|
||||
timer_config_t config = {
|
||||
.divider = divider,
|
||||
.counter_dir = countUp,
|
||||
.counter_en = TIMER_PAUSE,
|
||||
.alarm_en = TIMER_ALARM_DIS,
|
||||
.auto_reload = false,
|
||||
};
|
||||
|
||||
timer_init(timer->group, timer->num, &config);
|
||||
timer_set_counter_value(timer->group, timer->num, 0);
|
||||
timerStart(timer);
|
||||
addApbChangeCallback(timer, _on_apb_change);
|
||||
return timer;
|
||||
}
|
||||
|
||||
void timerEnd(hw_timer_t *timer){
|
||||
timer->dev->config.enable = 0;
|
||||
timerAttachInterrupt(timer, NULL, false);
|
||||
removeApbChangeCallback(timer, _on_apb_change);
|
||||
timer_deinit(timer->group, timer->num);
|
||||
}
|
||||
|
||||
void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge){
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
if(edge){
|
||||
log_w("EDGE timer interrupt does not work properly on ESP32! Setting to LEVEL...");
|
||||
log_w("EDGE timer interrupt is not supported! Setting to LEVEL...");
|
||||
edge = false;
|
||||
}
|
||||
#endif
|
||||
static bool initialized = false;
|
||||
static intr_handle_t intr_handle = NULL;
|
||||
if(intr_handle){
|
||||
esp_intr_disable(intr_handle);
|
||||
}
|
||||
if(fn == NULL){
|
||||
timer->dev->config.level_int_en = 0;
|
||||
timer->dev->config.edge_int_en = 0;
|
||||
timer->dev->config.alarm_en = 0;
|
||||
if(timer->num & 2){
|
||||
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);
|
||||
#endif
|
||||
} else {
|
||||
TIMERG0.int_ena.val &= ~BIT(timer->timer);
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
TIMERG0.int_clr_timers.val |= BIT(timer->timer);
|
||||
#else
|
||||
TIMERG0.int_clr.val = BIT(timer->timer);
|
||||
#endif
|
||||
}
|
||||
__timerInterruptHandlers[timer->num] = NULL;
|
||||
} else {
|
||||
__timerInterruptHandlers[timer->num] = fn;
|
||||
timer->dev->config.level_int_en = edge?0:1;//When set, an alarm will generate a level type interrupt.
|
||||
timer->dev->config.edge_int_en = edge?1:0;//When set, an alarm will generate an edge type interrupt.
|
||||
int intr_source = 0;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
if(!edge){
|
||||
#endif
|
||||
if(timer->group){
|
||||
intr_source = ETS_TG1_T0_LEVEL_INTR_SOURCE + timer->timer;
|
||||
} else {
|
||||
intr_source = ETS_TG0_T0_LEVEL_INTR_SOURCE + timer->timer;
|
||||
}
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
} else {
|
||||
if(timer->group){
|
||||
intr_source = ETS_TG1_T0_EDGE_INTR_SOURCE + timer->timer;
|
||||
} else {
|
||||
intr_source = ETS_TG0_T0_EDGE_INTR_SOURCE + timer->timer;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if(!initialized){
|
||||
initialized = true;
|
||||
esp_intr_alloc(intr_source, (int)(ARDUINO_ISR_FLAG|ESP_INTR_FLAG_LOWMED), __timerISR, NULL, &intr_handle);
|
||||
} else {
|
||||
intr_matrix_set(esp_intr_get_cpu(intr_handle), intr_source, esp_intr_get_intno(intr_handle));
|
||||
}
|
||||
if(timer->group){
|
||||
TIMERG1.int_ena.val |= BIT(timer->timer);
|
||||
} else {
|
||||
TIMERG0.int_ena.val |= BIT(timer->timer);
|
||||
}
|
||||
}
|
||||
if(intr_handle){
|
||||
esp_intr_enable(intr_handle);
|
||||
}
|
||||
timer_enable_intr(timer->group, timer->num);
|
||||
|
||||
timer_info_t *timer_info = calloc(1, sizeof(timer_info_t));
|
||||
timer_info->timer_group = timer->group;
|
||||
timer_info->timer_idx = timer->num;
|
||||
timer_info->auto_reload = timerGetAutoReload(timer);
|
||||
timer_info->alarm_interval = timerAlarmRead(timer);
|
||||
|
||||
timer_isr_callback_add(timer->group, timer->num, (timer_isr_t)fn, timer_info, 0);
|
||||
}
|
||||
|
||||
void timerDetachInterrupt(hw_timer_t *timer){
|
||||
@ -352,6 +246,12 @@ uint64_t timerReadMicros(hw_timer_t *timer){
|
||||
return timer_val * div / (getApbFrequency() / 1000000);
|
||||
}
|
||||
|
||||
uint64_t timerReadMilis(hw_timer_t *timer){
|
||||
uint64_t timer_val = timerRead(timer);
|
||||
uint16_t div = timerGetDivider(timer);
|
||||
return timer_val * div / (getApbFrequency() / 1000);
|
||||
}
|
||||
|
||||
double timerReadSeconds(hw_timer_t *timer){
|
||||
uint64_t timer_val = timerRead(timer);
|
||||
uint16_t div = timerGetDivider(timer);
|
||||
@ -364,6 +264,12 @@ uint64_t timerAlarmReadMicros(hw_timer_t *timer){
|
||||
return timer_val * div / (getApbFrequency() / 1000000);
|
||||
}
|
||||
|
||||
uint64_t timerAlarmReadMilis(hw_timer_t *timer){
|
||||
uint64_t timer_val = timerAlarmRead(timer);
|
||||
uint16_t div = timerGetDivider(timer);
|
||||
return timer_val * div / (getApbFrequency() / 1000);
|
||||
}
|
||||
|
||||
double timerAlarmReadSeconds(hw_timer_t *timer){
|
||||
uint64_t timer_val = timerAlarmRead(timer);
|
||||
uint16_t div = timerGetDivider(timer);
|
||||
|
@ -20,13 +20,13 @@
|
||||
#ifndef MAIN_ESP32_HAL_TIMER_H_
|
||||
#define MAIN_ESP32_HAL_TIMER_H_
|
||||
|
||||
#include "esp32-hal.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "esp32-hal.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
|
||||
struct hw_timer_s;
|
||||
typedef struct hw_timer_s hw_timer_t;
|
||||
|
||||
@ -50,6 +50,7 @@ void timerSetAutoReload(hw_timer_t *timer, bool autoreload);
|
||||
bool timerStarted(hw_timer_t *timer);
|
||||
uint64_t timerRead(hw_timer_t *timer);
|
||||
uint64_t timerReadMicros(hw_timer_t *timer);
|
||||
uint64_t timerReadMilis(hw_timer_t *timer);
|
||||
double timerReadSeconds(hw_timer_t *timer);
|
||||
uint16_t timerGetDivider(hw_timer_t *timer);
|
||||
bool timerGetCountUp(hw_timer_t *timer);
|
||||
|
@ -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,101 @@
|
||||
|
||||
#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)
|
||||
// solves issue https://github.com/espressif/arduino-esp32/issues/6032
|
||||
// baudrate must be multiplied when CPU Frequency is lower than APB 80MHz
|
||||
uint32_t _get_effective_baudrate(uint32_t baudrate)
|
||||
{
|
||||
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();
|
||||
uint32_t Freq = getApbFrequency()/1000000;
|
||||
if (Freq < 80) {
|
||||
return 80 / Freq * baudrate;
|
||||
}
|
||||
else {
|
||||
return baudrate;
|
||||
}
|
||||
}
|
||||
|
||||
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 +118,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 +131,146 @@ 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 = _get_effective_baudrate(baudrate);
|
||||
uart_config.data_bits = (config & 0xc) >> 2;
|
||||
uart_config.parity = (config & 0x3);
|
||||
uart_config.stop_bits = (config & 0x30) >> 4;
|
||||
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
|
||||
uart_config.rx_flow_ctrl_thresh = rxfifo_full_thrhd;
|
||||
uart_config.source_clk = UART_SCLK_APB;
|
||||
|
||||
|
||||
ESP_ERROR_CHECK(uart_driver_install(uart_nr, 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; //
|
||||
// Set RS485 half duplex mode on UART. This shall force flush to wait up to sending all bits out
|
||||
ESP_ERROR_CHECK(uart_set_mode(uart_nr, UART_MODE_RS485_HALF_DUPLEX));
|
||||
|
||||
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 +279,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 +304,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 +320,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), _get_effective_baudrate(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 +364,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 +382,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 +412,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 +433,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 +560,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 +592,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
|
0
cores/esp32/libb64/AUTHORS
Executable file → Normal file
0
cores/esp32/libb64/AUTHORS
Executable file → Normal file
0
cores/esp32/libb64/LICENSE
Executable file → Normal file
0
cores/esp32/libb64/LICENSE
Executable file → Normal file
2
cores/esp32/libb64/cdecode.c
Executable file → Normal file
2
cores/esp32/libb64/cdecode.c
Executable file → Normal file
@ -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];
|
||||
}
|
||||
|
||||
|
0
cores/esp32/libb64/cdecode.h
Executable file → Normal file
0
cores/esp32/libb64/cdecode.h
Executable file → Normal file
0
cores/esp32/libb64/cencode.c
Executable file → Normal file
0
cores/esp32/libb64/cencode.c
Executable file → Normal file
0
cores/esp32/libb64/cencode.h
Executable file → Normal file
0
cores/esp32/libb64/cencode.h
Executable file → Normal file
@ -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
|
||||
@ -30,6 +33,10 @@ void yieldIfNecessary(void){
|
||||
|
||||
bool loopTaskWDTEnabled;
|
||||
|
||||
__attribute__((weak)) size_t getArduinoLoopTaskStackSize(void) {
|
||||
return ARDUINO_LOOP_STACK_SIZE;
|
||||
}
|
||||
|
||||
void loopTask(void *pvParameters)
|
||||
{
|
||||
setup();
|
||||
@ -47,13 +54,21 @@ 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();
|
||||
xTaskCreateUniversal(loopTask, "loopTask", ARDUINO_LOOP_STACK_SIZE, NULL, 1, &loopTaskHandle, ARDUINO_RUNNING_CORE);
|
||||
xTaskCreateUniversal(loopTask, "loopTask", getArduinoLoopTaskStackSize(), NULL, 1, &loopTaskHandle, ARDUINO_RUNNING_CORE);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -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 |
BIN
docs/source/_static/gpio_output.png
Normal file
BIN
docs/source/_static/gpio_output.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.4 KiB |
BIN
docs/source/_static/gpio_pullup.png
Normal file
BIN
docs/source/_static/gpio_pullup.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.7 KiB |
@ -1,3 +1,170 @@
|
||||
####
|
||||
GPIO
|
||||
####
|
||||
|
||||
About
|
||||
-----
|
||||
|
||||
One of the most used and versatile peripheral in a microcontroller is the GPIO. The GPIO is commonly used to write and read the pin state.
|
||||
|
||||
GPIO stands to General Purpose Input Output, and is responsible to control or read the state of a specific pin in the digital world. For example, this peripheral is widely used to create the LED blinking or to read a simple button.
|
||||
|
||||
.. note:: There are some GPIOs with special restrictions, and not all GPIOs are accessible through the developemnt board. For more information about it, see the corresponding board pin layout information.
|
||||
|
||||
GPIOs Modes
|
||||
***********
|
||||
|
||||
There are two different modes in the GPIO configuration:
|
||||
|
||||
- **Input Mode**
|
||||
|
||||
In this mode, the GPIO will receive the digital state from a specific device. This device could be a button or a switch.
|
||||
|
||||
- **Output Mode**
|
||||
|
||||
For the output mode, the GPIO will change the GPIO digital state to a specific device. You can drive an LED for example.
|
||||
|
||||
GPIO API
|
||||
--------
|
||||
|
||||
Here is the common functions used for the GPIO peripheral.
|
||||
|
||||
pinMode
|
||||
*******
|
||||
|
||||
The ``pinMode`` function is used to define the GPIO operation mode for a specific pin.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
void pinMode(uint8_t pin, uint8_t mode);
|
||||
|
||||
* ``pin`` defines the GPIO pin number.
|
||||
* ``mode`` sets operation mode.
|
||||
|
||||
The following modes are supported for the basic `input` and `output`:
|
||||
|
||||
* **INPUT** sets the GPIO as input without pullup or pulldown (high impedance).
|
||||
* **OUTPUT** sets the GPIO as output/read mode.
|
||||
* **INPUT_PULLDOWN** sets the GPIO as input with the internal pulldown.
|
||||
* **INPUT_PULLUP** sets the GPIO as input with the internal pullup.
|
||||
|
||||
Internal Pullup and Pulldown
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The ESP32 SoC families supports the internal pullup and pulldown throught a 45kR resistor, that can be enabled when configuring the GPIO mode as ``INPUT`` mode.
|
||||
If the pullup or pulldown mode is not defined, the pin will stay in the high impedance mode.
|
||||
|
||||
digitalWrite
|
||||
*************
|
||||
|
||||
The function ``digitalWrite`` sets the state of the selected GPIO to ``HIGH`` or ``LOW``. This function is only used if the ``pinMode`` was configured as ``OUTPUT``.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
void digitalWrite(uint8_t pin, uint8_t val);
|
||||
|
||||
* ``pin`` defines the GPIO pin number.
|
||||
* ``val`` set the output digital state to ``HIGH`` or ``LOW``.
|
||||
|
||||
digitalRead
|
||||
***********
|
||||
|
||||
To read the state of a given pin configured as ``INPUT``, the function ``digitalRead`` is used.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
int digitalRead(uint8_t pin);
|
||||
|
||||
* ``pin`` select GPIO
|
||||
|
||||
This function will return the logical state of the selected pin as ``HIGH`` or ``LOW``.
|
||||
|
||||
Interrupts
|
||||
----------
|
||||
|
||||
The GPIO peripheral on the ESP32 supports interruptions.
|
||||
|
||||
attachInterrupt
|
||||
***************
|
||||
|
||||
The function ``attachInterruptArg`` is used to attach the interrupt to the defined pin.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode);
|
||||
|
||||
* ``pin`` defines the GPIO pin number.
|
||||
* ``handler`` set the handler function.
|
||||
* ``mode`` set the interrupt mode.
|
||||
|
||||
Here are the supported interrupt modes:
|
||||
|
||||
* **DISABLED**
|
||||
* **RISING**
|
||||
* **FALLING**
|
||||
* **CHANGE**
|
||||
* **ONLOW**
|
||||
* **ONHIGH**
|
||||
* **ONLOW_WE**
|
||||
* **ONHIGH_WE**
|
||||
|
||||
attachInterruptArg
|
||||
******************
|
||||
|
||||
The function ``attachInterruptArg`` is used to attach the interrupt to the defined pin using arguments.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
attachInterruptArg(uint8_t pin, voidFuncPtrArg handler, void * arg, int mode);
|
||||
|
||||
* ``pin`` defines the GPIO pin number.
|
||||
* ``handler`` set the handler function.
|
||||
* ``arg`` pointer to the interrupt arguments.
|
||||
* ``mode`` set the interrupt mode.
|
||||
|
||||
detachInterrupt
|
||||
***************
|
||||
|
||||
To detach the interruption from a specific pin, use the ``detachInterrupt`` function giving the GPIO to be detached.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
detachInterrupt(uint8_t pin);
|
||||
|
||||
* ``pin`` defines the GPIO pin number.
|
||||
|
||||
.. _gpio_example_code:
|
||||
|
||||
Example Code
|
||||
------------
|
||||
|
||||
GPIO Input and Output Modes
|
||||
***************************
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
#define LED 12
|
||||
#define BUTTON 2
|
||||
|
||||
uint8_t stateLED = 0;
|
||||
|
||||
void setup() {
|
||||
pinMode(LED, OUTPUT);
|
||||
pinMode(BUTTON,INPUT_PULLUP);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
if(!digitalRead(BUTTON)){
|
||||
stateLED = stateLED^1;
|
||||
digitalWrite(LED,stateLED);
|
||||
}
|
||||
}
|
||||
|
||||
GPIO Interrupt
|
||||
**************
|
||||
|
||||
.. literalinclude:: ../../../libraries/ESP32/examples/GPIO/GPIOInterrupt/GPIOInterrupt.ino
|
||||
:language: arduino
|
||||
|
||||
.. _datasheet: https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf
|
||||
|
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
|
331
docs/source/api/usb.rst
Normal file
331
docs/source/api/usb.rst
Normal file
@ -0,0 +1,331 @@
|
||||
#######
|
||||
USB API
|
||||
#######
|
||||
|
||||
.. note:: This feature is only supported on ESP chips that have USB peripheral, like the ESP32-S2 and ESP32-S3. Some chips, like the ESP32-C3 include native CDC+JTAG peripheral that is not covered here.
|
||||
|
||||
About
|
||||
-----
|
||||
|
||||
The **Universal Serial Bus** is a widely used peripheral to exchange data between devices. USB was introduced on the ESP32, supporting both device and host mode.
|
||||
|
||||
To learn about the USB, see the `USB.org`_ for developers.
|
||||
|
||||
USB as Device
|
||||
*************
|
||||
|
||||
In the device mode, the ESP32 acts as an USB device, like a mouse or keyboard to be connected to a host device, like your computer or smartphone.
|
||||
|
||||
USB as Host
|
||||
***********
|
||||
|
||||
The USB host mode, you can connect devices on the ESP32, like external modems, mouse and keyboards.
|
||||
|
||||
.. note:: This mode is still under development for the ESP32.
|
||||
|
||||
API Description
|
||||
---------------
|
||||
|
||||
This is the common USB API description.
|
||||
|
||||
For more supported USB classes implementation, see the following sections:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Classes:
|
||||
|
||||
USB CDC <usb_cdc>
|
||||
USB MSC <usb_msc>
|
||||
|
||||
USB Common
|
||||
**********
|
||||
|
||||
These are the common APIs for the USB driver.
|
||||
|
||||
onEvent
|
||||
^^^^^^^
|
||||
|
||||
Event handling function to set the callback.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
void onEvent(esp_event_handler_t callback);
|
||||
|
||||
Event handling function for the specific event.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
void onEvent(arduino_usb_event_t event, esp_event_handler_t callback);
|
||||
|
||||
Where ``event`` can be:
|
||||
|
||||
* ARDUINO_USB_ANY_EVENT
|
||||
* ARDUINO_USB_STARTED_EVENT
|
||||
* ARDUINO_USB_STOPPED_EVENT
|
||||
* ARDUINO_USB_SUSPEND_EVENT
|
||||
* ARDUINO_USB_RESUME_EVENT
|
||||
* ARDUINO_USB_MAX_EVENT
|
||||
|
||||
VID
|
||||
^^^
|
||||
|
||||
Set the Vendor ID. This 16 bits identification is used to identify the company that develops the product.
|
||||
|
||||
.. note:: You can't define your own VID. If you need your own VID, you need to buy one. See https://www.usb.org/getting-vendor-id for more details.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
bool VID(uint16_t v);
|
||||
|
||||
|
||||
Get the Vendor ID.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
uint16_t VID(void);
|
||||
|
||||
Returns the Vendor ID. The default value for the VID is: ``0x303A``.
|
||||
|
||||
PID
|
||||
^^^
|
||||
|
||||
Set the Product ID. This 16 bits identification is used to identify the product.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
bool PID(uint16_t p);
|
||||
|
||||
Get the Product ID.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
uint16_t PID(void);
|
||||
|
||||
Returns the Product ID. The default PID is: ``0x0002``.
|
||||
|
||||
firmwareVersion
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
Set the firmware version. This is a 16 bits unsigned value.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
bool firmwareVersion(uint16_t version);
|
||||
|
||||
Get the firmware version.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
uint16_t firmwareVersion(void);
|
||||
|
||||
Return the 16 bits unsigned value. The default value is: ``0x100``.
|
||||
|
||||
usbVersion
|
||||
^^^^^^^^^^
|
||||
|
||||
Set the USB version.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
bool usbVersion(uint16_t version);
|
||||
|
||||
Get the USB version.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
uint16_t usbVersion(void);
|
||||
|
||||
Return the USB version. The default value is: ``0x200`` (USB 2.0).
|
||||
|
||||
usbPower
|
||||
^^^^^^^^
|
||||
|
||||
Set the USB power as mA (current).
|
||||
|
||||
.. note:: This configuration does not change the physical power output. This is only used for the USB device information.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
bool usbPower(uint16_t mA);
|
||||
|
||||
Get the USB power configuration.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
uint16_t usbPower(void);
|
||||
|
||||
Return the current in mA. The default value is: ``0x500`` (500mA).
|
||||
|
||||
usbClass
|
||||
^^^^^^^^
|
||||
|
||||
Set the USB class.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
bool usbClass(uint8_t _class);
|
||||
|
||||
Get the USB class.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
uint8_t usbClass(void);
|
||||
|
||||
Return the USB class. The default value is: ``TUSB_CLASS_MISC``.
|
||||
|
||||
usbSubClass
|
||||
^^^^^^^^^^^
|
||||
|
||||
Set the USB sub-class.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
bool usbSubClass(uint8_t subClass);
|
||||
|
||||
Get the USB sub-class.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
uint8_t usbSubClass(void);
|
||||
|
||||
Return the USB sub-class. The default value is: ``MISC_SUBCLASS_COMMON``.
|
||||
|
||||
usbProtocol
|
||||
^^^^^^^^^^^
|
||||
|
||||
Define the USB protocol.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
bool usbProtocol(uint8_t protocol);
|
||||
|
||||
Get the USB protocol.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
uint8_t usbProtocol(void);
|
||||
|
||||
Return the USB protocol. The default value is: ``MISC_PROTOCOL_IAD``
|
||||
|
||||
usbAttributes
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
Set the USB attributes.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
bool usbAttributes(uint8_t attr);
|
||||
|
||||
Get the USB attributes.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
uint8_t usbAttributes(void);
|
||||
|
||||
Return the USB attributes. The default value is: ``TUSB_DESC_CONFIG_ATT_SELF_POWERED``
|
||||
|
||||
webUSB
|
||||
^^^^^^
|
||||
|
||||
This function is used to enable the ``webUSB`` functionality.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
bool webUSB(bool enabled);
|
||||
|
||||
This function is used to get the ``webUSB`` setting.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
bool webUSB(void);
|
||||
|
||||
Return the ``webUSB`` setting (`Enabled` or `Disabled`)
|
||||
|
||||
productName
|
||||
^^^^^^^^^^^
|
||||
|
||||
This function is used to define the product name.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
bool productName(const char * name);
|
||||
|
||||
This function is used to get the product's name.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
const char * productName(void);
|
||||
|
||||
manufacturerName
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
This function is used to define the manufacturer name.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
bool manufacturerName(const char * name);
|
||||
|
||||
This function is used to get the manufacturer's name.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
const char * manufacturerName(void);
|
||||
|
||||
serialNumber
|
||||
^^^^^^^^^^^^
|
||||
|
||||
This function is used to define the serial number.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
bool serialNumber(const char * name);
|
||||
|
||||
This function is used to get the serial number.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
const char * serialNumber(void);
|
||||
|
||||
The default serial number is: ``0``.
|
||||
|
||||
webUSBURL
|
||||
^^^^^^^^^
|
||||
|
||||
This function is used to define the ``webUSBURL``.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
bool webUSBURL(const char * name);
|
||||
|
||||
This function is used to get the ``webUSBURL``.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
const char * webUSBURL(void);
|
||||
|
||||
The default ``webUSBURL`` is: https://espressif.github.io/arduino-esp32/webusb.html
|
||||
|
||||
enableDFU
|
||||
^^^^^^^^^
|
||||
|
||||
This function is used to enable the DFU capability.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
bool enableDFU();
|
||||
|
||||
begin
|
||||
^^^^^
|
||||
|
||||
This function is used to start the peripheral using the default configuration.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
bool begin();
|
||||
|
||||
Example Code
|
||||
------------
|
||||
|
||||
There are a collection of USB device examples on the project GitHub, including Firmware MSC update, USB CDC, HID and composite device.
|
||||
|
||||
.. _USB.org: https://www.usb.org/developers
|
192
docs/source/api/usb_cdc.rst
Normal file
192
docs/source/api/usb_cdc.rst
Normal file
@ -0,0 +1,192 @@
|
||||
#######
|
||||
USB CDC
|
||||
#######
|
||||
|
||||
About
|
||||
-----
|
||||
|
||||
USB Communications Device Class API. This class is used to enable communication between the host and the device.
|
||||
|
||||
This class is often used to enable serial communication and can be used to flash the firmware on the ESP32 without the external USB to Serial chip.
|
||||
|
||||
APIs
|
||||
****
|
||||
|
||||
onEvent
|
||||
^^^^^^^
|
||||
|
||||
Event handling functions.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
void onEvent(esp_event_handler_t callback);
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
void onEvent(arduino_usb_cdc_event_t event, esp_event_handler_t callback);
|
||||
|
||||
Where ``event`` can be:
|
||||
|
||||
* ARDUINO_USB_CDC_ANY_EVENT
|
||||
* ARDUINO_USB_CDC_CONNECTED_EVENT
|
||||
* ARDUINO_USB_CDC_DISCONNECTED_EVENT
|
||||
* ARDUINO_USB_CDC_LINE_STATE_EVENT
|
||||
* ARDUINO_USB_CDC_LINE_CODING_EVENT
|
||||
* ARDUINO_USB_CDC_RX_EVENT
|
||||
* ARDUINO_USB_CDC_TX_EVENT
|
||||
* ARDUINO_USB_CDC_MAX_EVENT
|
||||
|
||||
setRxBufferSize
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
The ``setRxBufferSize`` function is used to set the size of the RX buffer.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
size_t setRxBufferSize(size_t size);
|
||||
|
||||
setTxTimeoutMs
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
This function is used to define the time to reach the timeout for the TX.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
void setTxTimeoutMs(uint32_t timeout);
|
||||
|
||||
begin
|
||||
^^^^^
|
||||
|
||||
This function is used to start the peripheral using the default CDC configuration.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
void begin(unsigned long baud);
|
||||
|
||||
Where:
|
||||
|
||||
* ``baud`` is the baud rate.
|
||||
|
||||
end
|
||||
^^^
|
||||
|
||||
This function will finish the peripheral as CDC and release all the allocated resources. After calling ``end`` you need to use ``begin`` again in order to initialize the USB CDC driver again.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
void end();
|
||||
|
||||
available
|
||||
^^^^^^^^^
|
||||
|
||||
This function will return if there are messages in the queue.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
int available(void);
|
||||
|
||||
The return is the number of bytes available to read.
|
||||
|
||||
availableForWrite
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
This function will return if the hardware is available to write data.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
int availableForWrite(void);
|
||||
|
||||
peek
|
||||
^^^^
|
||||
|
||||
This function is used to ``peek`` messages from the queue.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
int peek(void);
|
||||
|
||||
read
|
||||
^^^^
|
||||
|
||||
This function is used to read the bytes available.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
size_t read(uint8_t *buffer, size_t size);
|
||||
|
||||
Where:
|
||||
|
||||
* ``buffer`` is the pointer to the buffer to be read.
|
||||
* ``size`` is the number of bytes to be read.
|
||||
|
||||
write
|
||||
^^^^^
|
||||
|
||||
This function is used to write the message.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
size_t write(const uint8_t *buffer, size_t size);
|
||||
|
||||
Where:
|
||||
|
||||
* ``buffer`` is the pointer to the buffer to be written.
|
||||
* ``size`` is the number of bytes to be written.
|
||||
|
||||
flush
|
||||
^^^^^
|
||||
|
||||
This function is used to flush the data.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
void flush(void);
|
||||
|
||||
baudRate
|
||||
^^^^^^^^
|
||||
|
||||
This function is used to get the ``baudRate``.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
uint32_t baudRate();
|
||||
|
||||
setDebugOutput
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
This function will enable the debug output, usually from the `UART0`, to the USB CDC.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
void setDebugOutput(bool);
|
||||
|
||||
enableReboot
|
||||
^^^^^^^^^^^^
|
||||
|
||||
This function enables the device to reboot by the DTR as RTS signals.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
void enableReboot(bool enable);
|
||||
|
||||
rebootEnabled
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
This function will return if the reboot is enabled.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
bool rebootEnabled(void);
|
||||
|
||||
Example Code
|
||||
------------
|
||||
|
||||
Here is an example of how to use the USB CDC.
|
||||
|
||||
USBSerial
|
||||
*********
|
||||
|
||||
.. literalinclude:: ../../../libraries/USB/examples/USBSerial/USBSerial.ino
|
||||
:language: arduino
|
||||
|
||||
.. _USB.org: https://www.usb.org/developers
|
114
docs/source/api/usb_msc.rst
Normal file
114
docs/source/api/usb_msc.rst
Normal file
@ -0,0 +1,114 @@
|
||||
#######
|
||||
USB MSC
|
||||
#######
|
||||
|
||||
About
|
||||
-----
|
||||
|
||||
USB Mass Storage Class API. This class makes the device accessible as a mass storage device and allows you to transfer data between the host and the device.
|
||||
|
||||
One of the examples for this mode is to flash the device by dropping the firmware binary like a flash memory device when connecting the ESP32 to the host computer.
|
||||
|
||||
APIs
|
||||
****
|
||||
|
||||
begin
|
||||
^^^^^
|
||||
|
||||
This function is used to start the peripheral using the default MSC configuration.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
bool begin(uint32_t block_count, uint16_t block_size);
|
||||
|
||||
Where:
|
||||
|
||||
* ``block_count`` set the disk sector count.
|
||||
* ``block_size`` set the disk sector size.
|
||||
|
||||
This function will return ``true`` if the configuration was successful.
|
||||
|
||||
end
|
||||
^^^
|
||||
|
||||
This function will finish the peripheral as MSC and release all the allocated resources. After calling ``end`` you need to use ``begin`` again in order to initialize the USB MSC driver again.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
void end();
|
||||
|
||||
vendorID
|
||||
^^^^^^^^
|
||||
|
||||
This function is used to define the vendor ID.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
void vendorID(const char * vid);//max 8 chars
|
||||
|
||||
productID
|
||||
^^^^^^^^^
|
||||
|
||||
This function is used to define the product ID.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
void productID(const char * pid);//max 16 chars
|
||||
|
||||
productRevision
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
This function is used to define the product revision.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
void productRevision(const char * ver);//max 4 chars
|
||||
|
||||
mediaPresent
|
||||
^^^^^^^^^^^^
|
||||
|
||||
Set the ``mediaPresent`` configuration.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
void mediaPresent(bool media_present);
|
||||
|
||||
onStartStop
|
||||
^^^^^^^^^^^
|
||||
|
||||
Set the ``onStartStop`` callback function.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
void onStartStop(msc_start_stop_cb cb);
|
||||
|
||||
onRead
|
||||
^^^^^^
|
||||
|
||||
Set the ``onRead`` callback function.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
void onRead(msc_read_cb cb);
|
||||
|
||||
onWrite
|
||||
^^^^^^^
|
||||
|
||||
Set the ``onWrite`` callback function.
|
||||
|
||||
.. code-block:: arduino
|
||||
|
||||
void onWrite(msc_write_cb cb);
|
||||
|
||||
Example Code
|
||||
------------
|
||||
|
||||
Here is an example of how to use the USB MSC.
|
||||
|
||||
FirmwareMSC
|
||||
***********
|
||||
|
||||
.. literalinclude:: ../../../libraries/USB/examples/FirmwareMSC/FirmwareMSC.ino
|
||||
:language: arduino
|
||||
|
||||
.. _USB.org: https://www.usb.org/developers
|
@ -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,
|
||||
|
@ -5,13 +5,15 @@ Arduino as a ESP-IDF component
|
||||
ESP32 Arduino lib-builder
|
||||
-------------------------
|
||||
|
||||
For a simplified method, see `lib-builder <lib_builder>`_.
|
||||
For a simplified method, see `lib-builder <https://github.com/espressif/esp32-arduino-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).
|
||||
- Create blank ESP-IDF project (use sample_project from /examples/get-started) or choose one of the examples.
|
||||
- In the project folder, create a new folder called `components` and clone this repository inside the new created folder.
|
||||
|
||||
.. code-block:: bash
|
||||
@ -24,10 +26,19 @@ Installation
|
||||
cd ../.. && \
|
||||
idf.py menuconfig
|
||||
|
||||
Option 1. Using Arduino setup() and loop()
|
||||
******************************************
|
||||
|
||||
- The `idf.py menuconfig` has some Arduino options.
|
||||
- On `Autostart Arduino setup and loop on boot`.
|
||||
- If you enable these options, your main.cpp should be formated like any other sketch.
|
||||
- Turn on `Autostart Arduino setup and loop on boot`.
|
||||
- In main folder rename file `main.c` to `main.cpp`.
|
||||
- In main folder open file `CMakeList.txt` and change `main.c` to `main.cpp` as described below.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
idf_component_register(SRCS "main.cpp" INCLUDE_DIRS ".")
|
||||
|
||||
- Your main.cpp should be formated like any other sketch.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
@ -43,14 +54,17 @@ Installation
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
- Otherwise, you need to implement ```app_main()``` and call ```initArduino();``` in it.
|
||||
Option 2. Using ESP-IDF appmain()
|
||||
*********************************
|
||||
|
||||
- You need to implement ``app_main()`` and call ``initArduino();`` in it.
|
||||
|
||||
Keep in mind that setup() and loop() will not be called in this case.
|
||||
If you plan to base your code on examples provided in `examples <https://github.com/espressif/esp-idf/tree/master/examples>`_, please make sure to move the app_main() function in main.cpp from the files in the example.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
//file: main.cpp
|
||||
//file: main.c or main.cpp
|
||||
#include "Arduino.h"
|
||||
|
||||
extern "C" void app_main()
|
||||
@ -67,7 +81,11 @@ If you plan to base your code on examples provided in `examples <https://github.
|
||||
- If enabled, WiFi will start with the last known configuration
|
||||
- Otherwise it will wait for WiFi.begin
|
||||
|
||||
- ```idf.py -p <your-board-serial-port> flash monitor``` will build, upload and open serial monitor to your board
|
||||
Build, flash and monitor
|
||||
************************
|
||||
|
||||
- For both options use command ``idf.py -p <your-board-serial-port> flash monitor``
|
||||
- It will build, upload and open serial monitor to your board.
|
||||
|
||||
Logging To Serial
|
||||
-----------------
|
||||
|
@ -35,9 +35,9 @@ 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-S3 No No Not Available Yet
|
||||
ESP32-S2 Yes Yes `ESP32-S2 Datasheet`_
|
||||
ESP32-C3 Yes Yes `ESP32-C3 Datasheet`_
|
||||
ESP32-S3 No No `ESP32-S3 Datasheet`_
|
||||
======== ====== =========== ===================================
|
||||
|
||||
See `Boards <boards/boards.html>`_ for more details about ESP32 development boards.
|
||||
@ -122,6 +122,7 @@ 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
|
||||
.. _ESP32-S3 Datasheet: https://www.espressif.com/sites/default/files/documentation/esp32-s3_datasheet_en.pdf
|
||||
.. _Arduino.cc: https://www.arduino.cc/en/Main/Software
|
||||
.. _Arduino Reference: https://www.arduino.cc/reference/en/
|
||||
.. _ESP32 Forum: https://esp32.com
|
||||
|
@ -29,7 +29,7 @@ This is the preferred and easiest way to install Arduino-ESP32.
|
||||
|
||||
.. note::
|
||||
Starting with the Arduino IDE version 1.6.4, Arduino allows installation of third-party platform
|
||||
packages using Boards Manager. We have packages available for Windows, macOS, and Linux (x86, amd64, armhf and arm64).
|
||||
packages using Boards Manager. We have packages available for Windows, macOS, and Linux.
|
||||
|
||||
To start the installation process using the Boards Managaer, follow these steps:
|
||||
|
||||
@ -61,10 +61,11 @@ To start the installation process using the Boards Managaer, follow these steps:
|
||||
Windows
|
||||
-------
|
||||
|
||||
Steps to install Arduino ESP32 support on Windows:
|
||||
.. warning:: Arduino ESP32 core v2.x.x cannot be used on Windows 8.x x86 (32 bits), Windows 7 or earlier. The Windows 32 bits OS is no longer supported by this toolchain.
|
||||
|
||||
.. note::
|
||||
Tested on Windows 10 32 and 64 bit machines.
|
||||
The Arduino ESP32 v1.0.6 still works on WIN32. You might want to install python 3.8.x because it is the latest release supported by Windows 7.
|
||||
|
||||
Steps to install Arduino ESP32 support on Windows:
|
||||
|
||||
**Step 1**
|
||||
|
||||
@ -166,9 +167,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 +180,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 +200,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 +223,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 +239,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,89 @@
|
||||
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>
|
||||
USB <api/usb.rst>
|
||||
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));
|
||||
|
@ -26,6 +26,7 @@
|
||||
#define HID_CARD_READER 0x03C6
|
||||
#define HID_DIGITAL_PEN 0x03C7
|
||||
#define HID_BARCODE 0x03C8
|
||||
#define HID_BRAILLE_DISPLAY 0x03C9
|
||||
|
||||
class BLEHIDDevice {
|
||||
public:
|
||||
|
@ -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
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Author: Thomas M. (ArcticSnowSky)
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BLUEDROID_ENABLED)
|
||||
|
||||
#include "BTAddress.h"
|
||||
#include <string>
|
||||
|
@ -10,7 +10,7 @@
|
||||
#ifndef COMPONENTS_CPP_UTILS_BTADDRESS_H_
|
||||
#define COMPONENTS_CPP_UTILS_BTADDRESS_H_
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BLUEDROID_ENABLED)
|
||||
#include <esp_gap_bt_api.h> // ESP32 BT
|
||||
#include <string>
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BLUEDROID_ENABLED)
|
||||
|
||||
//#include <map>
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user