Moved files into separate folders per 'en' and 'zh_CN' language version and linked 'zh_CN' files back to 'en' files if translation is not yet available

This commit is contained in:
krzychb
2018-02-03 22:12:13 +01:00
parent c8d8cdbf38
commit 097adc3a33
267 changed files with 754 additions and 481 deletions
+115
View File
@@ -0,0 +1,115 @@
Copyrights and Licenses
***********************
Software Copyrights
===================
All original source code in this repository is Copyright (C) 2015-2016
Espressif Systems. This source code is licensed under the Apache
License 2.0 as described in the file LICENSE.
Additional third party copyrighted code is included under the following licenses:
* Newlib_ (components/newlib) is licensed under the BSD License and is Copyright of various parties, as described in the file components/newlib/COPYING.NEWLIB.
* Xtensa header files (components/esp32/include/xtensa) are Copyright (C) 2013 Tensilica Inc and are licensed under the MIT License as reproduce in the individual header files.
* `esptool.py`_ (components/esptool_py/esptool) is Copyright (C) 2014-2016 Fredrik Ahlberg, Angus Gratton and is licensed under the GNU General Public License v2, as described in the file components/esptool_py/LICENSE.
* Original parts of FreeRTOS_ (components/freertos) are Copyright (C) 2015 Real Time Engineers Ltd and is licensed under the GNU General Public License V2 with the FreeRTOS Linking Exception, as described in the file components/freertos/license.txt.
* Original parts of LWIP_ (components/lwip) are Copyright (C) 2001, 2002 Swedish Institute of Computer Science and are licensed under the BSD License as described in the file components/lwip/COPYING.
* KConfig (tools/kconfig) is Copyright (C) 2002 Roman Zippel and others, and is licensed under the GNU General Public License V2.
* `wpa_supplicant`_ Copyright (c) 2003-2005 Jouni Malinen and licensed under the BSD license.
* `FreeBSD net80211`_ Copyright (c) 2004-2008 Sam Leffler, Errno Consulting and licensed under the BSD license.
* `JSMN`_ JSON Parser (components/jsmn) Copyright (c) 2010 Serge A. Zaitsev and licensed under the MIT license.
* `argtable3`_ arugment parsing library Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann and licensed under 3-clause BSD license.
* `linenoise`_ line editing library Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>, Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>, licensed under 2-clause BSD license.
Where source code headers specify Copyright & License information, this information takes precedence over the summaries made here.
ROM Source Code Copyrights
==========================
ESP32 mask ROM hardware includes binaries compiled from portions of the following third party software:
* Newlib_, as licensed under the BSD License and Copyright of various parties, as described in the file components/newlib/COPYING.NEWLIB.
* Xtensa libhal, Copyright (c) Tensilica Inc and licensed under the MIT license (see below).
* TinyBasic_ Plus, Copyright Mike Field & Scott Lawrence and licensed under the MIT license (see below).
* miniz_, by Rich Geldreich - placed into the public domain.
* `wpa_supplicant`_ Copyright (c) 2003-2005 Jouni Malinen and licensed under the BSD license.
* TJpgDec_ Copyright (C) 2011, ChaN, all right reserved. See below for license.
Xtensa libhal MIT License
=========================
Copyright (c) 2003, 2006, 2010 Tensilica Inc.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
TinyBasic Plus MIT License
==========================
Copyright (c) 2012-2013
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
TJpgDec License
===============
TJpgDec - Tiny JPEG Decompressor R0.01 (C)ChaN, 2011
The TJpgDec is a generic JPEG decompressor module for tiny embedded systems.
This is a free software that opened for education, research and commercial
developments under license policy of following terms.
Copyright (C) 2011, ChaN, all right reserved.
* The TJpgDec module is a free software and there is NO WARRANTY.
* No restriction on use. You can use, modify and redistribute it for personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.
* Redistributions of source code must retain the above copyright notice.
.. _Newlib: https://sourceware.org/newlib/
.. _FreeRTOS: http://freertos.org/
.. _esptool.py: https://github.com/espressif/esptool
.. _LWIP: https://savannah.nongnu.org/projects/lwip/
.. _TinyBasic: https://github.com/BleuLlama/TinyBasicPlus
.. _miniz: https://code.google.com/archive/p/miniz/
.. _wpa_supplicant: https://w1.fi/wpa_supplicant/
.. _FreeBSD net80211: https://github.com/freebsd/freebsd/tree/master/sys/net80211
.. _TJpgDec: http://elm-chan.org/fsw/tjpgd/00index.html
.. _JSMN: http://zserge.com/jsmn.html
.. _argtable3: https://github.com/argtable/argtable3
.. _linenoise: https://github.com/antirez/linenoise
+201
View File
@@ -0,0 +1,201 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# User-friendly check for sphinx-build
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
endif
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) -w sphinx-warning-log.txt .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " xml to make Docutils-native XML files"
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/ReadtheDocsTemplate.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/ReadtheDocsTemplate.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/ReadtheDocsTemplate"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/ReadtheDocsTemplate"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
latexpdfja:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through platex and dvipdfmx..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
gh-linkcheck:
@echo "Checking for hardcoded GitHub links"
@if (find ../ -name '*.rst' | xargs grep \
'https://github.com/espressif/esp-idf/tree\|https://github.com/espressif/esp-idf/blob\|https://github.com/espressif/esp-idf/raw'\
); \
then \
echo "WARNINIG: Some .rst files contain hardcoded Github links."; \
echo "Please check above output and replace links with one of the following:"; \
echo "- :idf:\`dir\` - points to directory inside ESP-IDF"; \
echo "- :idf_file:\`file\` - points to file inside ESP-IDF"; \
echo "- :idf_raw:\`file\` - points to raw view of the file inside ESP-IDF"; \
echo "- :component:\`dir\` - points to directory inside ESP-IDF components dir"; \
echo "- :component_file:\`file\` - points to file inside ESP-IDF components dir"; \
echo "- :component_raw:\`file\` - points to raw view of the file inside ESP-IDF"; \
echo " components dir"; \
echo "- :example:\`dir\` - points to directory inside ESP-IDF examples dir"; \
echo "- :example_file:\`file\` - points to file inside ESP-IDF examples dir"; \
echo "- :example_raw:\`file\` - points to raw view of the file inside ESP-IDF"; \
echo " examples dir"; \
echo "These link types will point to the correct GitHub version automatically"; \
exit 1; \
fi
@echo "No hardcoded links found"
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
xml:
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
@echo
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
+16
View File
@@ -0,0 +1,16 @@
About
=====
This is documentation of `ESP-IDF <https://github.com/espressif/esp-idf>`_, the framework to develop applications for `ESP32 <https://espressif.com/en/products/hardware/esp32/overview>`_ chip by `Espressif <https://espressif.com>`_.
The ESP32 is 2.4 GHz Wi-Fi and Bluetooth combo, 32 bit dual core chip with 600 DMIPS processing power.
.. figure:: _static/about-doc.png
:align: center
:alt: Espressif IoT Integrated Development Framework
:figclass: align-center
Espressif IoT Integrated Development Framework
The ESP-IDF, Espressif IoT Integrated Development Framework, provides toolchain, API, components and workflows to develop applications for ESP32 using Windows, Linux and Mac OS operating systems.
+104
View File
@@ -0,0 +1,104 @@
128 vTaskAllocateMPURegions xTask=%t pxRegions=%u
33 vTaskDelete xTaskToDelete=%t
34 vTaskDelay xTicksToDelay=%u
35 vTaskDelayUntil
129 uxTaskPriorityGet xTask=%t
56 uxTaskPriorityGetFromISR xTask=%t
130 eTaskGetState xTask=%t
55 vTaskPrioritySet xTask=%t uxNewPriority=%u
36 vTaskSuspend xTaskToSuspend=%t
40 vTaskResume xTaskToResume=%t
43 xTaskResumeFromISR xTaskToResume=%t
131 vTaskStartScheduler
132 vTaskEndScheduler
133 vTaskSuspendAll
134 xTaskResumeAll
135 xTaskGetTickCount
57 xTaskGetTickCountFromISR
136 uxTaskGetNumberOfTasks
137 pcTaskGetTaskName xTaskToQuery=%t
138 uxTaskGetStackHighWaterMark xTask=%t
139 vTaskSetApplicationTaskTag xTask=%t pxHookFunction=%u
140 xTaskGetApplicationTaskTag xTask=%t
141 vTaskSetThreadLocalStoragePointer xTaskToSet=%T xIndex=%u pvValue=%u
142 pvTaskGetThreadLocalStoragePointer xTaskToQuery=%T xIndex=%u
143 xTaskCallApplicationTaskHook xTask=%T pvParameter=%u
144 xTaskGetIdleTaskHandle
145 uxTaskGetSystemState pxTaskStatusArray=%u uxArraySize=%u pulTotalRunTime=%u
146 vTaskList pcWriteBuffer=%u
147 vTaskGetRunTimeStats pcWriteBuffer=%u
44 xTaskGenericNotify xTaskToNotify=%t ulValue=%u eAction=%u pulPreviousNotificationValue=%u
45 xTaskGenericNotifyFromISR xTaskToNotify=%t ulValue=%u eAction=%u pulPreviousNotificationValue=%u pxHigherPriorityTaskWoken=%u
46 xTaskNotifyWait ulBitsToClearOnEntry=%u ulBitsToClearOnExit=%u pulNotificationValue=%u xTicksToWait=%u
38 vTaskNotifyGiveFromISR xTaskToNotify=%t pxHigherPriorityTaskWoken=%u
37 ulTaskNotifyTake xClearCountOnExit=%u xTicksToWait=%u
148 xTaskNotifyStateClear xTask=%t
149 xTaskGetCurrentTaskHandle
150 vTaskSetTimeOutState pxTimeOut=%u
151 xTaskCheckForTimeOut pxTimeOut=%u pxTicksToWait=%u
152 vTaskMissedYield
153 xTaskGetSchedulerState
39 vTaskPriorityInherit pxMutexHolder=%p
42 xTaskPriorityDisinherit pxMutexHolder=%p
154 xTaskGenericCreate pxTaskCode=%u pcName=%u usStackDepth=%u pvParameters=%u uxPriority=%u pxCreatedTask=%u puxStackBuffer=%u xRegions=%u
155 uxTaskGetTaskNumber xTask=%u
156 vTaskSetTaskNumber xTask=%u uxHandle=%u
41 vTaskStepTick xTicksToJump=%u
157 eTaskConfirmSleepModeStatus
158 xTimerCreate pcTimerName=%u xTimerPeriodInTicks=%u uxAutoReload=%u pvTimerID=%u pxCallbackFunction=%u
159 pvTimerGetTimerID xTimer=%u
160 vTimerSetTimerID xTimer=%u pvNewID=%u
161 xTimerIsTimerActive xTimer=%u
162 xTimerGetTimerDaemonTaskHandle
163 xTimerPendFunctionCallFromISR xFunctionToPend=%u pvParameter1=%u ulParameter2=%u pxHigherPriorityTaskWoken=%u
164 xTimerPendFunctionCall xFunctionToPend=%u pvParameter1=%u ulParameter2=%u xTicksToWait=%u
165 pcTimerGetTimerName xTimer=%u
166 xTimerCreateTimerTask
167 xTimerGenericCommand xTimer=%u xCommandID=%u xOptionalValue=%u pxHigherPriorityTaskWoken=%u xTicksToWait=%u
53 xQueueGenericSend xQueue=%I pvItemToQueue=%p xTicksToWait=%u xCopyPosition=%u
50 xQueuePeekFromISR xQueue=%I pvBuffer=%p
49 xQueueGenericReceive xQueue=%I pvBuffer=%p xTicksToWait=%u xJustPeek=%u
168 uxQueueMessagesWaiting xQueue=%I
169 uxQueueSpacesAvailable xQueue=%I
48 vQueueDelete xQueue=%I
54 xQueueGenericSendFromISR xQueue=%I pvItemToQueue=%p pxHigherPriorityTaskWoken=%u xCopyPosition=%u
61 xQueueGiveFromISR xQueue=%I pxHigherPriorityTaskWoken=%u
51 xQueueReceiveFromISR xQueue=%I pvBuffer=%p pxHigherPriorityTaskWoken=%u
62 xQueueIsQueueEmptyFromISR xQueue=%I
63 xQueueIsQueueFullFromISR xQueue=%I
170 uxQueueMessagesWaitingFromISR xQueue=%I
171 xQueueAltGenericSend xQueue=%I pvItemToQueue=%p xTicksToWait=%u xCopyPosition=%u
172 xQueueAltGenericReceive xQueue=%I pvBuffer=%p xTicksToWait=%u xJustPeeking=%u
173 xQueueCRSendFromISR xQueue=%I pvItemToQueue=%p xCoRoutinePreviouslyWoken=%u
174 xQueueCRReceiveFromISR xQueue=%I pvBuffer=%p pxTaskWoken=%u
175 xQueueCRSend xQueue=%I pvItemToQueue=%p xTicksToWait=%u
176 xQueueCRReceive xQueue=%I pvBuffer=%p xTicksToWait=%u
177 xQueueCreateMutex ucQueueType=%u
178 xQueueCreateCountingSemaphore uxMaxCount=%u uxInitialCount=%u
179 xQueueGetMutexHolder xSemaphore=%u
180 xQueueTakeMutexRecursive xMutex=%u xTicksToWait=%u
181 xQueueGiveMutexRecursive pxMutex=%u
52 vQueueAddToRegistry xQueue=%I pcName=%u
182 vQueueUnregisterQueue xQueue=%I
47 xQueueGenericCreate uxQueueLength=%u uxItemSize=%u ucQueueType=%u
183 xQueueCreateSet uxEventQueueLength=%u
184 xQueueAddToSet xQueueOrSemaphore=%u xQueueSet=%u
185 xQueueRemoveFromSet xQueueOrSemaphore=%u xQueueSet=%u
186 xQueueSelectFromSet xQueueSet=%u xTicksToWait=%u
187 xQueueSelectFromSetFromISR xQueueSet=%u
188 xQueueGenericReset xQueue=%I xNewQueue=%u
189 vListInitialise pxList=%u
190 vListInitialiseItem pxItem=%u
191 vListInsert pxList=%u pxNewListItem=%u
192 vListInsertEnd pxList=%u pxNewListItem=%u
193 uxListRemove pxItemToRemove=%u
194 xEventGroupCreate
195 xEventGroupWaitBits xEventGroup=%u uxBitsToWaitFor=%u xClearOnExit=%u xWaitForAllBits=%u xTicksToWait=%u
196 xEventGroupClearBits xEventGroup=%u uxBitsToClear=%u
58 xEventGroupClearBitsFromISR xEventGroup=%u uxBitsToSet=%u
197 xEventGroupSetBits xEventGroup=%u uxBitsToSet=%u
59 xEventGroupSetBitsFromISR xEventGroup=%u uxBitsToSet=%u pxHigherPriorityTaskWoken=%u
198 xEventGroupSync xEventGroup=%u uxBitsToSet=%u uxBitsToWaitFor=%u xTicksToWait=%u
60 xEventGroupGetBitsFromISR xEventGroup=%u
199 vEventGroupDelete xEventGroup=%u
200 uxEventGroupGetNumber xEventGroup=%u
+436
View File
@@ -0,0 +1,436 @@
Application Level Tracing library
=================================
Overview
--------
IDF provides useful feature for program behaviour analysis: application level tracing. It is implemented in the corresponding library and can be enabled in menuconfig. This feature allows to transfer arbitrary data between host and ESP32 via JTAG interface with small overhead on program execution.
Developers can use this library to send application specific state of execution to the host and receive commands or other type of info in the opposite direction at runtime. The main use cases of this library are:
1. Collecting application specific data, see :ref:`app_trace-application-specific-tracing`
2. Lightweight logging to the host, see :ref:`app_trace-logging-to-host`
3. System behaviour analysis, see :ref:`app_trace-system-behaviour-analysis-with-segger-systemview`
Tracing components when working over JTAG interface are shown in the figure below.
.. figure:: ../_static/app_trace-overview.jpg
:align: center
:alt: Tracing Components when Working Over JTAG
:figclass: align-center
Tracing Components when Working Over JTAG
Modes of Operation
------------------
The library supports two modes of operation:
**Post-mortem mode**. This is the default mode. The mode does not need interaction from the host side. In this mode tracing module does not check whether host has read all the data from *HW UP BUFFER* buffer and overwrites old data with the new ones. This mode is useful when only the latest trace data are interesting to the user, e.g. for analyzing program's behaviour just before the crash. Host can read the data later on upon user request, e.g. via special OpenOCD command in case of working via JTAG interface.
**Streaming mode.** Tracing module enters this mode when host connects to ESP32. In this mode before writing new data to *HW UP BUFFER* tracing module checks that there is enough space in it and if necessary waits for the host to read data and free enough memory. Maximum waiting time is controled via timeout values passed by users to corresponding API routines. So when application tries to write data to trace buffer using finite value of the maximum waiting time it is possible situation that this data will be dropped. Especially this is true for tracing from time critical code (ISRs, OS scheduler code etc.) when infinite timeouts can lead to system malfunction. In order to avoid loss of such critical data developers can enable additional data buffering via menuconfig option :ref:`CONFIG_ESP32_APPTRACE_PENDING_DATA_SIZE_MAX`. This macro specifies the size of data which can be buffered in above conditions. The option can also help to overcome situation when data transfer to the host is temporarily slowed down, e.g due to USB bus congestions etc. But it will not help when average bitrate of trace data stream exceeds HW interface capabilities.
Configuration Options and Dependencies
--------------------------------------
Using of this feature depends on two components:
1. **Host side:** Application tracing is done over JTAG, so it needs OpenOCD to be set up and running on host machine. For instructions how to set it up, please, see :doc:`JTAG Debugging <../api-guides/jtag-debugging/index>` for details.
2. **Target side:** Application tracing functionality can be enabled in menuconfig. *Component config > Application Level Tracing* menu allows selecting destination for the trace data (HW interface for transport). Choosing any of the destinations automatically enables ``CONFIG_ESP32_APPTRACE_ENABLE`` option.
.. note::
In order to achieve higher data rates and minimize number of dropped packets it is recommended to optimize setting of JTAG clock frequency, so it is at maximum and still provides stable operation of JTAG, see :ref:`jtag-debugging-tip-optimize-jtag-speed`.
There are two additional menuconfig options not mentioned above:
1. *Threshold for flushing last trace data to host on panic* (:ref:`CONFIG_ESP32_APPTRACE_POSTMORTEM_FLUSH_TRAX_THRESH`). This option is necessary due to the nature of working over JTAG. In that mode trace data are exposed to the host in 16KB blocks. In post-mortem mode when one block is filled it is exposed to the host and the previous one becomes unavailable. In other words trace data are overwritten in 16KB granularity. On panic the latest data from the current input block are exposed to host and host can read them for post-analysis. It can happen that system panic occurs when there are very small amount of data which are not exposed to the host yet. In this case the previous 16KB of collected data will be lost and host will see the latest, but very small piece of the trace. It can be insufficient to diagnose the problem. This menuconfig option allows avoiding such situations. It controls the threshold for flushing data in case of panic. For example user can decide that it needs not less then 512 bytes of the recent trace data, so if there is less then 512 bytes of pending data at the moment of panic they will not be flushed and will not overwrite previous 16KB. The option is only meaningful in post-mortem mode and when working over JTAG.
2. *Timeout for flushing last trace data to host on panic* (:ref:`CONFIG_ESP32_APPTRACE_ONPANIC_HOST_FLUSH_TMO`). The option is only meaningful in streaming mode and controls the maximum time tracing module will wait for the host to read the last data in case of panic.
How to use this library
-----------------------
This library provides API for transferring arbitrary data between host and ESP32. When enabled in menuconfig target application tracing module is initialized automatically at the system startup, so all what the user needs to do is to call corresponding API to send, receive or flush the data.
.. _app_trace-application-specific-tracing:
Application Specific Tracing
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In general user should decide what type of data should be transferred in every direction and how these data must be interpreted (processed). The following steps must be performed to transfer data between target and host:
1. On target side user should implement algorithms for writing trace data to the host. Piece of code below shows an example how to do this.
.. code-block:: c
#include "esp_app_trace.h"
...
char buf[] = "Hello World!";
esp_err_t res = esp_apptrace_write(ESP_APPTRACE_DEST_TRAX, buf, strlen(buf), ESP_APPTRACE_TMO_INFINITE);
if (res != ESP_OK) {
ESP_LOGE(TAG, "Failed to write data to host!");
return res;
}
``esp_apptrace_write()`` function uses memcpy to copy user data to the internal buffer. In some cases it can be more optimal to use ``esp_apptrace_buffer_get()`` and ``esp_apptrace_buffer_put()`` functions. They allow developers to allocate buffer and fill it themselves. The following piece of code shows how to do this.
.. code-block:: c
#include "esp_app_trace.h"
...
int number = 10;
char *ptr = (char *)esp_apptrace_buffer_get(ESP_APPTRACE_DEST_TRAX, 32, 100/*tmo in us*/);
if (ptr == NULL) {
ESP_LOGE("Failed to get buffer!");
return ESP_FAIL;
}
sprintf(ptr, "Here is the number %d", number);
esp_err_t res = esp_apptrace_buffer_put(ESP_APPTRACE_DEST_TRAX, ptr, 100/*tmo in us*/);
if (res != ESP_OK) {
/* in case of error host tracing tool (e.g. OpenOCD) will report incomplete user buffer */
ESP_LOGE("Failed to put buffer!");
return res;
}
Also according to his needs user may want to receive data from the host. Piece of code below shows an example how to do this.
.. code-block:: c
#include "esp_app_trace.h"
...
char buf[32];
char down_buf[32];
size_t sz = sizeof(buf);
/* config down buffer */
esp_apptrace_down_buffer_config(down_buf, sizeof(down_buf));
/* check for incoming data and read them if any */
esp_err_t res = esp_apptrace_read(ESP_APPTRACE_DEST_TRAX, buf, &sz, 0/*do not wait*/);
if (res != ESP_OK) {
ESP_LOGE(TAG, "Failed to read data from host!");
return res;
}
if (sz > 0) {
/* we have data, process them */
...
}
``esp_apptrace_read()`` function uses memcpy to copy host data to user buffer. In some cases it can be more optimal to use ``esp_apptrace_down_buffer_get()`` and ``esp_apptrace_down_buffer_put()`` functions. They allow developers to occupy chunk of read buffer and process it in-place. The following piece of code shows how to do this.
.. code-block:: c
#include "esp_app_trace.h"
...
char down_buf[32];
uint32_t *number;
size_t sz = 32;
/* config down buffer */
esp_apptrace_down_buffer_config(down_buf, sizeof(down_buf));
char *ptr = (char *)esp_apptrace_down_buffer_get(ESP_APPTRACE_DEST_TRAX, &sz, 100/*tmo in us*/);
if (ptr == NULL) {
ESP_LOGE("Failed to get buffer!");
return ESP_FAIL;
}
if (sz > 4) {
number = (uint32_t *)ptr;
printf("Here is the number %d", *number);
} else {
printf("No data");
}
esp_err_t res = esp_apptrace_down_buffer_put(ESP_APPTRACE_DEST_TRAX, ptr, 100/*tmo in us*/);
if (res != ESP_OK) {
/* in case of error host tracing tool (e.g. OpenOCD) will report incomplete user buffer */
ESP_LOGE("Failed to put buffer!");
return res;
}
2. The next step is to build the program image and download it to the target as described in :doc:`Build and Flash <../get-started/make-project>`.
3. Run OpenOCD (see :doc:`JTAG Debugging <../api-guides/jtag-debugging/index>`).
4. Connect to OpenOCD telnet server. It can be done using the following command in terminal ``telnet <oocd_host> 4444``. If telnet session is opened on the same machine which runs OpenOCD you can use ``localhost`` as ``<oocd_host>`` in the command above.
5. Start trace data collection using special OpenOCD command. This command will transfer tracing data and redirect them to specified file or socket (currently only files are supported as trace data destination). For description of the corresponding commands see `OpenOCD Application Level Tracing Commands`_.
6. The final step is to process received data. Since format of data is defined by user the processing stage is out of the scope of this document. Good starting points for data processor are python scripts in ``$IDF_PATH/tools/esp_app_trace``: ``apptrace_proc.py`` (used for feature tests) and ``logtrace_proc.py`` (see more details in section `Logging to Host`_).
OpenOCD Application Level Tracing Commands
""""""""""""""""""""""""""""""""""""""""""
*HW UP BUFFER* is shared between user data blocks and filling of the allocated memory is performed on behalf of the API caller (in task or ISR context). In multithreading environment it can happen that task/ISR which fills the buffer is preempted by another high priority task/ISR. So it is possible situation that user data preparation process is not completed at the moment when that chunk is read by the host. To handle such conditions tracing module prepends all user data chunks with header which contains allocated user buffer size (2 bytes) and length of actually written data (2 bytes). So total length of the header is 4 bytes. OpenOCD command which reads trace data reports error when it reads incomplete user data chunk, but in any case it puts contents of the whole user chunk (including unfilled area) to output file.
Below is the description of available OpenOCD application tracing commands.
.. note::
Currently OpenOCD does not provide commands to send arbitrary user data to the target.
Command usage:
``esp32 apptrace [start <options>] | [stop] | [status] | [dump <cores_num> <outfile>]``
Sub-commands:
``start``
Start tracing (continuous streaming).
``stop``
Stop tracing.
``status``
Get tracing status.
``dump``
Dump all data from (post-mortem dump).
Start command syntax:
``start <outfile> [poll_period [trace_size [stop_tmo [wait4halt [skip_size]]]]``
``outfile``
Path to file to save data from both CPUs. This argument should have the following format: ``file://path/to/file``.
``poll_period``
Data polling period (in ms) for available trace data. If greater then 0 then command runs in non-blocking mode. By default 1 ms.
``trace_size``
Maximum size of data to collect (in bytes). Tracing is stopped after specified amount of data is received. By default -1 (trace size stop trigger is disabled).
``stop_tmo``
Idle timeout (in sec). Tracing is stopped if there is no data for specified period of time. By default -1 (disable this stop trigger). Optionally set it to value longer than longest pause between tracing commands from target.
``wait4halt``
If 0 start tracing immediately, otherwise command waits for the target to be halted (after reset, by breakpoint etc.) and then automatically resumes it and starts tracing. By default 0.
``skip_size``
Number of bytes to skip at the start. By default 0.
.. note::
If ``poll_period`` is 0, OpenOCD telnet command line will not be available until tracing is stopped. You must stop it manually by resetting the board or pressing Ctrl+C in OpenOCD window (not one with the telnet session). Another option is to set ``trace_size`` and wait until this size of data is collected. At this point tracing stops automatically.
Command usage examples:
.. highlight:: none
1. Collect 2048 bytes of tracing data to a file "trace.log". The file will be saved in "openocd-esp32" directory.
::
esp32 apptrace start file://trace.log 1 2048 5 0 0
The tracing data will be retrieved and saved in non-blocking mode. This process will stop automatically after 2048 bytes are collected, or if no data are available for more than 5 seconds.
.. note::
Tracing data is buffered before it is made available to OpenOCD. If you see "Data timeout!" message, then the target is likely sending not enough data to empty the buffer to OpenOCD before expiration of timeout. Either increase the timeout or use a function ``esp_apptrace_flush()`` to flush the data on specific intervals.
2. Retrieve tracing data indefinitely in non-blocking mode.
::
esp32 apptrace start file://trace.log 1 -1 -1 0 0
There is no limitation on the size of collected data and there is no any data timeout set. This process may be stopped by issuing ``esp32 apptrace stop`` command on OpenOCD telnet prompt, or by pressing Ctrl+C in OpenOCD window.
3. Retrieve tracing data and save them indefinitely.
::
esp32 apptrace start file://trace.log 0 -1 -1 0 0
OpenOCD telnet command line prompt will not be available until tracing is stopped. To stop tracing press Ctrl+C in OpenOCD window.
4. Wait for target to be halted. Then resume target's operation and start data retrieval. Stop after collecting 2048 bytes of data:
::
esp32 apptrace start file://trace.log 0 2048 -1 1 0
To configure tracing immediately after reset use the openocd ``reset halt`` command.
.. _app_trace-logging-to-host:
Logging to Host
^^^^^^^^^^^^^^^
IDF implements useful feature: logging to host via application level tracing library. This is a kind of semihosting when all ESP_LOGx calls sends strings to be printed to the host instead of UART. This can be useful because "printing to host" eliminates some steps performed when logging to UART. The most part of work is done on the host.
By default IDF's logging library uses vprintf-like function to write formatted output to dedicated UART. In general it involves the following steps:
1. Format string is parsed to obtain type of each argument.
2. According to its type every argument is converted to string representation.
3. Format string combined with converted arguments is sent to UART.
Though implementation of vprintf-like function can be optimised to a certain level, all steps above have to be performed in any case and every step takes some time (especially item 3). So it is frequent situation when addition of extra logging to the program to diagnose some problem changes its behaviour and problem disappears or in the worst cases program can not work normally at all and ends up with an error or even hangs.
Possible ways to overcome this problem are to use higher UART bitrates (or another faster interface) and/or move string formatting procedure to the host.
Application level tracing feature can be used to transfer log information to host using ``esp_apptrace_vprintf`` function. This function does not perform full parsing of the format string and arguments, instead it just calculates number of arguments passed and sends them along with the format string address to the host. On the host log data are processed and printed out by a special Python script.
Limitations
"""""""""""
Current implementation of logging over JTAG has some limitations:
1. Tracing from ``ESP_EARLY_LOGx`` macros is not supported.
2. No support for printf arguments which size exceeds 4 bytes (e.g. ``double`` and ``uint64_t``).
3. Only strings from .rodata section are supported as format strings and arguments.
4. Maximum number of printf arguments is 256.
How To Use It
"""""""""""""
In order to use logging via trace module user needs to perform the following steps:
1. On target side special vprintf-like function needs to be installed. As it was mentioned earlier this function is ``esp_apptrace_vprintf``. It sends log data to the host. Example code is provided in :example:`system/app_trace_to_host`.
2. Follow instructions in items 2-5 in `Application Specific Tracing`_.
3. To print out collected log records, run the following command in terminal: ``$IDF_PATH/tools/esp_app_trace/logtrace_proc.py /path/to/trace/file /path/to/program/elf/file``.
Log Trace Processor Command Options
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Command usage:
``logtrace_proc.py [-h] [--no-errors] <trace_file> <elf_file>``
Positional arguments:
``trace_file``
Path to log trace file
``elf_file``
Path to program ELF file
Optional arguments:
``-h``, ``--help``
show this help message and exit
``--no-errors``, ``-n``
Do not print errors
.. _app_trace-system-behaviour-analysis-with-segger-systemview:
System Behaviour Analysis with SEGGER SystemView
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Another useful IDF feature built on top of application tracing library is the system level tracing which produces traces compatible with SEGGER SystemView tool (see `SystemView <https://www.segger.com/products/development-tools/systemview/>`_). SEGGER SystemView is a real-time recording and visualization tool that allows to analyze runtime behavior of an application.
.. note::
Currently IDF-based application is able to generate SystemView compatible traces in form of files to be opened in SystemView application. The tracing process can not yet be controlled using that tool.
How To Use It
"""""""""""""
Support for this feature is enabled by *Component config > Application Level Tracing > FreeRTOS SystemView Tracing* (:ref:`CONFIG_SYSVIEW_ENABLE`) menuconfig option. There are several other options enabled under the same menu:
1. *ESP32 timer to use as SystemView timestamp source* (:ref:`CONFIG_SYSVIEW_TS_SOURCE`) selects the source of timestamps for SystemView events. In single core mode timestamps are generated using ESP32 internal cycle counter running at maximum 240 Mhz (~4 ns granularity). In dual-core mode external timer working at 40Mhz is used, so timestamp granularity is 25 ns.
2. Individually enabled or disabled collection of SystemView events (``CONFIG_SYSVIEW_EVT_XXX``):
- Trace Buffer Overflow Event
- ISR Enter Event
- ISR Exit Event
- ISR Exit to Scheduler Event
- Task Start Execution Event
- Task Stop Execution Event
- Task Start Ready State Event
- Task Stop Ready State Event
- Task Create Event
- Task Terminate Event
- System Idle Event
- Timer Enter Event
- Timer Exit Event
IDF has all the code required to produce SystemView compatible traces, so user can just configure necessary project options (see above), build, download the image to target and use OpenOCD to collect data as described in the previous sections.
OpenOCD SystemView Tracing Command Options
""""""""""""""""""""""""""""""""""""""""""
Command usage:
``esp32 sysview [start <options>] | [stop] | [status]``
Sub-commands:
``start``
Start tracing (continuous streaming).
``stop``
Stop tracing.
``status``
Get tracing status.
Start command syntax:
``start <outfile1> [outfile2] [poll_period [trace_size [stop_tmo]]]``
``outfile1``
Path to file to save data from PRO CPU. This argument should have the following format: ``file://path/to/file``.
``outfile2``
Path to file to save data from APP CPU. This argument should have the following format: ``file://path/to/file``.
``poll_period``
Data polling period (in ms) for available trace data. If greater then 0 then command runs in non-blocking mode. By default 1 ms.
``trace_size``
Maximum size of data to collect (in bytes). Tracing is stopped after specified amount of data is received. By default -1 (trace size stop trigger is disabled).
``stop_tmo``
Idle timeout (in sec). Tracing is stopped if there is no data for specified period of time. By default -1 (disable this stop trigger).
.. note::
If ``poll_period`` is 0 OpenOCD telnet command line will not be available until tracing is stopped. You must stop it manually by resetting the board or pressing Ctrl+C in OpenOCD window (not one with the telnet session). Another option is to set ``trace_size`` and wait until this size of data is collected. At this point tracing stops automatically.
Command usage examples:
.. highlight:: none
1. Collect SystemView tracing data to files "pro-cpu.SVDat" and "pro-cpu.SVDat". The files will be saved in "openocd-esp32" directory.
::
esp32 sysview start file://pro-cpu.SVDat file://app-cpu.SVDat
The tracing data will be retrieved and saved in non-blocking mode. To stop data this process enter ``esp32 apptrace stop`` command on OpenOCD telnet prompt, Optionally pressing Ctrl+C in OpenOCD window.
2. Retrieve tracing data and save them indefinitely.
::
esp32 sysview start file://pro-cpu.SVDat file://app-cpu.SVDat 0 -1 -1
OpenOCD telnet command line prompt will not be available until tracing is stopped. To stop tracing, press Ctrl+C in OpenOCD window.
Data Visualization
""""""""""""""""""
After trace data are collected user can use special tool to visualize the results and inspect behaviour of the program. Unfortunately SystemView does not support tracing from multiple cores. So when tracing from ESP32 working in dual-core mode two files are generated: one for PRO CPU and another one for APP CPU. User can load every file into separate instance of the tool.
It is uneasy and awkward to analyze data for every core in separate instance of the tool. Fortunately there is Eclipse plugin called *Impulse* which can load several trace files and makes its possible to inspect events from both cores in one view. Also this plugin has no limitation of 1000000 events as compared to free version of SystemView.
Good instruction on how to install, configure and visualize data in Impulse from one core can be found `here <https://mcuoneclipse.com/2016/07/31/impulse-segger-systemview-in-eclipse/>`_.
.. note::
IDF uses its own mapping for SystemView FreeRTOS events IDs, so user needs to replace original file with mapping ``$SYSVIEW_INSTALL_DIR/Description/SYSVIEW_FreeRTOS.txt`` with ``$IDF_PATH/docs/api-guides/SYSVIEW_FreeRTOS.txt``.
Also contents of that IDF specific file should be used when configuring SystemView serializer using above link.
Configure Impulse for Dual Core Traces
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
After installing Impulse and ensuring that it can successfully load trace files for each core in separate tabs user can add special Multi Adapter port and load both files into one view. To do this user needs to do the following in Eclipse:
1. Open 'Signal Ports' view. Go to Windows->Show View->Other menu. Find 'Signal Ports' view in Impulse folder and double-click on it.
2. In 'Signal Ports' view right-click on 'Ports' and select 'Add ...'->New Multi Adapter Port
3. In open dialog Press 'Add' button and select 'New Pipe/File'.
4. In open dialog select 'SystemView Serializer' as Serializer and set path to PRO CPU trace file. Press OK.
5. Repeat steps 3-4 for APP CPU trace file.
6. Double-click on created port. View for this port should open.
7. Click Start/Stop Streaming button. Data should be loaded.
8. Use 'Zoom Out', 'Zoom In' and 'Zoom Fit' button to inspect data.
9. For settings measurement cursors and other features please see `Impulse documentation <http://toem.de/index.php/projects/impulse>`_).
.. note::
If you have problems with visualization (no data are shown or strange behaviour of zoom action is observed) you can try to delete current signal hierarchy and double click on necessary file or port. Eclipse will ask you to create new signal hierarchy.
+620
View File
@@ -0,0 +1,620 @@
Build System
************
This document explains the Espressif IoT Development Framework build system and the
concept of "components"
Read this document if you want to know how to organise a new ESP-IDF project.
We recommend using the esp-idf-template_ project as a starting point for your project.
Using the Build System
======================
The esp-idf README file contains a description of how to use the build system to build your project.
Overview
========
An ESP-IDF project can be seen as an amalgamation of a number of components.
For example, for a webserver that shows the current humidity, there could be:
- The ESP32 base libraries (libc, rom bindings etc)
- The WiFi drivers
- A TCP/IP stack
- The FreeRTOS operating system
- A webserver
- A driver for the humidity sensor
- Main code tying it all together
ESP-IDF makes these components explicit and configurable. To do that,
when a project is compiled, the build environment will look up all the
components in the ESP-IDF directories, the project directories and
(optionally) in additional custom component directories. It then
allows the user to configure the ESP-IDF project using a a text-based
menu system to customize each component. After the components in the
project are configured, the build process will compile the project.
Concepts
--------
- A "project" is a directory that contains all the files and configuration to build a single "app" (executable), as well as additional supporting output such as a partition table, data/filesystem partitions, and a bootloader.
- "Project configuration" is held in a single file called sdkconfig in the root directory of the project. This configuration file is modified via ``make menuconfig`` to customise the configuration of the project. A single project contains exactly one project configuration.
- An "app" is an executable which is built by esp-idf. A single project will usually build two apps - a "project app" (the main executable, ie your custom firmware) and a "bootloader app" (the initial bootloader program which launches the project app).
- "components" are modular pieces of standalone code which are compiled into static libraries (.a files) and linked into an app. Some are provided by esp-idf itself, others may be sourced from other places.
Some things are not part of the project:
- "ESP-IDF" is not part of the project. Instead it is standalone, and linked to the project via the ``IDF_PATH`` environment variable which holds the path of the ``esp-idf`` directory. This allows the IDF framework to be decoupled from your project.
- The toolchain for compilation is not part of the project. The toolchain should be installed in the system command line PATH, or the path to the toolchain can be set as part of the compiler prefix in the project configuration.
Example Project
---------------
An example project directory tree might look like this::
- myProject/
- Makefile
- sdkconfig
- components/ - component1/ - component.mk
- Kconfig
- src1.c
- component2/ - component.mk
- Kconfig
- src1.c
- include/ - component2.h
- main/ - src1.c
- src2.c
- component.mk
- build/
This example "myProject" contains the following elements:
- A top-level project Makefile. This Makefile set the ``PROJECT_NAME`` variable and (optionally) defines
other project-wide make variables. It includes the core ``$(IDF_PATH)/make/project.mk`` makefile which
implements the rest of the ESP-IDF build system.
- "sdkconfig" project configuration file. This file is created/updated when "make menuconfig" runs, and holds configuration for all of the components in the project (including esp-idf itself). The "sdkconfig" file may or may not be added to the source control system of the project.
- Optional "components" directory contains components that are part of the project. A project does not have to contain custom components of this kind, but it can be useful for structuring reusable code or including third party components that aren't part of ESP-IDF.
- "main" directory is a special "pseudo-component" that contains source code for the project itself. "main" is a default name, the Makefile variable ``COMPONENT_DIRS`` includes this component but you can modify this variable (or set ``EXTRA_COMPONENT_DIRS``) to look for components in other places.
- "build" directory is where build output is created. After the make process is run, this directory will contain interim object files and libraries as well as final binary output files. This directory is usually not added to source control or distributed with the project source code.
Component directories contain a component makefile - ``component.mk``. This may contain variable definitions
to control the build process of the component, and its integration into the overall project. See `Component Makefiles`_ for more details.
Each component may also include a ``Kconfig`` file defining the `component configuration` options that can be set via the project configuration. Some components may also include ``Kconfig.projbuild`` and ``Makefile.projbuild`` files, which are special files for `overriding parts of the project`.
Project Makefiles
-----------------
Each project has a single Makefile that contains build settings for the entire project. By default, the project Makefile can be quite minimal.
Minimal Example Makefile
^^^^^^^^^^^^^^^^^^^^^^^^
::
PROJECT_NAME := myProject
include $(IDF_PATH)/make/project.mk
Mandatory Project Variables
^^^^^^^^^^^^^^^^^^^^^^^^^^^
- ``PROJECT_NAME``: Name of the project. Binary output files will use this name - ie myProject.bin, myProject.elf.
Optional Project Variables
^^^^^^^^^^^^^^^^^^^^^^^^^^
These variables all have default values that can be overridden for custom behaviour. Look in ``make/project.mk`` for all of the implementation details.
- ``PROJECT_PATH``: Top-level project directory. Defaults to the directory containing the Makefile. Many other project variables are based on this variable. The project path cannot contain spaces.
- ``BUILD_DIR_BASE``: The build directory for all objects/libraries/binaries. Defaults to ``$(PROJECT_PATH)/build``.
- ``COMPONENT_DIRS``: Directories to search for components. Defaults to `$(IDF_PATH)/components`, `$(PROJECT_PATH)/components`, ``$(PROJECT_PATH)/main`` and ``EXTRA_COMPONENT_DIRS``. Override this variable if you don't want to search for components in these places.
- ``EXTRA_COMPONENT_DIRS``: Optional list of additional directories to search for components.
- ``COMPONENTS``: A list of component names to build into the project. Defaults to all components found in the COMPONENT_DIRS directories.
Any paths in these Makefile variables should be absolute paths. You can convert relative paths using ``$(PROJECT_PATH)/xxx``, ``$(IDF_PATH)/xxx``, or use the Make function ``$(abspath xxx)``.
These variables should all be set before the line ``include $(IDF_PATH)/make/project.mk`` in the Makefile.
Component Makefiles
-------------------
Each project contains one or more components, which can either be part of esp-idf or added from other component directories.
A component is any directory that contains a ``component.mk`` file.
Searching for Components
------------------------
The list of directories in ``COMPONENT_DIRS`` is searched for the project's components. Directories in this list can either be components themselves (ie they contain a `component.mk` file), or they can be top-level directories whose subdirectories are components.
Running the ``make list-components`` target dumps many of these variables and can help debug the discovery of component directories.
Multiple components with the same name
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When esp-idf is collecting all the components to compile, it will do this in the order specified by ``COMPONENT_DIRS``; by default, this means the
idf components first, the project components second and optionally the components in ``EXTRA_COMPONENT_DIRS`` last. If two or more of these directories
contain component subdirectories with the same name, the component in the last place searched is used. This allows, for example, overriding esp-idf components
with a modified version by simply copying the component from the esp-idf component directory to the project component tree and then modifying it there.
If used in this way, the esp-idf directory itself can remain untouched.
Minimal Component Makefile
^^^^^^^^^^^^^^^^^^^^^^^^^^
The minimal ``component.mk`` file is an empty file(!). If the file is empty, the default component behaviour is set:
- All source files in the same directory as the makefile (``*.c``, ``*.cpp``, ``*.cc``, ``*.S``) will be compiled into the component library
- A sub-directory "include" will be added to the global include search path for all other components.
- The component library will be linked into the project app.
See `example component makefiles`_ for more complete component makefile examples.
Note that there is a difference between an empty ``component.mk`` file (which invokes default component build behaviour) and no ``component.mk`` file (which means no default component build behaviour will occur.) It is possible for a component to have no `component.mk` file, if it only contains other files which influence the project configuration or build process.
.. component variables:
Preset Component Variables
^^^^^^^^^^^^^^^^^^^^^^^^^^
The following component-specific variables are available for use inside ``component.mk``, but should not be modified:
- ``COMPONENT_PATH``: The component directory. Evaluates to the absolute path of the directory containing ``component.mk``. The component path cannot contain spaces.
- ``COMPONENT_NAME``: Name of the component. Defaults to the name of the component directory.
- ``COMPONENT_BUILD_DIR``: The component build directory. Evaluates to the absolute path of a directory inside `$(BUILD_DIR_BASE)` where this component's source files are to be built. This is also the Current Working Directory any time the component is being built, so relative paths in make targets, etc. will be relative to this directory.
- ``COMPONENT_LIBRARY``: Name of the static library file (relative to the component build directory) that will be built for this component. Defaults to ``$(COMPONENT_NAME).a``.
The following variables are set at the project level, but exported for use in the component build:
- ``PROJECT_NAME``: Name of the project, as set in project Makefile
- ``PROJECT_PATH``: Absolute path of the project directory containing the project Makefile.
- ``COMPONENTS``: Name of all components that are included in this build.
- ``CONFIG_*``: Each value in the project configuration has a corresponding variable available in make. All names begin with ``CONFIG_``.
- ``CC``, ``LD``, ``AR``, ``OBJCOPY``: Full paths to each tool from the gcc xtensa cross-toolchain.
- ``HOSTCC``, ``HOSTLD``, ``HOSTAR``: Full names of each tool from the host native toolchain.
- ``IDF_VER``: Git version of ESP-IDF (produced by ``git describe``)
If you modify any of these variables inside ``component.mk`` then this will not prevent other components from building but it may make your component hard to build and/or debug.
Optional Project-Wide Component Variables
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The following variables can be set inside ``component.mk`` to control build settings across the entire project:
- ``COMPONENT_ADD_INCLUDEDIRS``: Paths, relative to the component
directory, which will be added to the include search path for
all components in the project. Defaults to ``include`` if not overridden. If an include directory is only needed to compile
this specific component, add it to ``COMPONENT_PRIV_INCLUDEDIRS`` instead.
- ``COMPONENT_ADD_LDFLAGS``: Add linker arguments to the LDFLAGS for
the app executable. Defaults to ``-l$(COMPONENT_NAME)``. If
adding pre-compiled libraries to this directory, add them as
absolute paths - ie $(COMPONENT_PATH)/libwhatever.a
- ``COMPONENT_DEPENDS``: Optional list of component names that should
be compiled before this component. This is not necessary for
link-time dependencies, because all component include directories
are available at all times. It is necessary if one component
generates an include file which you then want to include in another
component. Most components do not need to set this variable.
- ``COMPONENT_ADD_LINKER_DEPS``: Optional list of component-relative paths
to files which should trigger a re-link of the ELF file if they change.
Typically used for linker script files and binary libraries. Most components do
not need to set this variable.
The following variable only works for components that are part of esp-idf itself:
- ``COMPONENT_SUBMODULES``: Optional list of git submodule paths
(relative to COMPONENT_PATH) used by the component. These will be
checked (and initialised if necessary) by the build process. This
variable is ignored if the component is outside the IDF_PATH
directory.
Optional Component-Specific Variables
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The following variables can be set inside ``component.mk`` to control the build of that component:
- ``COMPONENT_PRIV_INCLUDEDIRS``: Directory paths, must be relative to
the component directory, which will be added to the include search
path for this component's source files only.
- ``COMPONENT_EXTRA_INCLUDES``: Any extra include paths used when
compiling the component's source files. These will be prefixed with
'-I' and passed as-is to the compiler. Similar to the
``COMPONENT_PRIV_INCLUDEDIRS`` variable, except these paths are not
expanded relative to the component directory.
- ``COMPONENT_SRCDIRS``: Directory paths, must be relative to the
component directory, which will be searched for source files (``*.cpp``,
``*.c``, ``*.S``). Defaults to '.', ie the component directory
itself. Override this to specify a different list of directories
which contain source files.
- ``COMPONENT_OBJS``: Object files to compile. Default value is a .o
file for each source file that is found in ``COMPONENT_SRCDIRS``.
Overriding this list allows you to exclude source files in
``COMPONENT_SRCDIRS`` that would otherwise be compiled. See
`Specifying source files`
- ``COMPONENT_EXTRA_CLEAN``: Paths, relative to the component build
directory, of any files that are generated using custom make rules
in the component.mk file and which need to be removed as part of
``make clean``. See `Source Code Generation`_ for an example.
- ``COMPONENT_OWNBUILDTARGET`` & ``COMPONENT_OWNCLEANTARGET``: These
targets allow you to fully override the default build behaviour for
the component. See `Fully Overriding The Component Makefile`_ for
more details.
- ``COMPONENT_CONFIG_ONLY``: If set, this flag indicates that the component
produces no built output at all (ie ``COMPONENT_LIBRARY`` is not built),
and most other component variables are ignored. This flag is used for IDF
internal components which contain only ``KConfig.projbuild`` and/or
``Makefile.projbuild`` files to configure the project, but no source files.
- ``CFLAGS``: Flags passed to the C compiler. A default set of
``CFLAGS`` is defined based on project settings. Component-specific
additions can be made via ``CFLAGS +=``. It is also possible
(although not recommended) to override this variable completely for
a component.
- ``CPPFLAGS``: Flags passed to the C preprocessor (used for .c, .cpp
and .S files). A default set of ``CPPFLAGS`` is defined based on
project settings. Component-specific additions can be made via
``CPPFLAGS +=``. It is also possible (although not recommended) to
override this variable completely for a component.
- ``CXXFLAGS``: Flags passed to the C++ compiler. A default set of
``CXXFLAGS`` is defined based on project
settings. Component-specific additions can be made via ``CXXFLAGS
+=``. It is also possible (although not recommended) to override
this variable completely for a component.
To apply compilation flags to a single source file, you can add a variable override as a target, ie::
apps/dhcpserver.o: CFLAGS += -Wno-unused-variable
This can be useful if there is upstream code that emits warnings.
Component Configuration
-----------------------
Each component can also have a Kconfig file, alongside ``component.mk``. This contains contains
configuration settings to add to the "make menuconfig" for this component.
These settings are found under the "Component Settings" menu when menuconfig is run.
To create a component KConfig file, it is easiest to start with one of the KConfig files distributed with esp-idf.
For an example, see `Adding conditional configuration`_.
Preprocessor Definitions
------------------------
ESP-IDF build systems adds the following C preprocessor definitions on the command line:
- ``ESP_PLATFORM`` — Can be used to detect that build happens within ESP-IDF.
- ``IDF_VER`` — Defined to a git version string. E.g. ``v2.0`` for a tagged release or ``v1.0-275-g0efaa4f`` for an arbitrary commit.
Build Process Internals
-----------------------
Top Level: Project Makefile
^^^^^^^^^^^^^^^^^^^^^^^^^^^
- "make" is always run from the project directory and the project makefile, typically named Makefile.
- The project makefile sets ``PROJECT_NAME`` and optionally customises other `optional project variables`
- The project makefile includes ``$(IDF_PATH)/make/project.mk`` which contains the project-level Make logic.
- ``project.mk`` fills in default project-level make variables and includes make variables from the project configuration. If the generated makefile containing project configuration is out of date, then it is regenerated (via targets in ``project_config.mk``) and then the make process restarts from the top.
- ``project.mk`` builds a list of components to build, based on the default component directories or a custom list of components set in `optional project variables`.
- Each component can set some `optional project-wide component variables`_. These are included via generated makefiles named ``component_project_vars.mk`` - there is one per component. These generated makefiles are included into ``project.mk``. If any are missing or out of date, they are regenerated (via a recursive make call to the component makefile) and then the make process restarts from the top.
- `Makefile.projbuild` files from components are included into the make process, to add extra targets or configuration.
- By default, the project makefile also generates top-level build & clean targets for each component and sets up `app` and `clean` targets to invoke all of these sub-targets.
- In order to compile each component, a recursive make is performed for the component makefile.
To better understand the project make process, have a read through the ``project.mk`` file itself.
Second Level: Component Makefiles
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Each call to a component makefile goes via the ``$(IDF_PATH)/make/component_wrapper.mk`` wrapper makefile.
- This component wrapper includes all component ``Makefile.componentbuild`` files, making any recipes, variables etc in these files available to every component.
- The ``component_wrapper.mk`` is called with the current directory set to the component build directory, and the ``COMPONENT_MAKEFILE`` variable is set to the absolute path to ``component.mk``.
- ``component_wrapper.mk`` sets default values for all `component variables`, then includes the `component.mk` file which can override or modify these.
- If ``COMPONENT_OWNBUILDTARGET`` and ``COMPONENT_OWNCLEANTARGET`` are not defined, default build and clean targets are created for the component's source files and the prerequisite ``COMPONENT_LIBRARY`` static library file.
- The ``component_project_vars.mk`` file has its own target in ``component_wrapper.mk``, which is evaluated from ``project.mk`` if this file needs to be rebuilt due to changes in the component makefile or the project configuration.
To better understand the component make process, have a read through the ``component_wrapper.mk`` file and some of the ``component.mk`` files included with esp-idf.
Running Make Non-Interactively
------------------------------
When running ``make`` in a situation where you don't want interactive prompts (for example: inside an IDE or an automated build system) append ``BATCH_BUILD=1`` to the make arguments (or set it as an environment variable).
Setting ``BATCH_BUILD`` implies the following:
- Verbose output (same as ``V=1``, see below). If you don't want verbose output, also set ``V=0``.
- If the project configuration is missing new configuration items (from new components or esp-idf updates) then the project use the default values, instead of prompting the user for each item.
- If the build system needs to invoke ``menuconfig``, an error is printed and the build fails.
Debugging The Make Process
--------------------------
Some tips for debugging the esp-idf build system:
- Appending ``V=1`` to the make arguments (or setting it as an environment variable) will cause make to echo all commands executed, and also each directory as it is entered for a sub-make.
- Running ``make -w`` will cause make to echo each directory as it is entered for a sub-make - same as ``V=1`` but without also echoing all commands.
- Running ``make --trace`` (possibly in addition to one of the above arguments) will print out every target as it is built, and the dependency which caused it to be built.
- Running ``make -p`` prints a (very verbose) summary of every generated target in each makefile.
For more debugging tips and general make information, see the `GNU Make Manual`.
.. _warn-undefined-variables:
Warning On Undefined Variables
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
By default, the build process will print a warning if an undefined variable is referenced (like ``$(DOES_NOT_EXIST)``). This can be useful to find errors in variable names.
If you don't want this behaviour, it can be disabled in menuconfig's top level menu under `SDK tool configuration`.
Note that this option doesn't trigger a warning if ``ifdef`` or ``ifndef`` are used in Makefiles.
Overriding Parts of the Project
-------------------------------
Makefile.projbuild
^^^^^^^^^^^^^^^^^^
For components that have build requirements that must be evaluated in the top-level
project make pass, you can create a file called ``Makefile.projbuild`` in the
component directory. This makefile is included when ``project.mk`` is evaluated.
For example, if your component needs to add to CFLAGS for the entire
project (not just for its own source files) then you can set
``CFLAGS +=`` in Makefile.projbuild.
``Makefile.projbuild`` files are used heavily inside esp-idf, for defining project-wide build features such as ``esptool.py`` command line arguments and the ``bootloader`` "special app".
Note that ``Makefile.projbuild`` isn't necessary for the most common component uses - such as adding include directories to the project, or LDFLAGS to the final linking step. These values can be customised via the ``component.mk`` file itself. See `Optional Project-Wide Component Variables`_ for details.
Take care when setting variables or targets in this file. As the values are included into the top-level project makefile pass, they can influence or break functionality across all components!
KConfig.projbuild
^^^^^^^^^^^^^^^^^
This is an equivalent to ``Makefile.projbuild`` for `component configuration` KConfig files. If you want to include
configuration options at the top-level of menuconfig, rather than inside the "Component Configuration" sub-menu, then these can be defined in the KConfig.projbuild file alongside the ``component.mk`` file.
Take care when adding configuration values in this file, as they will be included across the entire project configuration. Where possible, it's generally better to create a KConfig file for `component configuration`.
Makefile.componentbuild
^^^^^^^^^^^^^^^^^^^^^^^
For components that e.g. include tools to generate source files from other files, it is necessary to be able to add recipes, macros or variable definitions
into the component build process of every components. This is done by having a ``Makefile.componentbuild`` in a component directory. This file gets included
in ``component_wrapper.mk``, before the ``component.mk`` of the component is included. As with the Makefile.projbuild, take care with these files: as they're
included in each component build, a ``Makefile.componentbuild`` error may only show up when compiling an entirely different component.
Configuration-Only Components
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Some special components which contain no source files, only ``Kconfig.projbuild`` and ``Makefile.projbuild``, may set the flag ``COMPONENT_CONFIG_ONLY`` in the component.mk file. If this flag is set, most other component variables are ignored and no build step is run for the component.
Example Component Makefiles
---------------------------
Because the build environment tries to set reasonable defaults that will work most
of the time, component.mk can be very small or even empty (see `Minimal Component Makefile`_). However, overriding `component variables` is usually required for some functionality.
Here are some more advanced examples of ``component.mk`` makefiles:
Adding source directories
^^^^^^^^^^^^^^^^^^^^^^^^^
By default, sub-directories are ignored. If your project has sources in sub-directories
instead of in the root of the component then you can tell that to the build
system by setting ``COMPONENT_SRCDIRS``::
COMPONENT_SRCDIRS := src1 src2
This will compile all source files in the src1/ and src2/ sub-directories
instead.
Specifying source files
^^^^^^^^^^^^^^^^^^^^^^^
The standard component.mk logic adds all .S and .c files in the source
directories as sources to be compiled unconditionally. It is possible
to circumvent that logic and hard-code the objects to be compiled by
manually setting the ``COMPONENT_OBJS`` variable to the name of the
objects that need to be generated::
COMPONENT_OBJS := file1.o file2.o thing/filea.o thing/fileb.o anotherthing/main.o
COMPONENT_SRCDIRS := . thing anotherthing
Note that ``COMPONENT_SRCDIRS`` must be set as well.
Adding conditional configuration
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The configuration system can be used to conditionally compile some files
depending on the options selected in ``make menuconfig``. For this, ESP-IDF
has the compile_only_if and compile_only_if_not macros:
``Kconfig``::
config FOO_ENABLE_BAR
bool "Enable the BAR feature."
help
This enables the BAR feature of the FOO component.
``component.mk``::
$(call compile_only_if,$(CONFIG_FOO_ENABLE_BAR),bar.o)
As can be seen in the example, the ``compile_only_if`` macro takes a condition and a
list of object files as parameters. If the condition is true (in this case: if the
BAR feature is enabled in menuconfig) the object files (in this case: bar.o) will
always be compiled. The opposite goes as well: if the condition is not true, bar.o
will never be compiled. ``compile_only_if_not`` does the opposite: compile if the
condition is false, not compile if the condition is true.
This can also be used to select or stub out an implementation, as such:
``Kconfig``::
config ENABLE_LCD_OUTPUT
bool "Enable LCD output."
help
Select this if your board has a LCD.
config ENABLE_LCD_CONSOLE
bool "Output console text to LCD"
depends on ENABLE_LCD_OUTPUT
help
Select this to output debugging output to the lcd
config ENABLE_LCD_PLOT
bool "Output temperature plots to LCD"
depends on ENABLE_LCD_OUTPUT
help
Select this to output temperature plots
``component.mk``::
# If LCD is enabled, compile interface to it, otherwise compile dummy interface
$(call compile_only_if,$(CONFIG_ENABLE_LCD_OUTPUT),lcd-real.o lcd-spi.o)
$(call compile_only_if_not,$(CONFIG_ENABLE_LCD_OUTPUT),lcd-dummy.o)
#We need font if either console or plot is enabled
$(call compile_only_if,$(or $(CONFIG_ENABLE_LCD_CONSOLE),$(CONFIG_ENABLE_LCD_PLOT)), font.o)
Note the use of the Make 'or' function to include the font file. Other substitution functions,
like 'and' and 'if' will also work here. Variables that do not come from menuconfig can also
be used: ESP-IDF uses the default Make policy of judging a variable which is empty or contains
only whitespace to be false while a variable with any non-whitespace in it is true.
(Note: Older versions of this document advised conditionally adding object file names to
``COMPONENT_OBJS``. While this still is possible, this will only work when all object
files for a component are named explicitely, and will not clean up deselected object files
in a ``make clean`` pass.)
Source Code Generation
^^^^^^^^^^^^^^^^^^^^^^
Some components will have a situation where a source file isn't
supplied with the component itself but has to be generated from
another file. Say our component has a header file that consists of the
converted binary data of a BMP file, converted using a hypothetical
tool called bmp2h. The header file is then included in as C source
file called graphics_lib.c::
COMPONENT_EXTRA_CLEAN := logo.h
graphics_lib.o: logo.h
logo.h: $(COMPONENT_PATH)/logo.bmp
bmp2h -i $^ -o $@
In this example, graphics_lib.o and logo.h will be generated in the
current directory (the build directory) while logo.bmp comes with the
component and resides under the component path. Because logo.h is a
generated file, it needs to be cleaned when make clean is called which
why it is added to the COMPONENT_EXTRA_CLEAN variable.
Cosmetic Improvements
^^^^^^^^^^^^^^^^^^^^^
Because logo.h is a generated file, it needs to be cleaned when make
clean is called which why it is added to the COMPONENT_EXTRA_CLEAN
variable.
Adding logo.h to the ``graphics_lib.o`` dependencies causes it to be
generated before ``graphics_lib.c`` is compiled.
If a a source file in another component included ``logo.h``, then this
component's name would have to be added to the other component's
``COMPONENT_DEPENDS`` list to ensure that the components were built
in-order.
Embedding Binary Data
^^^^^^^^^^^^^^^^^^^^^
Sometimes you have a file with some binary or text data that you'd like to make available to your component - but you don't want to reformat the file as C source.
You can set a variable COMPONENT_EMBED_FILES in component.mk, giving the names of the files to embed in this way::
COMPONENT_EMBED_FILES := server_root_cert.der
Or if the file is a string, you can use the variable COMPONENT_EMBED_TXTFILES. This will embed the contents of the text file as a null-terminated string::
COMPONENT_EMBED_TXTFILES := server_root_cert.pem
The file's contents will be added to the .rodata section in flash, and are available via symbol names as follows::
extern const uint8_t server_root_cert_pem_start[] asm("_binary_server_root_cert_pem_start");
extern const uint8_t server_root_cert_pem_end[] asm("_binary_server_root_cert_pem_end");
The names are generated from the full name of the file, as given in COMPONENT_EMBED_FILES. Characters /, ., etc. are replaced with underscores. The _binary prefix in the symbol name is added by objcopy and is the same for both text and binary files.
For an example of using this technique, see :example:`protocols/https_request` - the certificate file contents are loaded from the text .pem file at compile time.
Fully Overriding The Component Makefile
---------------------------------------
Obviously, there are cases where all these recipes are insufficient for a
certain component, for example when the component is basically a wrapper
around another third-party component not originally intended to be
compiled under this build system. In that case, it's possible to forego
the esp-idf build system entirely by setting COMPONENT_OWNBUILDTARGET and
possibly COMPONENT_OWNCLEANTARGET and defining your own targets named ``build`` and ``clean`` in ``component.mk``
target. The build target can do anything as long as it creates
$(COMPONENT_LIBRARY) for the project make process to link into the app binary.
(Actually, even this is not strictly necessary - if the COMPONENT_ADD_LDFLAGS variable
is overridden then the component can instruct the linker to link other binaries instead.)
.. _esp-idf-template: https://github.com/espressif/esp-idf-template
.. _GNU Make Manual: https://www.gnu.org/software/make/manual/make.html
.. _custom-sdkconfig-defaults:
Custom sdkconfig defaults
-------------------------
For example projects or other projects where you don't want to specify a full sdkconfig configuration, but you do want to override some key values from the esp-idf defaults, it is possible to create a file ``sdkconfig.defaults`` in the project directory. This file will be used when running ``make defconfig``, or creating a new config from scratch.
To override the name of this file, set the ``SDKCONFIG_DEFAULTS`` environment variable.
Save flash arguments
--------------------
There're some scenarios that we want to flash the target board without IDF. For this case we want to save the built binaries, esptool.py and esptool write_flash arguments. It's simple to write a script to save binaries and esptool.py. We can use command ``make print_flash_cmd``, it will print the flash arguments::
--flash_mode dio --flash_freq 40m --flash_size detect 0x1000 bootloader/bootloader.bin 0x10000 example_app.bin 0x8000 partition_table_unit_test_app.bin
Then use flash arguments as the arguemnts for esptool write_flash arguments::
python esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect 0x1000 bootloader/bootloader.bin 0x10000 example_app.bin 0x8000 partition_table_unit_test_app.bin
Building the Bootloader
=======================
The bootloader is built by default as part of "make all", or can be built standalone via "make bootloader-clean". There is also "make bootloader-list-components" to see the components included in the bootloader build.
The component in IDF components/bootloader is special, as the second stage bootloader is a separate .ELF and .BIN file to the main project. However it shares its configuration and build directory with the main project.
This is accomplished by adding a subproject under components/bootloader/subproject. This subproject has its own Makefile, but it expects to be called from the project's own Makefile via some glue in the components/bootloader/Makefile.projectbuild file. See these files for more details.
+143
View File
@@ -0,0 +1,143 @@
Console
=======
ESP-IDF provides ``console`` component, which includes building blocks needed to develop an interactive console over serial port. This component includes following facilities:
- Line editing, provided by `linenoise`_ library. This includes handling of backspace and arrow keys, scrolling through command history, command auto-completion, and argument hints.
- Splitting of command line into arguments.
- Argument parsing, provided by `argtable3`_ library. This library includes APIs useful for parsing GNU style command line arguments.
- Functions for registration and dispatching of commands.
These facilities can be used together or independently. For example, it is possible to use line editing and command registration features, but use ``getopt`` or custom code for argument parsing, instead of `argtable3`_. Likewise, it is possible to use simpler means of command input (such as ``fgets``) together with the rest of the means for command splitting and argument parsing.
Line editing
------------
Line editing feature lets users compose commands by typing them, erasing symbols using 'backspace' key, navigating within the command using left/right keys, navigating to previously typed commands using up/down keys, and performing autocompletion using 'tab' key.
.. note:: This feature relies on ANSI escape sequence support in the terminal application. As such, serial monitors which display raw UART data can not be used together with the line editing library. If you see ``[6n`` or similar escape sequence when running get_started/console example instead of a command prompt (``[esp32]>``), it means that the serial monitor does not support escape sequences. Programs which are known to work are GNU screen, minicom, and idf_monitor.py (which can be invoked using ``make monitor`` from project directory).
Here is an overview of functions provided by `linenoise`_ library.
Configuration
^^^^^^^^^^^^^
Linenoise library does not need explicit initialization. However, some configuration defaults may need to be changed before invoking the main line editing function.
``linenoiseClearScreen``
Clear terminal screen using an escape sequence and position the cursor at the top left corner.
``linenoiseSetMultiLine``
Switch between single line and multi line editing modes. In single line mode, if the length of the command exceeds the width of the terminal, the command text is scrolled within the line to show the end of the text. In this case the beginning of the text is hidden. Single line needs less data to be sent to refresh screen on each key press, so exhibits less glitching compared to the multi line mode. On the flip side, editing commands and copying command text from terminal in single line mode is harder. Default is single line mode.
Main loop
^^^^^^^^^
``linenoise``
In most cases, console applications have some form of read/eval loop. ``linenoise`` is the single function which handles user's key presses and returns completed line once 'enter' key is pressed. As such, it handles the 'read' part of the loop.
``linenoiseFree``
This function must be called to release the command line buffer obtained from ``linenoise`` function.
Hints and completions
^^^^^^^^^^^^^^^^^^^^^
``linenoiseSetCompletionCallback``
When user presses 'tab' key, linenoise library invokes completion callback. The callback should inspect the contents of the command typed so far and provide a list of possible completions using calls to ``linenoiseAddCompletion`` function. ``linenoiseSetCompletionCallback`` function should be called to register this completion callback, if completion feature is desired.
``console`` component provides a ready made function to provide completions for registered commands, ``esp_console_get_completion`` (see below).
``linenoiseAddCompletion``
Function to be called by completion callback to inform the library about possible completions of the currently typed command.
``linenoiseSetHintsCallback``
Whenever user input changes, linenoise invokes hints callback. This callback can inspect the command line typed so far, and provide a string with hints (which can include list of command arguments, for example). The library then displays the hint text on the same line where editing happens, possibly with a different color.
``linenoiseSetFreeHintsCallback``
If hint string returned by hints callback is dynamically allocated or needs to be otherwise recycled, the function which performs such cleanup should be registered via ``linenoiseSetFreeHintsCallback``.
History
^^^^^^^
``linenoiseHistorySetMaxLen``
This function sets the number of most recently typed commands to be kept in memory. Users can navigate the history using up/down arrows.
``linenoiseHistoryAdd``
Linenoise does not automatically add commands to history. Instead, applications need to call this function to add command strings to the history.
``linenoiseHistorySave``
Function saves command history from RAM to a text file, for example on an SD card or on a filesystem in flash memory.
``linenoiseHistoryLoad``
Counterpart to ``linenoiseHistorySave``, loads history from a file.
``linenoiseHistoryFree``
Releases memory used to store command history. Call this function when done working with linenoise library.
Splitting of command line into arguments
----------------------------------------
``console`` component provides ``esp_console_split_argv`` function to split command line string into arguments. The function returns the number of arguments found (``argc``) and fills an array of pointers which can be passed as ``argv`` argument to any function which accepts arguments in ``argc, argv`` format.
The command line is split into arguments according to the following rules:
- Arguments are separated by spaces
- If spaces within arguments are required, they can be escaped using ``\`` (backslash) character.
- Other escape sequences which are recognized are ``\\`` (which produces literal backslash) and ``\"``, which produces a double quote.
- Arguments can be quoted using double quotes. Quotes may appear only in the beginning and at the end of the argument. Quotes within the argument must be escaped as mentioned above. Quotes surrounding the argument are stripped by ``esp_console_split_argv`` function.
Examples:
- ``abc def 1 20 .3`` ⟶ [ ``abc``, ``def``, ``1``, ``20``, ``.3`` ]
- ``abc "123 456" def`` ⟶ [ ``abc``, ``123 456``, ``def`` ]
- ```a\ b\\c\"`` ⟶ [ ``a b\c"`` ]
Argument parsing
----------------
For argument parsing, ``console`` component includes `argtable3`_ library. Please see `tutorial`_ for an introduction to `argtable3`_. Github repository also includes `examples`_.
.. _argtable3: http://www.argtable.org/
.. _linenoise: https://github.com/antirez/linenoise
.. _tutorial: http://www.argtable.org/tutorial/
.. _examples: https://github.com/argtable/argtable3/tree/master/examples
Command registration and dispatching
------------------------------------
``console`` component includes utility functions which handle registration of commands, matching commands typed by the user to registered ones, and calling these commands with the arguments given on the command line.
Application first initializes command registration module using a call to ``esp_console_init``, and calls ``esp_console_cmd_register`` function to register command handlers.
For each command, application provides the following information (in the form of ``esp_console_cmd_t`` structure):
- Command name (string without spaces)
- Help text explaining what the command does
- Optional hint text listing the arguments of the command. If application uses Argtable3 for argument parsing, hint text can be generated automatically by providing a pointer to argtable argument definitions structure instead.
- The command handler function.
A few other functions are provided by the command registration module:
``esp_console_run``
This function takes the command line string, splits it into argc/argv argument list using ``esp_console_split_argv``, looks up the command in the list of registered components, and if it is found, executes its handler.
``esp_console_split_argv``
Adds ``help`` command to the list of registered commands. This command prints the list of all the registered commands, along with their arguments and help texts.
``esp_console_get_completion``
Callback function to be used with ``linenoiseSetCompletionCallback`` from linenoise library. Provides completions to linenoise based on the list of registered commands.
``esp_console_get_hint``
Callback function to be used with ``linenoiseSetHintsCallback`` from linenoise library. Provides argument hints for registered commands to linenoise.
Example
-------
Example application illustrating usage of the ``console`` component is available in ``examples/system/console`` directory. This example shows how to initialize UART and VFS functions, set up linenoise library, read and handle commands from UART, and store command history in Flash. See README.md in the example directory for more details.
+86
View File
@@ -0,0 +1,86 @@
ESP32 Core Dump
===============
Overview
--------
ESP-IDF provides support to generate core dumps on unrecoverable software errors. This useful technique allows post-mortem analysis of software state at the moment of failure.
Upon the crash system enters panic state, prints some information and halts or reboots depending configuration. User can choose to generate core dump in order to analyse
the reason of failure on PC later on. Core dump contains snapshots of all tasks in the system at the moment of failure. Snapshots include tasks control blocks (TCB) and stacks.
So it is possible to find out what task, at what instruction (line of code) and what callstack of that task lead to the crash.
ESP-IDF provides special script `espcoredump.py` to help users to retrieve and analyse core dumps. This tool provides two commands for core dumps analysis:
* info_corefile - prints crashed task's registers, callstack, list of available tasks in the system, memory regions and contents of memory stored in core dump (TCBs and stacks)
* dbg_corefile - creates core dump ELF file and runs GDB debug session with this file. User can examine memory, variables and tasks states manually. Note that since not all memory is saved in core dump only values of variables allocated on stack will be meaningfull
Configuration
-------------
There are a number of core dump related configuration options which user can choose in configuration menu of the application (`make menuconfig`).
1. Core dump data destination (`Components -> ESP32-specific config -> Core dump destination`):
* Disable core dump generation
* Save core dump to flash
* Print core dump to UART
2. Logging level of core dump module (`Components -> ESP32-specific config -> Core dump module logging level`). Value is a number from 0 (no output) to 5 (most verbose).
3. Delay before core dump will be printed to UART (`Components -> ESP32-specific config -> Core dump print to UART delay`). Value is in ms.
Save core dump to flash
-----------------------
When this option is selected core dumps are saved to special partition on flash. When using default partition table files which are provided with ESP-IDF it automatically
allocates necessary space on flash, But if user wants to use its own layout file together with core dump feature it should define separate partition for core dump
as it is shown below::
# Name, Type, SubType, Offset, Size
# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
nvs, data, nvs, 0x9000, 0x6000
phy_init, data, phy, 0xf000, 0x1000
factory, app, factory, 0x10000, 1M
coredump, data, coredump,, 64K
There are no special requrements for partition name. It can be choosen according to the user application needs, but partition type should be 'data' and
sub-type should be 'coredump'. Also when choosing partition size note that core dump data structure introduces constant overhead of 20 bytes and per-task overhead of 12 bytes.
This overhead does not include size of TCB and stack for every task. So partirion size should be at least 20 + max tasks number x (12 + TCB size + max task stack size) bytes.
The example of generic command to analyze core dump from flash is: `espcoredump.py -p </path/to/serial/port> info_corefile </path/to/program/elf/file>`
or `espcoredump.py -p </path/to/serial/port> dbg_corefile </path/to/program/elf/file>`
Print core dump to UART
-----------------------
When this option is selected base64-encoded core dumps are printed on UART upon system panic. In this case user should save core dump text body to some file manually and
then run the following command: `espcoredump.py info_corefile -t b64 -c </path/to/saved/base64/text> </path/to/program/elf/file>`
or `espcoredump.py dbg_corefile -t b64 -c </path/to/saved/base64/text> </path/to/program/elf/file>`
Base64-encoded body of core dump will be between the following header and footer::
================= CORE DUMP START =================
<body of base64-encoded core dump, save it to file on disk>
================= CORE DUMP END ===================
Running 'espcoredump.py'
------------------------------------
Generic command syntax:
`espcoredump.py [options] command [args]`
:Script Options:
* --chip,-c {auto,esp32}. Target chip type. Supported values are `auto` and `esp32`.
* --port,-p PORT. Serial port device.
* --baud,-b BAUD. Serial port baud rate used when flashing/reading.
:Commands:
* info_corefile. Retrieve core dump and print useful info.
* dbg_corefile. Retrieve core dump and start GDB session with it.
:Command Arguments:
* --gdb,-g GDB. Path to gdb to use for data retrieval.
* --core,-c CORE. Path to core dump file to use (if skipped core dump will be read from flash).
* --core-format,-t CORE_FORMAT. Specifies that file passed with "-c" is an ELF ("elf"), dumped raw binary ("raw") or base64-encoded ("b64") format.
* --off,-o OFF. Ofsset of coredump partition in flash (type "make partition_table" to see it).
* --save-core,-s SAVE_CORE. Save core to file. Othwerwise temporary core file will be deleted. Ignored with "-c".
* --print-mem,-m Print memory dump. Used only with "info_corefile".
+87
View File
@@ -0,0 +1,87 @@
Deep Sleep Wake Stubs
=====================
ESP32 supports running a "deep sleep wake stub" when coming out of deep sleep. This function runs immediately as soon as the chip wakes up - before any normal initialisation, bootloader, or ESP-IDF code has run. After the wake stub runs, the SoC can go back to sleep or continue to start ESP-IDF normally.
Deep sleep wake stub code is loaded into "RTC Fast Memory" and any data which it uses must also be loaded into RTC memory. RTC memory regions hold their contents during deep sleep.
Rules for Wake Stubs
--------------------
Wake stub code must be carefully written:
* As the SoC has freshly woken from sleep, most of the peripherals are in reset states. The SPI flash is unmapped.
* The wake stub code can only call functions implemented in ROM or loaded into RTC Fast Memory (see below.)
* The wake stub code can only access data loaded in RTC memory. All other RAM will be unintiailised and have random contents. The wake stub can use other RAM for temporary storage, but the contents will be overwritten when the SoC goes back to sleep or starts ESP-IDF.
* RTC memory must include any read-only data (.rodata) used by the stub.
* Data in RTC memory is initialised whenever the SoC restarts, except when waking from deep sleep. When waking from deep sleep, the values which were present before going to sleep are kept.
* Wake stub code is a part of the main esp-idf app. During normal running of esp-idf, functions can call the wake stub functions or access RTC memory. It is as if these were regular parts of the app.
Implementing A Stub
-------------------
The wake stub in esp-idf is called ``esp_wake_deep_sleep()``. This function runs whenever the SoC wakes from deep sleep. There is a default version of this function provided in esp-idf, but the default function is weak-linked so if your app contains a function named ``esp_wake_deep_sleep()`` then this will override the default.
If supplying a custom wake stub, the first thing it does should be to call ``esp_default_wake_deep_sleep()``.
It is not necessary to implement ``esp_wake_deep_sleep()`` in your app in order to use deep sleep. It is only necessary if you want to have special behaviour immediately on wake.
If you want to swap between different deep sleep stubs at runtime, it is also possible to do this by calling the ``esp_set_deep_sleep_wake_stub()`` function. This is not necessary if you only use the default ``esp_wake_deep_sleep()`` function.
All of these functions are declared in the ``esp_deepsleep.h`` header under components/esp32.
Loading Code Into RTC Memory
----------------------------
Wake stub code must be resident in RTC Fast Memory. This can be done in one of two ways.
The first way is to use the ``RTC_IRAM_ATTR`` attribute to place a function into RTC memory::
void RTC_IRAM_ATTR esp_wake_deep_sleep(void) {
esp_default_wake_deep_sleep();
// Add additional functionality here
}
The second way is to place the function into any source file whose name starts with ``rtc_wake_stub``. Files names ``rtc_wake_stub*`` have their contents automatically put into RTC memory by the linker.
The first way is simpler for very short and simple code, or for source files where you want to mix "normal" and "RTC" code. The second way is simpler when you want to write longer pieces of code for RTC memory.
Loading Data Into RTC Memory
----------------------------
Data used by stub code must be resident in RTC Slow Memory. This memory is also used by the ULP.
Specifying this data can be done in one of two ways:
The first way is to use the ``RTC_DATA_ATTR`` and ``RTC_RODATA_ATTR`` to specify any data (writeable or read-only, respectivley) which should be loaded into RTC slow memory::
RTC_DATA_ATTR int wake_count;
void RTC_IRAM_ATTR esp_wake_deep_sleep(void) {
esp_default_wake_deep_sleep();
static RTC_RODATA_ATTR const char fmt_str[] = "Wake count %d\n";
ets_printf(fmt_str, wake_count++);
}
Unfortunately, any string constants used in this way must be declared as arrays and marked with RTC_RODATA_ATTR, as shown in the example above.
The second way is to place the data into any source file whose name starts with ``rtc_wake_stub``.
For example, the equivalent example in ``rtc_wake_stub_counter.c``::
int wake_count;
void RTC_IRAM_ATTR esp_wake_deep_sleep(void) {
esp_default_wake_deep_sleep();
ets_printf("Wake count %d\n", wake_count++);
}
The second way is a better option if you need to use strings, or write other more complex code.
+104
View File
@@ -0,0 +1,104 @@
Support for external RAM
************************
.. toctree::
:maxdepth: 1
Introduction
============
The ESP32 has a few hundred KiB of internal RAM, residing on the same die as the rest of the ESP32. For some purposes, this is insufficient,
and therefore the ESP32 incorporates the ability to also use up to 4MiB of external SPI RAM memory as memory. The external memory is incorporated
in the memory map and is, within certain restrictions, usable in the same way internal data RAM is.
Hardware
========
The ESP32 supports SPI (P)SRAM connected in parallel with the SPI flash chip. While the ESP32 is capable of supporting several types
of RAM chips, the ESP32 SDK at the moment only supports the ESP-PSRAM32 chip.
The ESP-PSRAM32 chip is an 1.8V device, and can only be used in parallel with an 1.8V flash part. Make sure to either set the MTDI
pin to a high signal level on bootup, or program the fuses in the ESP32 to always use a VDD_SIO level of 1.8V. Not doing this risks
damaging the PSRAM and/or flash chip.
To connect the ESP-PSRAM chip to the ESP32D0W*, connect the following signals:
* PSRAM /CE (pin 1) - ESP32 GPIO 16
* PSRAM SO (pin 2) - flash DO
* PSRAM SIO[2] (pin 3) - flash WP
* PSRAM SI (pin 5) - flash DI
* PSRAM SCLK (pin 6) - ESP32 GPIO 17
* PSRAM SIO[3] (pin 7) - flash HOLD
* PSRAM Vcc (pin 8) - ESP32 VCC_SDIO
Connections for the ESP32D2W* chips are TBD.
.. NOTE::
Espressif sells an ESP-WROVER module which contains an ESP32, 1.8V flash and the ESP-PSRAM32 integrated in a module, ready for inclusion
on an end product PCB.
Software
========
ESP-IDF fully supports integrating external memory use into your applications. ESP-IDF can be configured to handle external RAM in several ways:
* Only initialize RAM. This allows the application to manually place data here by dereferencing pointers pointed at the external RAM memory
region (0x3F800000 and up).
* Initialize RAM and add it to the capability allocator. This allows a program to specifically allocate a chunk of external RAM using
``heap_caps_malloc(size, MALLOC_CAP_SPIRAM)``. This memory can be used and subsequently freed using a normal ``free()`` call.
* Initialize RAM, add it to the capability allocator and add memory to the pool of RAM that can be returned by ``malloc()``. This allows
any application to use the external RAM without having to rewrite the code to use ``heap_caps_malloc``.
All these options can be selected from the menuconfig menu.
Restrictions
------------
The use of external RAM has a few restrictions:
* When disabling flash cache (for example, because the flash is being written to), the external RAM also becomes inaccessible; any reads from or
writes to it will lead to an illegal cache access exception. This is also the reason that ESP-IDF will never allocate a tasks stack in external
RAM.
* External RAM cannot be used as a place to store DMA transaction descriptors or as a buffer for a DMA transfer to read from or write into. Any
buffers that will be used in combination with DMA must be allocated using ``heap_caps_malloc(size, MALLOC_CAP_DMA)`` (and can be freed using a
standard ``free()`` call.)
* External RAM uses the same cache region as the external flash. This means that often accessed variables in external RAM can be read and
modified almost as quickly as in internal ram. However, when accessing large chunks of data (>32K), the cache can be insufficient and speeds
will fall back to the access speed of the external RAM. Moreover, accessing large chunks of data can 'push out' cached flash, possibly making
execution of code afterwards slower.
* External RAM cannot be used as task stack memory; because of this, xTaskCreate and similar functions will always allocate internal memory
for stack and task TCBs and xTaskCreateStatic-type functions will check if the buffers passed are internal. However, for tasks not calling
on code in ROM in any way, directly or indirectly, the menuconfig option :ref:`CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY` will eliminate
the check in xTaskCreateStatic, allowing task stack in external RAM. Using this is not advised, however.
Because there are a fair few situations that have a specific need for internal memory, but it is also possible to use malloc() to exhaust
internal memory, there is a pool reserved specifically for requests that cannot be resolved from external memory; allocating task
stack, DMA buffers and memory that stays accessible when cache is disabled is drawn from this pool. The size of this pool is configurable
in menuconfig.
Chip revisions
==============
There are some issues with certain revisions of the ESP32 that have repercussions for use with external RAM. These are documented in the ESP32
ECO_ document. In particular, ESP-IDF handles the bugs mentioned in the following ways:
ESP32 rev v0
------------
ESP-IDF has no workaround for the bugs in this revision of silicon, and it cannot be used to map external PSRAM into the ESP32s main memory map.
ESP32 rev v1
------------
The bugs in this silicon revision introduce a hazard when certain sequences of machine instructions operate on external memory locations (ESP32 ECO 3.2).
To work around this, the gcc compiler to compile ESP-IDF has been expanded with a flag: ``-mfix-esp32-psram-cache-issue``. With this flag passed to gcc
on the command line, the compiler works around these sequences and only outputs code that can safely be executed.
In ESP-IDF, this flag is enabled when you select :ref:`CONFIG_SPIRAM_CACHE_WORKAROUND`. ESP-IDF also takes other measures to make
sure no combination of PSRAM access plus the offending instruction sets are used: it links to a version of Newlib recompiled with the gcc flag, doesn't use
some ROM functions and allocates static memory for the WiFi stack.
.. _ECO: https://www.espressif.com/sites/default/files/documentation/eco_and_workarounds_for_bugs_in_esp32_en.pdf
+499
View File
@@ -0,0 +1,499 @@
ESP-IDF FreeRTOS SMP Changes
============================
Overview
--------
The vanilla FreeRTOS is designed to run on a single core. However the ESP32 is
dual core containing a Protocol CPU (known as **CPU 0** or **PRO_CPU**) and an
Application CPU (known as **CPU 1** or **APP_CPU**). The two cores are
identical in practice and share the same memory. This allows the two cores to
run tasks interchangeably between them.
The ESP-IDF FreeRTOS is a modified version of vanilla FreeRTOS which supports
symmetric multiprocessing (SMP). ESP-IDF FreeRTOS is based on the Xtensa port
of FreeRTOS v8.2.0. This guide outlines the major differences between vanilla
FreeRTOS and ESP-IDF FreeRTOS. The API reference for vanilla FreeRTOS can be
found via http://www.freertos.org/a00106.html
:ref:`backported-features`: Although ESP-IDF FreeRTOS is based on the Xtensa
port of FreeRTOS v8.2.0, a number of FreeRTOS v9.0.0 features have been backported
to ESP-IDF.
:ref:`tasks-and-task-creation`: Use :cpp:func:`xTaskCreatePinnedToCore` or
:cpp:func:`xTaskCreateStaticPinnedToCore` to create tasks in ESP-IDF FreeRTOS. The
last parameter of the two functions is ``xCoreID``. This parameter specifies
which core the task is pinned to. Acceptable values are ``0`` for **PRO_CPU**,
``1`` for **APP_CPU**, or ``tskNO_AFFINITY`` which allows the task to run on
both.
:ref:`round-robin-scheduling`: The ESP-IDF FreeRTOS scheduler will skip tasks when
implementing Round-Robin scheduling between multiple tasks in the Ready state
that are of the same priority. To avoid this behavior, ensure that those tasks either
enter a blocked state, or are distributed across a wider range of priorities.
:ref:`scheduler-suspension`: Suspending the scheduler in ESP-IDF FreeRTOS will only
affect the scheduler on the the calling core. In other words, calling
:cpp:func:`vTaskSuspendAll` on **PRO_CPU** will not prevent **APP_CPU** from scheduling, and
vice versa. Use critical sections or semaphores instead for simultaneous
access protection.
:ref:`tick-interrupt-synchronicity`: Tick interrupts of **PRO_CPU** and **APP_CPU**
are not synchronized. Do not expect to use :cpp:func:`vTaskDelay` or
:cpp:func:`vTaskDelayUntil` as an accurate method of synchronizing task execution
between the two cores. Use a counting semaphore instead as their context
switches are not tied to tick interrupts due to preemption.
:ref:`critical-sections`: In ESP-IDF FreeRTOS, critical sections are implemented using
mutexes. Entering critical sections involve taking a mutex, then disabling the
scheduler and interrupts of the calling core. However the other core is left
unaffected. If the other core attemps to take same mutex, it will spin until
the calling core has released the mutex by exiting the critical section.
:ref:`floating-points`: The ESP32 supports hardware acceleration of single
precision floating point arithmetic (``float``). However the use of hardware
acceleration leads to some behavioral restrictions in ESP-IDF FreeRTOS.
Therefore, tasks that utilize ``float`` will automatically be pinned to a core if
not done so already. Furthermore, ``float`` cannot be used in interrupt service
routines.
:ref:`task-deletion`: Task deletion behavior has been backported from FreeRTOS
v9.0.0 and modified to be SMP compatible. Task memory will be freed immediately
when :cpp:func:`vTaskDelete` is called to delete a task that is not currently running
and not pinned to the other core. Otherwise, freeing of task memory will still
be delegated to the Idle Task.
:ref:`deletion-callbacks`: ESP-IDF FreeRTOS has backported the Thread Local
Storage Pointers (TLSP) feature. However the extra feature of Deletion Callbacks has been
added. Deletion callbacks are called automatically during task deletion and are
used to free memory pointed to by TLSP. Call
:cpp:func:`vTaskSetThreadLocalStoragePointerAndDelCallback()` to set TLSP and Deletion
Callbacks.
:ref:`FreeRTOS Hooks<hooks_api_reference>`: Vanilla FreeRTOS Hooks were not designed for SMP.
ESP-IDF provides its own Idle and Tick Hooks in addition to the Vanilla FreeRTOS
hooks. For full details, see the ESP-IDF Hooks API Reference.
:ref:`esp-idf-freertos-configuration`: Several aspects of ESP-IDF FreeRTOS can be
configured using ``make meunconfig`` such as running ESP-IDF in Unicore Mode,
or configuring the number of Thread Local Storage Pointers each task will have.
.. _backported-features:
Backported Features
-------------------
The following features have been backported from FreeRTOS v9.0.0 to ESP-IDF.
Static Alocation
^^^^^^^^^^^^^^^^^
This feature has been backported from FreeRTOS v9.0.0 to ESP-IDF. The
:ref:`CONFIG_SUPPORT_STATIC_ALLOCATION` option must be enabled in `menuconfig`
in order for static allocation functions to be available. Once enabled, the
following functions can be called...
- :cpp:func:`xTaskCreateStatic` (see :ref:`backporting-notes` below)
- :c:macro:`xQueueCreateStatic`
- :c:macro:`xSemaphoreCreateBinaryStatic`
- :c:macro:`xSemaphoreCreateCountingStatic`
- :c:macro:`xSemaphoreCreateMutexStatic`
- :c:macro:`xSemaphoreCreateRecursiveMutexStatic`
- :cpp:func:`xTimerCreateStatic` (see :ref:`backporting-notes` below)
- :cpp:func:`xEventGroupCreateStatic`
Other Features
^^^^^^^^^^^^^^
- :cpp:func:`vTaskSetThreadLocalStoragePointer` (see :ref:`backporting-notes` below)
- :cpp:func:`pvTaskGetThreadLocalStoragePointer` (see :ref:`backporting-notes` below)
- :cpp:func:`vTimerSetTimerID`
- :cpp:func:`xTimerGetPeriod`
- :cpp:func:`xTimerGetExpiryTime`
- :cpp:func:`pcQueueGetName`
- :c:macro:`uxSemaphoreGetCount`
.. _backporting-notes:
Backporting Notes
^^^^^^^^^^^^^^^^^
**1)** :cpp:func:`xTaskCreateStatic` has been made SMP compatible in a similar
fashion to :cpp:func:`xTaskCreate` (see :ref:`tasks-and-task-creation`). Therefore
:cpp:func:`xTaskCreateStaticPinnedToCore` can also be called.
**2)** Although vanilla FreeRTOS allows the Timer feature's daemon task to
be statically allocated, the daemon task is always dynamically allocated in
ESP-IDF. Therefore ``vApplicationGetTimerTaskMemory`` **does not** need to be
defined when using statically allocated timers in ESP-IDF FreeRTOS.
**3)** The Thread Local Storage Pointer feature has been modified in ESP-IDF
FreeRTOS to include Deletion Callbacks (see :ref:`deletion-callbacks`). Therefore
the function :cpp:func:`vTaskSetThreadLocalStoragePointerAndDelCallback` can also be
called.
.. _tasks-and-task-creation:
Tasks and Task Creation
-----------------------
Tasks in ESP-IDF FreeRTOS are designed to run on a particular core, therefore
two new task creation functions have been added to ESP-IDF FreeRTOS by
appending ``PinnedToCore`` to the names of the task creation functions in
vanilla FreeRTOS. The vanilla FreeRTOS functions of :cpp:func:`xTaskCreate`
and :cpp:func:`xTaskCreateStatic` have led to the addition of
:cpp:func:`xTaskCreatePinnedToCore` and :cpp:func:`xTaskCreateStaticPinnedToCore` in
ESP-IDF FreeRTOS (see :ref:`backported-features`).
For more details see :component_file:`freertos/task.c`
The ESP-IDF FreeRTOS task creation functions are nearly identical to their
vanilla counterparts with the exception of the extra parameter known as
``xCoreID``. This parameter specifies the core on which the task should run on
and can be one of the following values.
- ``0`` pins the task to **PRO_CPU**
- ``1`` pins the task to **APP_CPU**
- ``tskNO_AFFINITY`` allows the task to be run on both CPUs
For example ``xTaskCreatePinnedToCore(tsk_callback, “APP_CPU Task”, 1000, NULL, 10, NULL, 1)``
creates a task of priority 10 that is pinned to **APP_CPU** with a stack size
of 1000 bytes. It should be noted that the ``uxStackDepth`` parameter in
vanilla FreeRTOS specifies a tasks stack depth in terms of the number of
words, whereas ESP-IDF FreeRTOS specifies the stack depth in terms of bytes.
Note that the vanilla FreeRTOS functions :cpp:func:`xTaskCreate` and
:cpp:func:`xTaskCreateStatic` have been defined in ESP-IDF FreeRTOS as inline functions which call
:cpp:func:`xTaskCreatePinnedToCore` and :cpp:func:`xTaskCreateStaticPinnedToCore`
respectively with ``tskNO_AFFINITY`` as the ``xCoreID`` value.
Each Task Control Block (TCB) in ESP-IDF stores the ``xCoreID`` as a member.
Hence when each core calls the scheduler to select a task to run, the
``xCoreID`` member will allow the scheduler to determine if a given task is
permitted to run on the core that called it.
Scheduling
----------
The vanilla FreeRTOS implements scheduling in the ``vTaskSwitchContext()``
function. This function is responsible for selecting the highest priority task
to run from a list of tasks in the Ready state known as the Ready Tasks List
(described in the next section). In ESP-IDF FreeRTOS, each core will call
``vTaskSwitchContext()`` independently to select a task to run from the
Ready Tasks List which is shared between both cores. There are several
differences in scheduling behavior between vanilla and ESP-IDF FreeRTOS such as
differences in Round Robin scheduling, scheduler suspension, and tick interrupt
synchronicity.
.. _round-robin-scheduling:
Round Robin Scheduling
^^^^^^^^^^^^^^^^^^^^^^
Given multiple tasks in the Ready state and of the same priority, vanilla
FreeRTOS implements Round Robin scheduling between each task. This will result
in running those tasks in turn each time the scheduler is called
(e.g. every tick interrupt). On the other hand, the ESP-IDF FreeRTOS scheduler
may skip tasks when Round Robin scheduling multiple Ready state tasks of the
same priority.
The issue of skipping tasks during Round Robin scheduling arises from the way
the Ready Tasks List is implemented in FreeRTOS. In vanilla FreeRTOS,
``pxReadyTasksList`` is used to store a list of tasks that are in the Ready
state. The list is implemented as an array of length ``configMAX_PRIORITIES``
where each element of the array is a linked list. Each linked list is of type
``List_t`` and contains TCBs of tasks of the same priority that are in the
Ready state. The following diagram illustrates the ``pxReadyTasksList``
structure.
.. figure:: ../_static/freertos-ready-task-list.png
:align: center
:alt: Vanilla FreeRTOS Ready Task List Structure
Illustration of FreeRTOS Ready Task List Data Structure
Each linked list also contains a ``pxIndex`` which points to the last TCB
returned when the list was queried. This index allows the ``vTaskSwitchContext()``
to start traversing the list at the TCB immediately after ``pxIndex`` hence
implementing Round Robin Scheduling between tasks of the same priority.
In ESP-IDF FreeRTOS, the Ready Tasks List is shared between cores hence
``pxReadyTasksList`` will contain tasks pinned to different cores. When a core
calls the scheduler, it is able to look at the ``xCoreID`` member of each TCB
in the list to determine if a task is allowed to run on calling the core. The
ESP-IDF FreeRTOS ``pxReadyTasksList`` is illustrated below.
.. figure:: ../_static/freertos-ready-task-list-smp.png
:align: center
:alt: ESP-IDF FreeRTOS Ready Task List Structure
Illustration of FreeRTOS Ready Task List Data Structure in ESP-IDF
Therefore when **PRO_CPU** calls the scheduler, it will only consider the tasks
in blue or purple. Whereas when **APP_CPU** calls the scheduler, it will only
consider the tasks in orange or purple.
Although each TCB has an ``xCoreID`` in ESP-IDF FreeRTOS, the linked list of
each priority only has a single ``pxIndex``. Therefore when the scheduler is
called from a particular core and traverses the linked list, it will skip all
TCBs pinned to the other core and point the pxIndex at the selected task. If
the other core then calls the scheduler, it will traverse the linked list
starting at the TCB immediately after ``pxIndex``. Therefore, TCBs skipped on
the previous scheduler call from the other core would not be considered on the
current scheduler call. This issue is demonstrated in the following
illustration.
.. figure:: ../_static/freertos-ready-task-list-smp-pxIndex.png
:align: center
:alt: ESP-IDF pxIndex Behavior
Illustration of pxIndex behavior in ESP-IDF FreeRTOS
Referring to the illustration above, assume that priority 9 is the highest
priority, and none of the tasks in priority 9 will block hence will always be
either in the running or Ready state.
1) **PRO_CPU** calls the scheduler and selects Task A to run, hence moves
``pxIndex`` to point to Task A
2) **APP_CPU** calls the scheduler and starts traversing from the task after
``pxIndex`` which is Task B. However Task B is not selected to run as it is not
pinned to **APP_CPU** hence it is skipped and Task C is selected instead.
``pxIndex`` now points to Task C
3) **PRO_CPU** calls the scheduler and starts traversing from Task D. It skips
Task D and selects Task E to run and points ``pxIndex`` to Task E. Notice that
Task B isnt traversed because it was skipped the last time **APP_CPU** called
the scheduler to traverse the list.
4) The same situation with Task D will occur if **APP_CPU** calls the
scheduler again as ``pxIndex`` now points to Task E
One solution to the issue of task skipping is to ensure that every task will
enter a blocked state so that they are removed from the Ready Task List.
Another solution is to distribute tasks across multiple priorities such that
a given priority will not be assigned multiple tasks that are pinned to
different cores.
.. _scheduler-suspension:
Scheduler Suspension
^^^^^^^^^^^^^^^^^^^^
In vanilla FreeRTOS, suspending the scheduler via :cpp:func:`vTaskSuspendAll` will
prevent calls of ``vTaskSwitchContext`` from context switching until the
scheduler has been resumed with :cpp:func:`xTaskResumeAll`. However servicing ISRs
are still permitted. Therefore any changes in task states as a result from the
current running task or ISRSs will not be executed until the scheduler is
resumed. Scheduler suspension in vanilla FreeRTOS is a common protection method
against simultaneous access of data shared between tasks, whilst still allowing
ISRs to be serviced.
In ESP-IDF FreeRTOS, :cpp:func:`xTaskResumeAll` will only prevent calls of
``vTaskSwitchContext()`` from switching contexts on the core that called for the
suspension. Hence if **PRO_CPU** calls :cpp:func:`vTaskSuspendAll`, **APP_CPU** will
still be able to switch contexts. If data is shared between tasks that are
pinned to different cores, scheduler suspension is **NOT** a valid method of
protection against simultaneous access. Consider using critical sections
(disables interrupts) or semaphores (does not disable interrupts) instead when
protecting shared resources in ESP-IDF FreeRTOS.
In general, it's better to use other RTOS primitives like mutex semaphores to protect
against data shared between tasks, rather than :cpp:func:`vTaskSuspendAll`.
.. _tick-interrupt-synchronicity:
Tick Interrupt Synchronicity
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In ESP-IDF FreeRTOS, tasks on different cores that unblock on the same tick
count might not run at exactly the same time due to the scheduler calls from
each core being independent, and the tick interrupts to each core being
unsynchronized.
In vanilla FreeRTOS the tick interrupt triggers a call to
:cpp:func:`xTaskIncrementTick` which is responsible for incrementing the tick
counter, checking if tasks which have called :cpp:func:`vTaskDelay` have fulfilled
their delay period, and moving those tasks from the Delayed Task List to the
Ready Task List. The tick interrupt will then call the scheduler if a context
switch is necessary.
In ESP-IDF FreeRTOS, delayed tasks are unblocked with reference to the tick
interrupt on PRO_CPU as PRO_CPU is responsible for incrementing the shared tick
count. However tick interrupts to each core might not be synchronized (same
frequency but out of phase) hence when PRO_CPU receives a tick interrupt,
APP_CPU might not have received it yet. Therefore if multiple tasks of the same
priority are unblocked on the same tick count, the task pinned to PRO_CPU will
run immediately whereas the task pinned to APP_CPU must wait until APP_CPU
receives its out of sync tick interrupt. Upon receiving the tick interrupt,
APP_CPU will then call for a context switch and finally switches contexts to
the newly unblocked task.
Therefore, task delays should **NOT** be used as a method of synchronization
between tasks in ESP-IDF FreeRTOS. Instead, consider using a counting semaphore
to unblock multiple tasks at the same time.
.. _critical-sections:
Critical Sections & Disabling Interrupts
----------------------------------------
Vanilla FreeRTOS implements critical sections in ``vTaskEnterCritical`` which
disables the scheduler and calls ``portDISABLE_INTERRUPTS``. This prevents
context switches and servicing of ISRs during a critical section. Therefore,
critical sections are used as a valid protection method against simultaneous
access in vanilla FreeRTOS.
On the other hand, the ESP32 has no hardware method for cores to disable each
others interrupts. Calling ``portDISABLE_INTERRUPTS()`` will have no effect on
the interrupts of the other core. Therefore, disabling interrupts is **NOT**
a valid protection method against simultaneous access to shared data as it
leaves the other core free to access the data even if the current core has
disabled its own interrupts.
For this reason, ESP-IDF FreeRTOS implements critical sections using mutexes,
and calls to enter or exit a critical must provide a mutex that is associated
with a shared resource requiring access protection. When entering a critical
section in ESP-IDF FreeRTOS, the calling core will disable its scheduler and
interrupts similar to the vanilla FreeRTOS implementation. However, the calling
core will also take the mutex whilst the other core is left unaffected during
the critical section. If the other core attempts to take the same mutex, it
will spin until the mutex is released. Therefore, the ESP-IDF FreeRTOS
implementation of critical sections allows a core to have protected access to a
shared resource without disabling the other core. The other core will only be
affected if it tries to concurrently access the same resource.
The ESP-IDF FreeRTOS critical section functions have been modified as follows…
- ``taskENTER_CRITICAL(mux)``, ``taskENTER_CRITICAL_ISR(mux)``,
``portENTER_CRITICAL(mux)``, ``portENTER_CRITICAL_ISR(mux)`` are all macro
defined to call :cpp:func:`vTaskEnterCritical`
- ``taskEXIT_CRITICAL(mux)``, ``taskEXIT_CRITICAL_ISR(mux)``,
``portEXIT_CRITICAL(mux)``, ``portEXIT_CRITICAL_ISR(mux)`` are all macro
defined to call :cpp:func:`vTaskExitCritical`
For more details see :component_file:`freertos/include/freertos/portmacro.h`
and :component_file:`freertos/task.c`
It should be noted that when modifying vanilla FreeRTOS code to be ESP-IDF
FreeRTOS compatible, it is trivial to modify the type of critical section
called as they are all defined to call the same function. As long as the same
mutex is provided upon entering and exiting, the type of call should not
matter.
.. _floating-points:
Floating Point Aritmetic
------------------------
The ESP32 supports hardware acceleration of single precision floating point
arithmetic (``float``) via Floating Point Units (FPU, also known as coprocessors)
attached to each core. The use of the FPUs imposes some behavioral restrictions
on ESP-IDF FreeRTOS.
ESP-IDF FreeRTOS implements Lazy Context Switching for FPUs. In other words,
the state of a core's FPU registers are not immediately saved when a context
switch occurs. Therefore, tasks that utilize ``float`` must be pinned to a
particular core upon creation. If not, ESP-IDF FreeRTOS will automatically pin
the task in question to whichever core the task was running on upon the task's
first use of ``float``. Likewise due to Lazy Context Switching, interrupt service
routines must also not use ``float``.
ESP32 does not support hardware acceleration for double precision floating point
arithmetic (``double``). Instead ``double`` is implemented via software hence the
behavioral restrictions with regards to ``float`` do not apply to ``double``. Note
that due to the lack of hardware acceleration, ``double`` operations may consume
significantly larger amount of CPU time in comparison to ``float``.
.. _task-deletion:
Task Deletion
-------------
FreeRTOS task deletion prior to v9.0.0 delegated the freeing of task memory
entirely to the Idle Task. Currently, the freeing of task memory will occur
immediately (within :cpp:func:`vTaskDelete`) if the task being deleted is not currently
running or is not pinned to the other core (with respect to the core
:cpp:func:`vTaskDelete` is called on). TLSP deletion callbacks will also run immediately
if the same conditions are met.
However, calling :cpp:func:`vTaskDelete` to delete a task that is either currently
running or pinned to the other core will still result in the freeing of memory
being delegated to the Idle Task.
.. _deletion-callbacks:
Thread Local Storage Pointers & Deletion Callbacks
--------------------------------------------------
Thread Local Storage Pointers (TLSP) are pointers stored directly in the TCB.
TLSP allow each task to have its own unique set of pointers to data structures.
However task deletion behavior in vanilla FreeRTOS does not automatically
free the memory pointed to by TLSP. Therefore if the memory pointed to by
TLSP is not explicitly freed by the user before task deletion, memory leak will
occur.
ESP-IDF FreeRTOS provides the added feature of Deletion Callbacks. Deletion
Callbacks are called automatically during task deletion to free memory pointed
to by TLSP. Each TLSP can have its own Deletion Callback. Note that due to the
to :ref:`task-deletion` behavior, there can be instances where Deletion
Callbacks are called in the context of the Idle Tasks. Therefore Deletion
Callbacks **should never attempt to block** and critical sections should be kept
as short as possible to minimize priority inversion.
Deletion callbacks are of type
``void (*TlsDeleteCallbackFunction_t)( int, void * )`` where the first parameter
is the index number of the associated TLSP, and the second parameter is the
TLSP itself.
Deletion callbacks are set alongside TLSP by calling
:cpp:func:`vTaskSetThreadLocalStoragePointerAndDelCallback`. Calling the vanilla
FreeRTOS function :cpp:func:`vTaskSetThreadLocalStoragePointer` will simply set the
TLSP's associated Deletion Callback to `NULL` meaning that no callback will be
called for that TLSP during task deletion. If a deletion callback is `NULL`,
users should manually free the memory pointed to by the associated TLSP before
task deletion in order to avoid memory leak.
:ref:`CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS` in menuconfig can be used
to configure the number TLSP and Deletion Callbacks a TCB will have.
For more details see :doc:`FreeRTOS API reference<../api-reference/system/freertos>`.
.. _esp-idf-freertos-configuration:
Configuring ESP-IDF FreeRTOS
----------------------------
The ESP-IDF FreeRTOS can be configured using ``make menuconfig`` under
``Component_Config/FreeRTOS``. The following section highlights some of the
ESP-IDF FreeRTOS configuration options. For a full list of ESP-IDF
FreeRTOS configurations, see :doc:`FreeRTOS <../api-reference/kconfig>`
:ref:`CONFIG_FREERTOS_UNICORE` will run ESP-IDF FreeRTOS only
on **PRO_CPU**. Note that this is **not equivalent to running vanilla
FreeRTOS**. Behaviors of multiple components in ESP-IDF will be modified such
as :component_file:`esp32/cpu_start.c`. For more details regarding the
effects of running ESP-IDF FreeRTOS on a single core, search for
occurences of ``CONFIG_FREERTOS_UNICORE`` in the ESP-IDF components.
:ref:`CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS` will define the
number of Thread Local Storage Pointers each task will have in ESP-IDF
FreeRTOS.
:ref:`CONFIG_SUPPORT_STATIC_ALLOCATION` will enable the backported
functionality of :cpp:func:`xTaskCreateStaticPinnedToCore` in ESP-IDF FreeRTOS
:ref:`CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION` will trigger a halt in
particular functions in ESP-IDF FreeRTOS which have not been fully tested
in an SMP context.
+176
View File
@@ -0,0 +1,176 @@
General Notes About ESP-IDF Programming
=======================================
Application startup flow
------------------------
This note explains various steps which happen before ``app_main`` function of an ESP-IDF application is called.
The high level view of startup process is as follows:
1. First-stage bootloader in ROM loads second-stage bootloader image to RAM (IRAM & DRAM) from flash offset 0x1000.
2. Second-stage bootloader loads partition table and main app image from flash. Main app incorporates both RAM segments and read-only segments mapped via flash cache.
3. Main app image executes. At this point the second CPU and RTOS scheduler can be started.
This process is explained in detail in the following sections.
First stage bootloader
^^^^^^^^^^^^^^^^^^^^^^
After SoC reset, PRO CPU will start running immediately, executing reset vector code, while APP CPU will be held in reset. During startup process, PRO CPU does all the initialization. APP CPU reset is de-asserted in the ``call_start_cpu0`` function of application startup code. Reset vector code is located at address 0x40000400 in the mask ROM of the ESP32 chip and can not be modified.
Startup code called from the reset vector determines the boot mode by checking ``GPIO_STRAP_REG`` register for bootstrap pin states. Depending on the reset reason, the following takes place:
1. Reset from deep sleep: if the value in ``RTC_CNTL_STORE6_REG`` is non-zero, and CRC value of RTC memory in ``RTC_CNTL_STORE7_REG`` is valid, use ``RTC_CNTL_STORE6_REG`` as an entry point address and jump immediately to it. If ``RTC_CNTL_STORE6_REG`` is zero, or ``RTC_CNTL_STORE7_REG`` contains invalid CRC, or once the code called via ``RTC_CNTL_STORE6_REG`` returns, proceed with boot as if it was a power-on reset. **Note**: to run customized code at this point, a deep sleep stub mechanism is provided. Please see :doc:`deep sleep <deep-sleep-stub>` documentation for this.
2. For power-on reset, software SOC reset, and watchdog SOC reset: check the ``GPIO_STRAP_REG`` register if UART or SDIO download mode is requested. If this is the case, configure UART or SDIO, and wait for code to be downloaded. Otherwise, proceed with boot as if it was due to software CPU reset.
3. For software CPU reset and watchdog CPU reset: configure SPI flash based on EFUSE values, and attempt to load the code from flash. This step is described in more detail in the next paragraphs. If loading code from flash fails, unpack BASIC interpreter into the RAM and start it. Note that RTC watchdog is still enabled when this happens, so unless any input is received by the interpreter, watchdog will reset the SOC in a few hundred milliseconds, repeating the whole process. If the interpreter receives any input from the UART, it disables the watchdog.
Application binary image is loaded from flash starting at address 0x1000. First 4kB sector of flash is used to store secure boot IV and signature of the application image. Please check secure boot documentation for details about this.
.. TODO: describe application binary image format, describe optional flash configuration commands.
Second stage bootloader
^^^^^^^^^^^^^^^^^^^^^^^
In ESP-IDF, the binary image which resides at offset 0x1000 in flash is the second stage bootloader. Second stage bootloader source code is available in components/bootloader directory of ESP-IDF. Note that this arrangement is not the only one possible with the ESP32 chip. It is possible to write a fully featured application which would work when flashed to offset 0x1000, but this is out of scope of this document. Second stage bootloader is used in ESP-IDF to add flexibility to flash layout (using partition tables), and allow for various flows associated with flash encryption, secure boot, and over-the-air updates (OTA) to take place.
When the first stage bootloader is finished checking and loading the second stage bootloader, it jumps to the second stage bootloader entry point found in the binary image header.
Second stage bootloader reads the partition table found at offset 0x8000. See :doc:`partition tables <partition-tables>` documentation for more information. The bootloader finds factory and OTA partitions, and decides which one to boot based on data found in *OTA info* partition.
For the selected partition, second stage bootloader copies data and code sections which are mapped into IRAM and DRAM to their load addresses. For sections which have load addresses in DROM and IROM regions, flash MMU is configured to provide the correct mapping. Note that the second stage bootloader configures flash MMU for both PRO and APP CPUs, but it only enables flash MMU for PRO CPU. Reason for this is that second stage bootloader code is loaded into the memory region used by APP CPU cache. The duty of enabling cache for APP CPU is passed on to the application. Once code is loaded and flash MMU is set up, second stage bootloader jumps to the application entry point found in the binary image header.
Currently it is not possible to add application-defined hooks to the bootloader to customize application partition selection logic. This may be required to load different application image depending on a state of a GPIO, for example. Such customization features will be added to ESP-IDF in the future. For now, bootloader can be customized by copying bootloader component into application directory and making necessary changes there. ESP-IDF build system will compile the component in application directory instead of ESP-IDF components directory in this case.
Application startup
^^^^^^^^^^^^^^^^^^^
ESP-IDF application entry point is ``call_start_cpu0`` function found in ``components/esp32/cpu_start.c``. Two main things this function does are to enable heap allocator and to make APP CPU jump to its entry point, ``call_start_cpu1``. The code on PRO CPU sets the entry point for APP CPU, de-asserts APP CPU reset, and waits for a global flag to be set by the code running on APP CPU, indicating that it has started. Once this is done, PRO CPU jumps to ``start_cpu0`` function, and APP CPU jumps to ``start_cpu1`` function.
Both ``start_cpu0`` and ``start_cpu1`` are weak functions, meaning that they can be overridden in the application, if some application-specific change to initialization sequence is needed. Default implementation of ``start_cpu0`` enables or initializes components depending on choices made in ``menuconfig``. Please see source code of this function in ``components/esp32/cpu_start.c`` for an up to date list of steps performed. Note that any C++ global constructors present in the application will be called at this stage. Once all essential components are initialized, *main task* is created and FreeRTOS scheduler is started.
While PRO CPU does initialization in ``start_cpu0`` function, APP CPU spins in ``start_cpu1`` function, waiting for the scheduler to be started on the PRO CPU. Once the scheduler is started on the PRO CPU, code on the APP CPU starts the scheduler as well.
Main task is the task which runs ``app_main`` function. Main task stack size and priority can be configured in ``menuconfig``. Application can use this task for initial application-specific setup, for example to launch other tasks. Application can also use main task for event loops and other general purpose activities. If ``app_main`` function returns, main task is deleted.
.. _memory-layout:
Application memory layout
-------------------------
ESP32 chip has flexible memory mapping features. This section describes how ESP-IDF uses these features by default.
Application code in ESP-IDF can be placed into one of the following memory regions.
IRAM (instruction RAM)
^^^^^^^^^^^^^^^^^^^^^^
ESP-IDF allocates part of `Internal SRAM0` region (defined in the Technical Reference Manual) for instruction RAM. Except for the first 64 kB block which is used for PRO and APP CPU caches, the rest of this memory range (i.e. from ``0x40080000`` to ``0x400A0000``) is used to store parts of application which need to run from RAM.
A few components of ESP-IDF and parts of WiFi stack are placed into this region using the linker script.
If some application code needs to be placed into IRAM, it can be done using ``IRAM_ATTR`` define::
#include "esp_attr.h"
void IRAM_ATTR gpio_isr_handler(void* arg)
{
// ...
}
Here are the cases when parts of application may or should be placed into IRAM.
- Interrupt handlers must be placed into IRAM if ``ESP_INTR_FLAG_IRAM`` is used when registering the interrupt handler. In this case, ISR may only call functions placed into IRAM or functions present in ROM. *Note 1:* all FreeRTOS APIs are currently placed into IRAM, so are safe to call from interrupt handlers. If the ISR is placed into IRAM, all constant data used by the ISR and functions called from ISR (including, but not limited to, ``const char`` arrays), must be placed into DRAM using ``DRAM_ATTR``.
- Some timing critical code may be placed into IRAM to reduce the penalty associated with loading the code from flash. ESP32 reads code and data from flash via a 32 kB cache. In some cases, placing a function into IRAM may reduce delays caused by a cache miss.
IROM (code executed from Flash)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If a function is not explicitly placed into IRAM or RTC memory, it is placed into flash. The mechanism by which Flash MMU is used to allow code execution from flash is described in the Technical Reference Manual. ESP-IDF places the code which should be executed from flash starting from the beginning of ``0x400D0000 — 0x40400000`` region. Upon startup, second stage bootloader initializes Flash MMU to map the location in flash where code is located into the beginning of this region. Access to this region is transparently cached using two 32kB blocks in ``0x40070000````0x40080000`` range.
Note that the code outside ``0x40000000 — 0x40400000`` region may not be reachable with Window ABI ``CALLx`` instructions, so special care is required if ``0x40400000 — 0x40800000`` or ``0x40800000 — 0x40C00000`` regions are used by the application. ESP-IDF doesn't use these regions by default.
RTC fast memory
^^^^^^^^^^^^^^^
The code which has to run after wake-up from deep sleep mode has to be placed into RTC memory. Please check detailed description in :doc:`deep sleep <deep-sleep-stub>` documentation.
DRAM (data RAM)
^^^^^^^^^^^^^^^
Non-constant static data and zero-initialized data is placed by the linker into the 256 kB ``0x3FFB0000 — 0x3FFF0000`` region. Note that this region is reduced by 64kB (by shifting start address to ``0x3FFC0000``) if Bluetooth stack is used. Length of this region is also reduced by 16 kB or 32kB if trace memory is used. All space which is left in this region after placing static data there is used for the runtime heap.
Constant data may also be placed into DRAM, for example if it is used in an ISR (see notes in IRAM section above). To do that, ``DRAM_ATTR`` define can be used::
DRAM_ATTR const char[] format_string = "%p %x";
char buffer[64];
sprintf(buffer, format_string, ptr, val);
Needless to say, it is not advised to use ``printf`` and other output functions in ISRs. For debugging purposes, use ``ESP_EARLY_LOGx`` macros when logging from ISRs. Make sure that both ``TAG`` and format string are placed into ``DRAM`` in that case.
DROM (data stored in Flash)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
By default, constant data is placed by the linker into a 4 MB region (``0x3F400000 — 0x3F800000``) which is used to access external flash memory via Flash MMU and cache. Exceptions to this are literal constants which are embedded by the compiler into application code.
RTC slow memory
^^^^^^^^^^^^^^^
Global and static variables used by code which runs from RTC memory (i.e. deep sleep stub code) must be placed into RTC slow memory. Please check detailed description in :doc:`deep sleep <deep-sleep-stub>` documentation.
DMA Capable Requirement
-----------------------
Most DMA controllers (e.g. SPI, sdmmc, etc.) have requirements that sending/receiving buffers should be placed in DRAM
and word-aligned. We suggest to place DMA buffers in static variables rather than in the stack. Use macro ``DMA_ATTR``
to declare global/local static variables like::
DMA_ATTR uint8_t buffer[]="I want to send something";
void app_main()
{
// initialization code...
spi_transaction_t temp = {
.tx_buffer = buffer,
.length = 8*sizeof(buffer),
};
spi_device_transmit( spi, &temp );
// other stuff
}
Or::
void app_main()
{
DMA_ATTR static uint8_t buffer[]="I want to send something";
// initialization code...
spi_transaction_t temp = {
.tx_buffer = buffer,
.length = 8*sizeof(buffer),
};
spi_device_transmit( spi, &temp );
// other stuff
}
Placing DMA buffers in the stack is still allowed, though you have to keep in mind:
1. Never try to do this if the stack is in the pSRAM. If the stack of a task is placed in the pSRAM, several steps have
to be taken as described in :doc:`external-ram` (at least ``SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY`` option enabled in
the menuconfig). Make sure your task is not in the pSRAM.
2. Use macro ``WORD_ALIGNED_ATTR`` in functions before variables to place them in proper positions like::
void app_main()
{
uint8_t stuff;
WORD_ALIGNED_ATTR uint8_t buffer[]="I want to send something"; //or the buffer will be placed right after stuff.
// initialization code...
spi_transaction_t temp = {
.tx_buffer = buffer,
.length = 8*sizeof(buffer),
};
spi_device_transmit( spi, &temp );
// other stuff
}
+63
View File
@@ -0,0 +1,63 @@
High-Level Interrupts
=====================
.. toctree::
:maxdepth: 1
The Xtensa architecture has support for 32 interrupts, divided over 8 levels, plus an assortment of exceptions. On the ESP32, the interrupt mux allows most interrupt sources to be routed to these interrupts using the :doc:`interrupt allocator <../api-reference/system/intr_alloc>`. Normally, interrupts will be written in C, but ESP-IDF allows high-level interrupts to be written in assembly as well, allowing for very low interrupt latencies.
Interrupt Levels
----------------
===== ================= ====================================================
Level Symbol Remark
===== ================= ====================================================
1 N/A Exception and level 0 interrupts. Handled by ESP-IDF
2-3 N/A Medium level interrupts. Handled by ESP-IDF
4 xt_highint4 Normally used by ESP-IDF debug logic
5 xt_highint5 Free to use
NMI xt_nmi Free to use
dbg xt_debugexception Debug exception. Called on e.g. a BREAK instruction.
===== ================= ====================================================
Using these symbols is done by creating an assembly file (suffix .S) and defining the named symbols, like this::
.section .iram1,"ax"
.global xt_highint5
.type xt_highint5,@function
.align 4
xt_highint5:
... your code here
rsr a0, EXCSAVE_5
rfi 5
For a real-life example, see the components/esp32/panic_highint_hdl.S file; the panic handler iunterrupt is implemented there.
Notes
-----
- Do not call C code from a high-level interrupt; because these interupts still run in critical sections, this can cause crashes.
(The panic handler interrupt does call normal C code, but this is OK because there is no intention of returning to the normal code
flow afterwards.)
- Make sure your assembly code gets linked in. If the interrupt handler symbol is the only symbol the rest of the code uses from this
file, the linker will take the default ISR instead and not link the assembly file into the final project. To get around this, in the
assembly file, define a symbol, like this::
.global ld_include_my_isr_file
ld_include_my_isr_file:
(The symbol is called ``ld_include_my_isr_file`` here but can have any arbitrary name not defined anywhere else.)
Then, in the component.mk, add this file as an unresolved symbol to the ld command line arguments::
COMPONENT_ADD_LDFLAGS := -u ld_include_my_isr_file
This should cause the linker to always include a file defining ``ld_include_my_isr_file``, causing the ISR to always be linked in.
- High-level interrupts can be routed and handled using esp_intr_alloc and associated functions. The handler and handler arguments
to esp_intr_alloc must be NULL, however.
- In theory, medium priority interrupts could also be handled in this way. For now, ESP-IDF does not support this.
+24
View File
@@ -0,0 +1,24 @@
API Guides
**********
.. toctree::
:maxdepth: 1
General Notes <general-notes>
Build System <build-system>
Deep Sleep Wake Stubs <deep-sleep-stub>
ESP32 Core Dump <core_dump>
Flash Encryption <../security/flash-encryption>
FreeRTOS SMP Changes <freertos-smp>
Thread Local Storage <thread-local-storage>
High Level Interrupts <hlinterrupts>
JTAG Debugging <jtag-debugging/index>
Partition Tables <partition-tables>
Secure Boot <../security/secure-boot>
ULP Coprocessor <ulp>
Unit Testing <unit-tests>
Application Level Tracing <app_trace>
Console Component <console>
ROM debug console <romconsole>
WiFi Driver <wifi>
External SPI-connected RAM <external-ram>
@@ -0,0 +1,75 @@
***************************************
Building OpenOCD from Sources for Linux
***************************************
The following instructions are alternative to downloading binary OpenOCD from Espressif website. To quickly setup the binary OpenOCD, instead of compiling it yourself, backup and proceed to section :doc:`setup-openocd-linux`.
.. highlight:: bash
Download Sources of OpenOCD
===========================
The sources for the ESP32-enabled variant of OpenOCD are available from Espressif GitHub under https://github.com/espressif/openocd-esp32. To download the sources, use the following commands::
cd ~/esp
git clone recursive https://github.com/espressif/openocd-esp32.git
The clone of sources should be now saved in ``~/esp/openocd-esp32`` directory.
Install Dependencies
====================
Install packages that are required to compile OpenOCD.
.. note::
Install the following packages one by one, check if installation was successful and then proceed to the next package. Resolve reported problems before moving to the next step.
::
sudo apt-get install make
sudo apt-get install libtool
sudo apt-get install pkg-config
sudo apt-get install autoconf
sudo apt-get install automake
sudo apt-get install texinfo
sudo apt-get install libusb-1.0
.. note::
* Version of pkg-config should be 0.2.3 or above.
* Version of autoconf should be 2.6.4 or above.
* Version of automake should be 1.9 or above.
* When using USB-Blaster, ASIX Presto, OpenJTAG and FT2232 as adapters, drivers libFTDI and FTD2XX need to be downloaded and installed.
* When using CMSIS-DAP, HIDAPI is needed.
Build OpenOCD
=============
Proceed with configuring and building OpenOCD::
cd ~/esp/openocd-esp32
./bootstrap
./configure
make
Optionally you can add ``sudo make install`` step at the end. Skip it, if you have an existing OpenOCD (from e.g. another development platform), as it may get overwritten.
.. note::
* Should an error occur, resolve it and try again until the command ``make`` works.
* If there is a submodule problem from OpenOCD, please ``cd`` to the ``openocd-esp32`` directory and input ``git submodule update --init``.
* If the ``./configure`` is successfully run, information of enabled JTAG will be printed under ``OpenOCD configuration summary``.
* If the information of your device is not shown in the log, use ``./configure`` to enable it as described in ``../openocd-esp32/doc/INSTALL.txt``.
* For details concerning compiling OpenOCD, please refer to ``openocd-esp32/README``.
Once ``make`` process is successfully completed, the executable of OpenOCD will be saved in ``~/openocd-esp32/bin`` directory.
Next Steps
==========
To carry on with debugging environment setup, proceed to section :ref:`jtag-debugging-configuring-esp32-target`.
@@ -0,0 +1,54 @@
***************************************
Building OpenOCD from Sources for MacOS
***************************************
The following instructions are alternative to downloading binary OpenOCD from Espressif website. To quickly setup the binary OpenOCD, instead of compiling it yourself, backup and proceed to section :doc:`setup-openocd-macos`.
.. highlight:: bash
Download Sources of OpenOCD
===========================
The sources for the ESP32-enabled variant of OpenOCD are available from Espressif GitHub under https://github.com/espressif/openocd-esp32. To download the sources, use the following commands::
cd ~/esp
git clone recursive https://github.com/espressif/openocd-esp32.git
The clone of sources should be now saved in ``~/esp/openocd-esp32`` directory.
Install Dependencies
====================
Install packages that are required to compile OpenOCD using Homebrew::
brew install automake libtool libusb wget gcc@4.9
Build OpenOCD
=============
Proceed with configuring and building OpenOCD::
cd ~/esp/openocd-esp32
./bootstrap
./configure
make
Optionally you can add ``sudo make install`` step at the end. Skip it, if you have an existing OpenOCD (from e.g. another development platform), as it may get overwritten.
.. note::
* Should an error occur, resolve it and try again until the command ``make`` works.
* If there is a submodule problem from OpenOCD, please ``cd`` to the ``openocd-esp32`` directory and input ``git submodule update --init``.
* If the ``./configure`` is successfully run, information of enabled JTAG will be printed under ``OpenOCD configuration summary``.
* If the information of your device is not shown in the log, use ``./configure`` to enable it as described in ``../openocd-esp32/doc/INSTALL.txt``.
* For details concerning compiling OpenOCD, please refer to ``openocd-esp32/README.OSX``.
Once ``make`` process is successfully completed, the executable of OpenOCD will be saved in ``~/esp/openocd-esp32/src/openocd`` directory.
Next Steps
==========
To carry on with debugging environment setup, proceed to section :ref:`jtag-debugging-configuring-esp32-target`.
@@ -0,0 +1,74 @@
*****************************************
Building OpenOCD from Sources for Windows
*****************************************
The following instructions are alternative to downloading binary OpenOCD from Espressif website. To quickly setup the binary OpenOCD, instead of compiling it yourself, backup and proceed to section :doc:`setup-openocd-windows`.
.. highlight:: bash
Download Sources of OpenOCD
===========================
The sources for the ESP32-enabled variant of OpenOCD are available from Espressif GitHub under https://github.com/espressif/openocd-esp32. To download the sources, use the following commands::
cd ~/esp
git clone recursive https://github.com/espressif/openocd-esp32.git
The clone of sources should be now saved in ``~/esp/openocd-esp32`` directory.
Install Dependencies
====================
Install packages that are required to compile OpenOCD:
.. note::
Install the following packages one by one, check if installation was successful and then proceed to the next package. Resolve reported problems before moving to the next step.
::
pacman -S libtool
pacman -S autoconf
pacman -S automake
pacman -S texinfo
pacman -S mingw-w64-i686-libusb-compat-git
pacman -S pkg-config
.. note::
Installation of ``pkg-config`` is breaking operation of esp-idf toolchain. After building of OpenOCD it should be uninstalled. It be covered at the end of this instruction. To build OpenOCD again, you will need to run ``pacman -S pkg-config`` once more. This issue does not concern other packages installed in this step (before ``pkg-config``).
Build OpenOCD
=============
Proceed with configuring and building OpenOCD::
cd ~/esp/openocd-esp32
./bootstrap
./configure
make
Optionally you can add ``make install`` step at the end. Skip it, if you have an existing OpenOCD (from e.g. another development platform), as it may get overwritten.
.. note::
* Should an error occur, resolve it and try again until the command ``make`` works.
* If there is a submodule problem from OpenOCD, please ``cd`` to the ``openocd-esp32`` directory and input ``git submodule update --init``.
* If the ``./configure`` is successfully run, information of enabled JTAG will be printed under ``OpenOCD configuration summary``.
* If the information of your device is not shown in the log, use ``./configure`` to enable it as described in ``../openocd-esp32/doc/INSTALL.txt``.
* For details concerning compiling OpenOCD, please refer to ``openocd-esp32/README.Windows``.
Once ``make`` process is successfully completed, the executable of OpenOCD will be saved in ``~/esp/openocd-esp32/src/openocd`` directory.
Remove ``pkg-config``, as discussed during installation of dependencies::
pacman -Rs pkg-config
Next Steps
==========
To carry on with debugging environment setup, proceed to section :ref:`jtag-debugging-configuring-esp32-target`.
@@ -0,0 +1,45 @@
Configure Other JTAG Interface
==============================
Refer to section :ref:`jtag-debugging-selecting-jtag-adapter` for guidance what JTAG interface to select, so it is able to operate with OpenOCD and ESP32. Then follow three configuration steps below to get it working.
Configure Hardware
^^^^^^^^^^^^^^^^^^
1. Identify all pins / signals on JTAG interface and ESP32 board, that should be connected to establish communication.
+---+---------------+-------------+
| | ESP32 Pin | JTAG Signal |
+===+===============+=============+
| 1 | CHIP_PU | TRST_N |
+---+---------------+-------------+
| 2 | MTDO / GPIO15 | TDO |
+---+---------------+-------------+
| 3 | MTDI / GPIO12 | TDI |
+---+---------------+-------------+
| 4 | MTCK / GPIO13 | TCK |
+---+---------------+-------------+
| 5 | MTMS / GPIO14 | TMS |
+---+---------------+-------------+
| 6 | GND | GND |
+---+---------------+-------------+
2. Verify if ESP32 pins used for JTAG communication are not connected to some other h/w that may disturb JTAG operation.
3. Connect identified pin / signals of ESP32 and JTAG interface.
Configure Drivers
^^^^^^^^^^^^^^^^^
You may need to install driver s/w to make JTAG work with computer. Refer to documentation of JTAG adapter, that should provide related details.
Connect
^^^^^^^
Connect JTAG interface to the computer. Power on ESP32 and JTAG interface boards. Check if JTAG interface is visible by computer.
To carry on with debugging environment setup, proceed to section :ref:`jtag-debugging-run-openocd`.
@@ -0,0 +1,195 @@
Configure WROVER JTAG Interface
===============================
All versions of ESP32 WROVER KIT boards have JTAG functionality build in. Putting it to work requires setting jumpers to enable JTAG functionality, setting SPI flash voltage and configuring USB drivers. Please refer to step by step instructions below.
Configure Hardware
^^^^^^^^^^^^^^^^^^
1. Enable on-board JTAG functionality by setting JP8 according to :doc:`../../get-started/get-started-wrover-kit`, section :ref:`get-started-esp-wrover-kit-setup-options`.
2. Verify if ESP32 pins used for JTAG communication are not connected to some other h/w that may disturb JTAG operation:
+---+---------------+-------------+
| | ESP32 Pin | JTAG Signal |
+===+===============+=============+
| 1 | CHIP_PU | TRST_N |
+---+---------------+-------------+
| 2 | MTDO / GPIO15 | TDO |
+---+---------------+-------------+
| 3 | MTDI / GPIO12 | TDI |
+---+---------------+-------------+
| 4 | MTCK / GPIO13 | TCK |
+---+---------------+-------------+
| 5 | MTMS / GPIO14 | TMS |
+---+---------------+-------------+
Configure USB Drivers
^^^^^^^^^^^^^^^^^^^^^
Install and configure USB drivers, so OpenOCD is able to communicate with JTAG interface on ESP32 WROVER KIT board as well as with UART interface used to upload application for flash. Follow steps below specific to your operating system.
.. note:: ESP32 WROVER KIT uses an FT2232 adapter. The following instructions can also be used for other FT2232 based JTAG adapters.
Windows
"""""""
1. Using standard USB A / micro USB B cable connect ESP32 WROVER KIT to the computer. Switch the WROVER KIT on.
2. Wait until USB ports of WROVER KIT are recognized by Windows and drives are installed. If they do not install automatically, then then download them from http://www.ftdichip.com/Drivers/D2XX.htm and install manually.
3. Download Zadig tool (Zadig_X.X.exe) from http://zadig.akeo.ie/ and run it.
4. In Zadig tool go to "Options" and check "List All Devices".
5. Check the list of devices that should contain two WROVER specific USB entries: "Dual RS232-HS (Interface 0)" and "Dual RS232-HS (Interface 1)". The driver name would be "FTDIBUS (vxxxx)" and USB ID: 0403 6010.
.. figure:: ../../_static/jtag-usb-configuration-zadig.jpg
:align: center
:alt: Configuration of JTAG USB driver in Zadig tool
:figclass: align-center
Configuration of JTAG USB driver in Zadig tool
6. The first device (Dual RS232-HS (Interface 0)) is connected to the JTAG port of the ESP32. Original "FTDIBUS (vxxxx)" driver of this device should be replaced with "WinUSB (v6xxxxx)". To do so, select "Dual RS232-HS (Interface 0) and reinstall attached driver to the "WinUSB (v6xxxxx)", see picture above.
.. note::
Do not change the second device "Dual RS232-HS (Interface 1)". It is routed to ESP32's serial port (UART) used for upload of application to ESP32's flash.
Now ESP32 WROVER KIT's JTAG interface should be available to the OpenOCD. To carry on with debugging environment setup, proceed to section :ref:`jtag-debugging-run-openocd`.
Linux
"""""
1. Using standard USB A / micro USB B cable connect ESP32 WROVER KIT board to the computer. Power on the board.
.. highlight:: none
2. Open a terminal, enter ``ls -l /dev/ttyUSB*`` command and check, if board's USB ports are recognized by the OS. You are looking for similar result:
::
user-name@computer-name:~/esp$ ls -l /dev/ttyUSB*
crw-rw---- 1 root dialout 188, 0 Jul 10 19:04 /dev/ttyUSB0
crw-rw---- 1 root dialout 188, 1 Jul 10 19:04 /dev/ttyUSB1
3. Following section "Permissions delegation" in OpenOCD's README, set up the access permissions to both USB ports.
4. Log off and login, then cycle the power to the board to make the changes effective. In terminal enter again ``ls -l /dev/ttyUSB*`` command to verify, if group-owner has changed from ``dialout`` to ``plugdev``:
::
user-name@computer-name:~/esp$ ls -l /dev/ttyUSB*
crw-rw-r-- 1 root plugdev 188, 0 Jul 10 19:07 /dev/ttyUSB0
crw-rw-r-- 1 root plugdev 188, 1 Jul 10 19:07 /dev/ttyUSB1
If you see similar result and you are a member of ``plugdev`` group, then the set up is complete.
The ``/dev/ttyUSBn`` interface with lower number is used for JTAG communication. The other interface is routed to ESP32's serial port (UART) used for upload of application to ESP32's flash.
Now ESP32 WROVER KIT's JTAG interface should be available to the OpenOCD. To carry on with debugging environment setup, proceed to section :ref:`jtag-debugging-run-openocd`.
MacOS
"""""
On macOS, using FT2232 for JTAG and serial port at the same time needs some additional steps. When the OS loads FTDI serial port driver, it does so for both channels of FT2232 chip. However only one of these channels is used as a serial port, while the other is used as JTAG. If the OS has loaded FTDI serial port driver for the channel used for JTAG, OpenOCD will not be able to connect to to the chip. There are two ways around this:
1. Manually unload the FTDI serial port driver before starting OpenOCD, start OpenOCD, then load the serial port driver.
2. Modify FTDI driver configuration so that it doesn't load itself for channel B of FT2232 chip, which is the channel used for JTAG on WROVER KIT.
Manually unloading the driver
.............................
1. Install FTDI driver from http://www.ftdichip.com/Drivers/VCP.htm
2. Connect USB cable to the WROVER KIT.
3. Unload the serial port driver::
sudo kextunload -b com.FTDI.driver.FTDIUSBSerialDriver
In some cases you may need to unload Apple's FTDI driver as well::
sudo kextunload -b com.apple.driver.AppleUSBFTDI
4. Run OpenOCD (paths are given for downloadable OpenOCD archive)::
bin/openocd -s share/openocd/scripts -f interface/ftdi/esp32_devkitj_v1.cfg -f board/esp-wroom-32.cfg
Or, if OpenOCD was built from source::
src/openocd -s tcl -f interface/ftdi/esp32_devkitj_v1.cfg -f board/esp-wroom-32.cfg
5. In another terminal window, load FTDI serial port driver again::
sudo kextload -b com.FTDI.driver.FTDIUSBSerialDriver
Note that if you need to restart OpenOCD, there is no need to unload FTDI driver again — just stop OpenOCD and start it again. The driver only needs to be unloaded if WROVER KIT was reconnected or power was toggled.
This procedure can be wrapped into a shell script, if desired.
Modifying FTDI driver
.....................
In a nutshell, this approach requires modification to FTDI driver configuration file, which prevents the driver from being loaded for channel B of FT2232H.
.. note:: Other boards may use channel A for JTAG, so use this option with caution.
.. warning:: This approach also needs signature verification of drivers to be disabled, so may not be acceptable for all users.
1. Open FTDI driver configuration file using a text editor (note ``sudo``)::
sudo nano /Library/Extensions/FTDIUSBSerialDriver.kext/Contents/Info.plist
2. Find and delete the following lines::
<key>FT2232H_B</key>
<dict>
<key>CFBundleIdentifier</key>
<string>com.FTDI.driver.FTDIUSBSerialDriver</string>
<key>IOClass</key>
<string>FTDIUSBSerialDriver</string>
<key>IOProviderClass</key>
<string>IOUSBInterface</string>
<key>bConfigurationValue</key>
<integer>1</integer>
<key>bInterfaceNumber</key>
<integer>1</integer>
<key>bcdDevice</key>
<integer>1792</integer>
<key>idProduct</key>
<integer>24592</integer>
<key>idVendor</key>
<integer>1027</integer>
</dict>
3. Save and close the file
4. Disable driver signature verification:
1. Open Apple logo menu, choose "Restart..."
2. When you hear the chime after reboot, press CMD+R immediately
3. Once Recovery mode starts up, open Terminal
4. Run the command::
csrutil enable --without kext
5. Restart again
After these steps, serial port and JTAG can be used at the same time.
To carry on with debugging environment setup, proceed to section :ref:`jtag-debugging-run-openocd`.
@@ -0,0 +1,655 @@
Debugging Examples
==================
This section describes debugging with GDB from :ref:`jtag-debugging-examples-eclipse` as well as from :ref:`jtag-debugging-examples-command-line`.
.. highlight:: none
.. _jtag-debugging-examples-eclipse:
Eclipse
-------
Verify if your target is ready and loaded with :example:`get-started/blink` example. Configure and start debugger following steps in section :ref:`jtag-debugging-using-debugger-eclipse`. Pick up where target was left by debugger, i.e. having the application halted at breakpoint established at ``app_main()``.
.. figure:: ../../_static/debug-perspective.jpg
:align: center
:alt: Debug Perspective in Eclipse
:figclass: align-center
Debug Perspective in Eclipse
Examples in this section
^^^^^^^^^^^^^^^^^^^^^^^^
1. :ref:`jtag-debugging-examples-eclipse-01`
2. :ref:`jtag-debugging-examples-eclipse-02`
3. :ref:`jtag-debugging-examples-eclipse-03`
4. :ref:`jtag-debugging-examples-eclipse-04`
5. :ref:`jtag-debugging-examples-eclipse-05`
6. :ref:`jtag-debugging-examples-eclipse-06`
7. :ref:`jtag-debugging-examples-eclipse-07`
.. _jtag-debugging-examples-eclipse-01:
Navigating though the code, call stack and threads
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When the target is halted, debugger shows the list of threads in "Debug" window. The line of code where program halted is highlighted in another window below, as shown on the following picture. The LED stops blinking.
.. figure:: ../../_static/debugging-target-halted.jpg
:align: center
:alt: Target halted during debugging
:figclass: align-center
Target halted during debugging
Specific thread where the program halted is expanded showing the call stack. It represents function calls that lead up to the highlighted line of code, where the target halted. The first line of call stack under Thread #1 contains the last called function ``app_main()``, that in turn was called from function ``main_task()`` shown in a line below. Each line of the stack also contains the file name and line number where the function was called. By clicking / highlighting the stack entries, in window below, you will see contents of this file.
By expanding threads you can navigate throughout the application. Expand Thread #5 that contains much longer call stack. You will see there, besides function calls, numbers like ``0x4000000c``. They represent addresses of binary code not provided in source form.
.. figure:: ../../_static/debugging-navigate-through-the-stack.jpg
:align: center
:alt: Navigate through the call stack
:figclass: align-center
Navigate through the call stack
In another window on right, you can see the disassembled machine code no matter if your project provides it in source or only the binary form.
Go back to the ``app_main()`` in Thread #1 to familiar code of ``blink.c`` file that will be examined in more details in the following examples. Debugger makes it easy to navigate through the code of entire application. This comes handy when stepping though the code and working with breakpoints and will be discussed below.
.. _jtag-debugging-examples-eclipse-02:
Setting and clearing breakpoints
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When debugging, we would like to be able to stop the application at critical lines of code and then examine the state of specific variables, memory and registers / peripherals. To do so we are using breakpoints. They provide a convenient way to quickly get to and halt the application at specific line.
Let's establish two breakpoints when the state of LED changes. Basing on code listing above, this happens at lines 33 and 36. To do so, hold the "Control" on the keyboard and double clink on number ``33`` in file ``blink.c`` file. A dialog will open where you can confirm your selection by pressing "OK" button. If you do not like to see the dialog just double click the line number. Set another breakpoint in line 36.
.. figure:: ../../_static/debugging-setting-breakpoint.jpg
:align: center
:alt: Setting a breakpoint
:figclass: align-center
Setting a breakpoint
Information how many breakpoints are set and where is shown in window "Breakpoints" on top right. Click "Show Breakpoints Supported by Selected Target" to refresh this list. Besides the two just set breakpoints the list may contain temporary breakpoint at function ``app_main()`` established at debugger start. As maximum two breakpoints are allowed (see :ref:`jtag-debugging-tip-breakpoints`), you need to delete it, or debugging will fail.
.. figure:: ../../_static/debugging-three-breakpoints-set.jpg
:align: center
:alt: Three breakpoints are set / maximum two are allowed
:figclass: align-center
Three breakpoints are set / maximum two are allowed
If you now click "Resume" (click ``blink_task()`` under "Tread #8", if "Resume" button is grayed out), the processor will run and halt at a breakpoint. Clicking "Resume" another time will make it run again, halt on second breakpoint, and so on.
You will be also able to see that LED is changing the state after each click to "Resume" program execution.
Read more about breakpoints under :ref:`jtag-debugging-tip-breakpoints` and :ref:`jtag-debugging-tip-where-breakpoints`
.. _jtag-debugging-examples-eclipse-03:
Halting the target manually
^^^^^^^^^^^^^^^^^^^^^^^^^^^
When debugging, you may resume application and enter code waiting for some event or staying in infinite loop without any break points defined. In such case, to go back to debugging mode, you can break program execution manually by pressing "Suspend" button.
To check it, delete all breakpoints and click "Resume". Then click "Suspend". Application will be halted at some random point and LED will stop blinking. Debugger will expand tread and highlight the line of code where application halted.
.. figure:: ../../_static/debugging-target-halted-manually.jpg
:align: center
:alt: Target halted manually
:figclass: align-center
Target halted manually
In particular case above, the application has been halted in line 52 of code in file ``freertos_hooks.c`` Now you can resume it again by pressing "Resume" button or do some debugging as discussed below.
.. _jtag-debugging-examples-eclipse-04:
Stepping through the code
^^^^^^^^^^^^^^^^^^^^^^^^^
It is also possible to step through the code using "Step Into (F5)" and "Step Over (F6)" commands. The difference is that "Step Into (F5)" is entering inside subroutines calls, while "Step Over (F6)" steps over the call, treating it as a single source line.
Before being able to demonstrate this functionality, using information discussed in previous paragraph, make sure that you have only one breakpoint defined at line ``36`` of ``blink.c``.
Resume program by entering pressing F8 and let it halt. Now press "Step Over (F6)", one by one couple of times, to see how debugger is stepping one program line at a time.
.. figure:: ../../_static/debugging-step-over.jpg
:align: center
:alt: Stepping through the code with "Step Over (F6)"
:figclass: align-center
Stepping through the code with "Step Over (F6)"
If you press "Step Into (F5)" instead, then debugger will step inside subroutine calls.
.. figure:: ../../_static/debugging-step-into.jpg
:align: center
:alt: Stepping through the code with "Step Into (F5)"
:figclass: align-center
Stepping through the code with "Step Into (F5)"
In this particular case debugger stepped inside ``gpio_set_level(BLINK_GPIO, 0)`` and effectively moved to ``gpio.c`` driver code.
See :ref:`jtag-debugging-tip-why-next-works-as-step` for potential limitation of using ``next`` command.
.. _jtag-debugging-examples-eclipse-05:
Checking and setting memory
^^^^^^^^^^^^^^^^^^^^^^^^^^^
To display or set contents of memory use "Memory" tab at the bottom of "Debug" perspective.
With the "Memory" tab, we will read from and write to the memory location ``0x3FF44004`` labeled as ``GPIO_OUT_REG`` used to set and clear individual GPIO's. For more information please refer to `ESP32 Technical Reference Manual <https://espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf>`__, chapter IO_MUX and GPIO Matrix.
Being in the same ``blink.c`` project as before, set two breakpoints right after ``gpio_set_level`` instruction. Click "Memory" tab and then "Add Memory Monitor" button. Enter ``0x3FF44004`` in provided dialog.
Now resume program by pressing F8 and observe "Monitor" tab.
.. figure:: ../../_static/debugging-memory-location-on.jpg
:align: center
:alt: Observing memory location 0x3FF44004 changing one bit to ON"
:figclass: align-center
Observing memory location 0x3FF44004 changing one bit to ON"
You should see one bit being flipped over at memory location ``0x3FF44004`` (and LED changing the state) each time F8 is pressed.
.. figure:: ../../_static/debugging-memory-location-off.jpg
:align: center
:alt: Observing memory location 0x3FF44004 changing one bit to ON"
:figclass: align-center
Observing memory location 0x3FF44004 changing one bit to ON"
To set memory use the same "Monitor" tab and the same memory location. Type in alternate bit pattern as previously observed. Immediately after pressing enter you will see LED changing the state.
.. _jtag-debugging-examples-eclipse-06:
Watching and setting program variables
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A common debugging tasks is checking the value of a program variable as the program runs. To be able to demonstrate this functionality, update file ``blink.c`` by adding a declaration of a global variable ``int i`` above definition of function ``blink_task``. Then add ``i++`` inside ``loop(1)`` of this function to get ``i`` incremented on each blink.
Exit debugger, so it is not confused with new code, build and flash the code to the ESP and restart debugger. There is no need to restart OpenOCD.
Once application is halted, enter a breakpoint in the line where you put ``i++``.
In next step, in the window with "Breakpoints", click the "Expressions" tab. If this tab is not visible, then add it by going to the top menu Window > Show View > Expressions. Then click "Add new expression" and enter ``i``.
Resume program execution by pressing F8. Each time the program is halted you will see ``i`` value being incremented.
.. figure:: ../../_static/debugging-watch-variable.jpg
:align: center
:alt: Watching program variable "i"
:figclass: align-center
Watching program variable "i"
To modify ``i`` enter a new number in "Value" column. After pressing "Resume (F8)" the program will keep incrementing ``i`` starting from the new entered number.
.. _jtag-debugging-examples-eclipse-07:
Setting conditional breakpoints
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Here comes more interesting part. You may set a breakpoint to halt the program execution, if certain condition is satisfied. Right click on the breakpoint to open a context menu and select "Breakpoint Properties". Change the selection under "Type:" to "Hardware" and enter a "Condition:" like ``i == 2``.
.. figure:: ../../_static/debugging-setting-conditional-breakpoint.jpg
:align: center
:alt: Setting a conditional breakpoint
:figclass: align-center
Setting a conditional breakpoint
If current value of ``i`` is less than ``2`` (change it if required) and program is resumed, it will blink LED in a loop until condition ``i == 2`` gets true and then finally halt.
.. _jtag-debugging-examples-command-line:
Command Line
------------
Verify if your target is ready and loaded with :example:`get-started/blink` example. Configure and start debugger following steps in section :ref:`jtag-debugging-using-debugger-command-line`. Pick up where target was left by debugger, i.e. having the application halted at breakpoint established at ``app_main()``::
Temporary breakpoint 1, app_main () at /home/user-name/esp/blink/main/./blink.c:43
43 xTaskCreate(&blink_task, "blink_task", configMINIMAL_STACK_SIZE, NULL, 5, NULL);
(gdb)
Examples in this section
^^^^^^^^^^^^^^^^^^^^^^^^
1. :ref:`jtag-debugging-examples-command-line-01`
2. :ref:`jtag-debugging-examples-command-line-02`
3. :ref:`jtag-debugging-examples-command-line-03`
4. :ref:`jtag-debugging-examples-command-line-04`
5. :ref:`jtag-debugging-examples-command-line-05`
6. :ref:`jtag-debugging-examples-command-line-06`
7. :ref:`jtag-debugging-examples-command-line-07`
.. _jtag-debugging-examples-command-line-01:
Navigating though the code, call stack and threads
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When you see the ``(gdb)`` prompt, the application is halted. LED should not be blinking.
To find out where exactly the code is halted, enter ``l`` or ``list``, and debugger will show couple of lines of code around the halt point (line 43 of code in file ``blink.c``) ::
(gdb) l
38 }
39 }
40
41 void app_main()
42 {
43 xTaskCreate(&blink_task, "blink_task", configMINIMAL_STACK_SIZE, NULL, 5, NULL);
44 }
(gdb)
Check how code listing works by entering, e.g. ``l 30, 40`` to see particular range of lines of code.
You can use ``bt`` or ``backtrace`` to see what function calls lead up to this code::
(gdb) bt
#0 app_main () at /home/user-name/esp/blink/main/./blink.c:43
#1 0x400d057e in main_task (args=0x0) at /home/user-name/esp/esp-idf/components/esp32/./cpu_start.c:339
(gdb)
Line #0 of output provides the last function call before the application halted, i.e. ``app_main ()`` we have listed previously. The ``app_main ()`` was in turn called by function ``main_task`` from line 339 of code located in file ``cpu_start.c``.
To get to the context of ``main_task`` in file ``cpu_start.c``, enter ``frame N``, where N = 1, because the ``main_task`` is listed under #1)::
(gdb) frame 1
#1 0x400d057e in main_task (args=0x0) at /home/user-name/esp/esp-idf/components/esp32/./cpu_start.c:339
339 app_main();
(gdb)
Enter ``l`` and this will reveal the piece of code that called ``app_main()`` (in line 339)::
(gdb) l
334 ;
335 }
336 #endif
337 //Enable allocation in region where the startup stacks were located.
338 heap_caps_enable_nonos_stack_heaps();
339 app_main();
340 vTaskDelete(NULL);
341 }
342
(gdb)
By listing some lines before, you will see the function name ``main_task`` we have been looking for::
(gdb) l 326, 341
326 static void main_task(void* args)
327 {
328 // Now that the application is about to start, disable boot watchdogs
329 REG_CLR_BIT(TIMG_WDTCONFIG0_REG(0), TIMG_WDT_FLASHBOOT_MOD_EN_S);
330 REG_CLR_BIT(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_FLASHBOOT_MOD_EN);
331 #if !CONFIG_FREERTOS_UNICORE
332 // Wait for FreeRTOS initialization to finish on APP CPU, before replacing its startup stack
333 while (port_xSchedulerRunning[1] == 0) {
334 ;
335 }
336 #endif
337 //Enable allocation in region where the startup stacks were located.
338 heap_caps_enable_nonos_stack_heaps();
339 app_main();
340 vTaskDelete(NULL);
341 }
(gdb)
To see the other code, enter ``i threads``. This will show the list of threads running on target::
(gdb) i threads
Id Target Id Frame
8 Thread 1073411336 (dport) 0x400d0848 in dport_access_init_core (arg=<optimized out>)
at /home/user-name/esp/esp-idf/components/esp32/./dport_access.c:170
7 Thread 1073408744 (ipc0) xQueueGenericReceive (xQueue=0x3ffae694, pvBuffer=0x0, xTicksToWait=1644638200,
xJustPeeking=0) at /home/user-name/esp/esp-idf/components/freertos/./queue.c:1452
6 Thread 1073431096 (Tmr Svc) prvTimerTask (pvParameters=0x0)
at /home/user-name/esp/esp-idf/components/freertos/./timers.c:445
5 Thread 1073410208 (ipc1 : Running) 0x4000bfea in ?? ()
4 Thread 1073432224 (dport) dport_access_init_core (arg=0x0)
at /home/user-name/esp/esp-idf/components/esp32/./dport_access.c:150
3 Thread 1073413156 (IDLE) prvIdleTask (pvParameters=0x0)
at /home/user-name/esp/esp-idf/components/freertos/./tasks.c:3282
2 Thread 1073413512 (IDLE) prvIdleTask (pvParameters=0x0)
at /home/user-name/esp/esp-idf/components/freertos/./tasks.c:3282
* 1 Thread 1073411772 (main : Running) app_main () at /home/user-name/esp/blink/main/./blink.c:43
(gdb)
The thread list shows the last function calls per each thread together with the name of C source file if available.
You can navigate to specific thread by entering ``thread N``, where ``N`` is the thread Id. To see how it works go to thread thread 5::
(gdb) thread 5
[Switching to thread 5 (Thread 1073410208)]
#0 0x4000bfea in ?? ()
(gdb)
Then check the backtrace::
(gdb) bt
#0 0x4000bfea in ?? ()
#1 0x40083a85 in vPortCPUReleaseMutex (mux=<optimized out>) at /home/user-name/esp/esp-idf/components/freertos/./port.c:415
#2 0x40083fc8 in vTaskSwitchContext () at /home/user-name/esp/esp-idf/components/freertos/./tasks.c:2846
#3 0x4008532b in _frxt_dispatch ()
#4 0x4008395c in xPortStartScheduler () at /home/user-name/esp/esp-idf/components/freertos/./port.c:222
#5 0x4000000c in ?? ()
#6 0x4000000c in ?? ()
#7 0x4000000c in ?? ()
#8 0x4000000c in ?? ()
(gdb)
As you see, the backtrace may contain several entries. This will let you check what exact sequence of function calls lead to the code where the target halted. Question marks ``??`` instead of a function name indicate that application is available only in binary format, without any source file in C language. The value like ``0x4000bfea`` is the memory address of the function call.
Using ``bt``, ``i threads``, ``thread N`` and ``list`` commands we are now able to navigate through the code of entire application. This comes handy when stepping though the code and working with breakpoints and will be discussed below.
.. _jtag-debugging-examples-command-line-02:
Setting and clearing breakpoints
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When debugging, we would like to be able to stop the application at critical lines of code and then examine the state of specific variables, memory and registers / peripherals. To do so we are using breakpoints. They provide a convenient way to quickly get to and halt the application at specific line.
Let's establish two breakpoints when the state of LED changes. Basing on code listing above this happens at lines 33 and 36. Breakpoints may be established using command ``break M`` where M is the code line number::
(gdb) break 33
Breakpoint 2 at 0x400db6f6: file /home/user-name/esp/blink/main/./blink.c, line 33.
(gdb) break 36
Breakpoint 3 at 0x400db704: file /home/user-name/esp/blink/main/./blink.c, line 36.
If you new enter ``c``, the processor will run and halt at a breakpoint. Entering ``c`` another time will make it run again, halt on second breakpoint, and so on::
(gdb) c
Continuing.
Target halted. PRO_CPU: PC=0x400DB6F6 (active) APP_CPU: PC=0x400D10D8
Breakpoint 2, blink_task (pvParameter=0x0) at /home/user-name/esp/blink/main/./blink.c:33
33 gpio_set_level(BLINK_GPIO, 0);
(gdb) c
Continuing.
Target halted. PRO_CPU: PC=0x400DB6F8 (active) APP_CPU: PC=0x400D10D8
Target halted. PRO_CPU: PC=0x400DB704 (active) APP_CPU: PC=0x400D10D8
Breakpoint 3, blink_task (pvParameter=0x0) at /home/user-name/esp/blink/main/./blink.c:36
36 gpio_set_level(BLINK_GPIO, 1);
(gdb)
You will be also able to see that LED is changing the state only if you resume program execution by entering ``c``.
To examine how many breakpoints are set and where, use command ``info break``::
(gdb) info break
Num Type Disp Enb Address What
2 breakpoint keep y 0x400db6f6 in blink_task at /home/user-name/esp/blink/main/./blink.c:33
breakpoint already hit 1 time
3 breakpoint keep y 0x400db704 in blink_task at /home/user-name/esp/blink/main/./blink.c:36
breakpoint already hit 1 time
(gdb)
Please note that breakpoint numbers (listed under ``Num``) start with ``2``. This is because first breakpoint has been already established at function ``app_main()`` by running command ``thb app_main`` on debugger launch. As it was a temporary breakpoint, it has been automatically deleted and now is not listed anymore.
To remove breakpoints enter ``delete N`` command (in short ``d N``), where ``N`` is the breakpoint number::
(gdb) delete 1
No breakpoint number 1.
(gdb) delete 2
(gdb)
Read more about breakpoints under :ref:`jtag-debugging-tip-breakpoints` and :ref:`jtag-debugging-tip-where-breakpoints`
.. _jtag-debugging-examples-command-line-03:
Halting and resuming the application
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When debugging, you may resume application and enter code waiting for some event or staying in infinite loop without any break points defined. In such case, to go back to debugging mode, you can break program execution manually by entering Ctrl+C.
To check it delete all breakpoints and enter ``c`` to resume application. Then enter Ctrl+C. Application will be halted at some random point and LED will stop blinking. Debugger will print the following::
(gdb) c
Continuing.
^CTarget halted. PRO_CPU: PC=0x400D0C00 APP_CPU: PC=0x400D0C00 (active)
[New Thread 1073433352]
Program received signal SIGINT, Interrupt.
[Switching to Thread 1073413512]
0x400d0c00 in esp_vApplicationIdleHook () at /home/user-name/esp/esp-idf/components/esp32/./freertos_hooks.c:52
52 asm("waiti 0");
(gdb)
In particular case above, the application has been halted in line 52 of code in file ``freertos_hooks.c``. Now you can resume it again by enter ``c`` or do some debugging as discussed below.
.. note::
In MSYS2 shell Ctrl+C does not halt the target but exists debugger. To resolve this issue consider debugging with :ref:`jtag-debugging-examples-eclipse` or check a workaround under http://www.mingw.org/wiki/Workaround_for_GDB_Ctrl_C_Interrupt.
.. _jtag-debugging-examples-command-line-04:
Stepping through the code
^^^^^^^^^^^^^^^^^^^^^^^^^
It is also possible to step through the code using ``step`` and ``next`` commands (in short ``s`` and ``n``). The difference is that ``step`` is entering inside subroutines calls, while ``next`` steps over the call, treating it as a single source line.
To demonstrate this functionality, using command ``break`` and ``delete`` discussed in previous paragraph, make sure that you have only one breakpoint defined at line ``36`` of ``blink.c``::
(gdb) info break
Num Type Disp Enb Address What
3 breakpoint keep y 0x400db704 in blink_task at /home/user-name/esp/blink/main/./blink.c:36
breakpoint already hit 1 time
(gdb)
Resume program by entering ``c`` and let it halt::
(gdb) c
Continuing.
Target halted. PRO_CPU: PC=0x400DB754 (active) APP_CPU: PC=0x400D1128
Breakpoint 3, blink_task (pvParameter=0x0) at /home/user-name/esp/blink/main/./blink.c:36
36 gpio_set_level(BLINK_GPIO, 1);
(gdb)
Then enter ``n`` couple of times to see how debugger is stepping one program line at a time::
(gdb) n
Target halted. PRO_CPU: PC=0x400DB756 (active) APP_CPU: PC=0x400D1128
Target halted. PRO_CPU: PC=0x400DB758 (active) APP_CPU: PC=0x400D1128
Target halted. PRO_CPU: PC=0x400DC04C (active) APP_CPU: PC=0x400D1128
Target halted. PRO_CPU: PC=0x400DB75B (active) APP_CPU: PC=0x400D1128
37 vTaskDelay(1000 / portTICK_PERIOD_MS);
(gdb) n
Target halted. PRO_CPU: PC=0x400DB75E (active) APP_CPU: PC=0x400D1128
Target halted. PRO_CPU: PC=0x400846FC (active) APP_CPU: PC=0x400D1128
Target halted. PRO_CPU: PC=0x400DB761 (active) APP_CPU: PC=0x400D1128
Target halted. PRO_CPU: PC=0x400DB746 (active) APP_CPU: PC=0x400D1128
33 gpio_set_level(BLINK_GPIO, 0);
(gdb)
If you enter ``s`` instead, then debugger will step inside subroutine calls::
(gdb) s
Target halted. PRO_CPU: PC=0x400DB748 (active) APP_CPU: PC=0x400D1128
Target halted. PRO_CPU: PC=0x400DB74B (active) APP_CPU: PC=0x400D1128
Target halted. PRO_CPU: PC=0x400DC04C (active) APP_CPU: PC=0x400D1128
Target halted. PRO_CPU: PC=0x400DC04F (active) APP_CPU: PC=0x400D1128
gpio_set_level (gpio_num=GPIO_NUM_4, level=0) at /home/user-name/esp/esp-idf/components/driver/./gpio.c:183
183 GPIO_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "GPIO output gpio_num error", ESP_ERR_INVALID_ARG);
(gdb)
In this particular case debugger stepped inside ``gpio_set_level(BLINK_GPIO, 0)`` and effectively moved to ``gpio.c`` driver code.
See :ref:`jtag-debugging-tip-why-next-works-as-step` for potential limitation of using ``next`` command.
.. _jtag-debugging-examples-command-line-05:
Checking and setting memory
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Displaying the contents of memory is done with command ``x``. With additional parameters you may vary the format and count of memory locations displayed. Run ``help x`` to see more details. Companion command to ``x`` is ``set`` that let you write values to the memory.
We will demonstrate how ``x`` and ``set`` work by reading from and writing to the memory location ``0x3FF44004`` labeled as ``GPIO_OUT_REG`` used to set and clear individual GPIO's. For more information please refer to `ESP32 Technical Reference Manual <https://espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf>`__, chapter IO_MUX and GPIO Matrix.
Being in the same ``blink.c`` project as before, set two breakpoints right after ``gpio_set_level`` instruction. Enter two times ``c`` to get to the break point followed by ``x /1wx 0x3FF44004`` to display contents of ``GPIO_OUT_REG`` memory location::
(gdb) c
Continuing.
Target halted. PRO_CPU: PC=0x400DB75E (active) APP_CPU: PC=0x400D1128
Target halted. PRO_CPU: PC=0x400DB74E (active) APP_CPU: PC=0x400D1128
Breakpoint 2, blink_task (pvParameter=0x0) at /home/user-name/esp/blink/main/./blink.c:34
34 vTaskDelay(1000 / portTICK_PERIOD_MS);
(gdb) x /1wx 0x3FF44004
0x3ff44004: 0x00000000
(gdb) c
Continuing.
Target halted. PRO_CPU: PC=0x400DB751 (active) APP_CPU: PC=0x400D1128
Target halted. PRO_CPU: PC=0x400DB75B (active) APP_CPU: PC=0x400D1128
Breakpoint 3, blink_task (pvParameter=0x0) at /home/user-name/esp/blink/main/./blink.c:37
37 vTaskDelay(1000 / portTICK_PERIOD_MS);
(gdb) x /1wx 0x3FF44004
0x3ff44004: 0x00000010
(gdb)
If your are blinking LED connected to GPIO4, then you should see fourth bit being flipped each time the LED changes the state::
0x3ff44004: 0x00000000
...
0x3ff44004: 0x00000010
Now, when the LED is off, that corresponds to ``0x3ff44004: 0x00000000`` being displayed, try using ``set`` command to set this bit by writting ``0x00000010`` to the same memory location::
(gdb) x /1wx 0x3FF44004
0x3ff44004: 0x00000000
(gdb) set {unsigned int}0x3FF44004=0x000010
You should see the LED to turn on immediately after entering ``set {unsigned int}0x3FF44004=0x000010`` command.
.. _jtag-debugging-examples-command-line-06:
Watching and setting program variables
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A common debugging tasks is checking the value of a program variable as the program runs. To be able to demonstrate this functionality, update file ``blink.c`` by adding a declaration of a global variable ``int i`` above definition of function ``blink_task``. Then add ``i++`` inside ``loop(1)`` of this function to get ``i`` incremented on each blink.
Exit debugger, so it is not confused with new code, build and flash the code to the ESP and restart debugger. There is no need to restart OpenOCD.
Once application is halted, enter the command ``watch i``::
(gdb) watch i
Hardware watchpoint 2: i
(gdb)
This will insert so called "watchpoint" in each place of code where variable ``i`` is being modified. Now enter ``continue`` to resume the application and observe it being halted::
(gdb) c
Continuing.
Target halted. PRO_CPU: PC=0x400DB751 (active) APP_CPU: PC=0x400D0811
[New Thread 1073432196]
Program received signal SIGTRAP, Trace/breakpoint trap.
[Switching to Thread 1073432196]
0x400db751 in blink_task (pvParameter=0x0) at /home/user-name/esp/blink/main/./blink.c:33
33 i++;
(gdb)
Resume application couple more times so ``i`` gets incremented. Now you can enter ``print i`` (in short ``p i``) to check the current value of ``i``::
(gdb) p i
$1 = 3
(gdb)
To modify the value of ``i`` use ``set`` command as below (you can then print it out to check if it has been indeed changed)::
(gdb) set var i = 0
(gdb) p i
$3 = 0
(gdb)
You may have up to two watchpoints, see :ref:`jtag-debugging-tip-breakpoints`.
.. _jtag-debugging-examples-command-line-07:
Setting conditional breakpoints
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Here comes more interesting part. You may set a breakpoint to halt the program execution, if certain condition is satisfied. Delete existing breakpoints and try this::
(gdb) break blink.c:34 if (i == 2)
Breakpoint 3 at 0x400db753: file /home/user-name/esp/blink/main/./blink.c, line 34.
(gdb)
Above command sets conditional breakpoint to halt program execution in line ``34`` of ``blink.c`` if ``i == 2``.
If current value of ``i`` is less than ``2`` and program is resumed, it will blink LED in a loop until condition ``i == 2`` gets true and then finally halt::
(gdb) set var i = 0
(gdb) c
Continuing.
Target halted. PRO_CPU: PC=0x400DB755 (active) APP_CPU: PC=0x400D112C
Target halted. PRO_CPU: PC=0x400DB753 (active) APP_CPU: PC=0x400D112C
Target halted. PRO_CPU: PC=0x400DB755 (active) APP_CPU: PC=0x400D112C
Target halted. PRO_CPU: PC=0x400DB753 (active) APP_CPU: PC=0x400D112C
Breakpoint 3, blink_task (pvParameter=0x0) at /home/user-name/esp/blink/main/./blink.c:34
34 gpio_set_level(BLINK_GPIO, 0);
(gdb)
Obtaining help on commands
^^^^^^^^^^^^^^^^^^^^^^^^^^
Commands presented so for should provide are very basis and intended to let you quickly get started with JTAG debugging. Check help what are the other commands at you disposal. To obtain help on syntax and functionality of particular command, being at ``(gdb)`` prompt type ``help`` and command name::
(gdb) help next
Step program, proceeding through subroutine calls.
Usage: next [N]
Unlike "step", if the current source line calls a subroutine,
this command does not enter the subroutine, but instead steps over
the call, in effect treating it as a single source line.
(gdb)
By typing just ``help``, you will get top level list of command classes, to aid you drilling down to more details. Optionally refer to available GDB cheat sheets, for instance http://darkdust.net/files/GDB%20Cheat%20Sheet.pdf. Good to have as a reference (even if not all commands are applicable in an embedded environment).
Ending debugger session
^^^^^^^^^^^^^^^^^^^^^^^
To quit debugger enter ``q``::
(gdb) q
A debugging session is active.
Inferior 1 [Remote target] will be detached.
Quit anyway? (y or n) y
Detaching from program: /home/user-name/esp/blink/build/blink.elf, Remote target
Ending remote debugging.
user-name@computer-name:~/esp/blink$
+305
View File
@@ -0,0 +1,305 @@
JTAG Debugging
==============
This document provides a guide to installing OpenOCD for ESP32 and debugging using
GDB. The document is structured as follows:
:ref:`jtag-debugging-introduction`
Introduction to the purpose of this guide.
:ref:`jtag-debugging-how-it-works`
Description how ESP32, JTAG interface, OpenOCD and GDB are interconnected and working together to enable debugging of ESP32.
:ref:`jtag-debugging-selecting-jtag-adapter`
What are the criteria and options to select JTAG adapter hardware.
:ref:`jtag-debugging-setup-openocd`
Procedure to install OpenOCD using prebuilt software packages for :doc:`Windows <setup-openocd-windows>`, :doc:`Linux <setup-openocd-linux>` and :doc:`MacOS <setup-openocd-macos>` operating systems.
:ref:`jtag-debugging-configuring-esp32-target`
Configuration of OpenOCD software and set up JTAG adapter hardware that will make together a debugging target.
:ref:`jtag-debugging-launching-debugger`
Steps to start up a debug session with GDB from :ref:`jtag-debugging-using-debugger-eclipse` and from :ref:`jtag-debugging-using-debugger-command-line`.
:ref:`jtag-debugging-examples`
If you are not familiar with GDB, check this section for debugging examples provided from from :ref:`jtag-debugging-examples-eclipse` as well as from :ref:`jtag-debugging-examples-command-line`.
:ref:`jtag-debugging-building-openocd`
Procedure to build OpenOCD from sources for :doc:`Windows <building-openocd-windows>`, :doc:`Linux <building-openocd-linux>` and :doc:`MacOS <building-openocd-macos>` operating systems.
:ref:`jtag-debugging-tips-and-quirks`
This section provides collection of tips and quirks related JTAG debugging of ESP32 with OpenOCD and GDB.
.. _jtag-debugging-introduction:
Introduction
------------
The ESP32 has two powerful Xtensa cores, allowing for a great deal of variety of program architectures. The FreeRTOS OS that comes with ESP-IDF is capable of multi-core preemptive multithreading, allowing for an intuitive way of writing software.
The downside of the ease of programming is that debugging without the right tools is harder: figuring out a bug that is caused by two threads, running even simultaneously on two different CPU cores, can take a long time when all you have are printf statements. A better and in many cases quicker way to debug such problems is by using a debugger, connected to the processors over a debug port.
Espressif has ported OpenOCD to support the ESP32 processor and the multicore FreeRTOS, which will be the foundation of most ESP32 apps, and has written some tools to help with features OpenOCD does not support natively.
This document provides a guide to installing OpenOCD for ESP32 and debugging using GDB under Linux, Windows and MacOS. Except for OS specific installation procedures, the s/w user interface and use procedures are the same across all supported operating systems.
.. note::
Screenshots presented in this document have been made for Eclipse Neon 3 running on Ubuntu 16.04 LTE. There may be some small differences in what a particular user interface looks like, depending on whether you are using Windows, MacOS or Linux and / or a different release of Eclipse.
.. _jtag-debugging-how-it-works:
How it Works?
-------------
The key software and hardware to perform debugging of ESP32 with OpenOCD over JTAG (Joint Test Action Group) interface is presented below and includes **xtensa-esp32-elf-gdb debugger**, **OpenOCD on chip debugger** and **JTAG adapter** connected to **ESP32** target.
.. figure:: ../../_static/jtag-debugging-overview.jpg
:align: center
:alt: JTAG debugging - overview diagram
:figclass: align-center
JTAG debugging - overview diagram
Under "Application Loading and Monitoring" there is another software and hardware to compile, build and flash application to ESP32, as well as to provide means to monitor diagnostic messages from ESP32.
Debugging using JTAG and application loading / monitoring is integrated under the `Eclipse <https://www.eclipse.org/>`_ environment, to provide quick and easy transition from writing, compiling and loading the code to debugging, back to writing the code, and so on. All the software is available for Windows, Linux and MacOS platforms.
If the :doc:`ESP32 WROVER KIT <../../hw-reference/modules-and-boards>` is used, then connection from PC to ESP32 is done effectively with a single USB cable thanks to FT2232H chip installed on WROVER, which provides two USB channels, one for JTAG and the second for UART connection.
Depending on user preferences, both `debugger` and `make` can be operated directly from terminal / command line, instead from Eclipse.
.. _jtag-debugging-selecting-jtag-adapter:
Selecting JTAG Adapter
----------------------
The quickest and most convenient way to start with JTAG debugging is by using :doc:`ESP32 WROVER KIT <../../hw-reference/modules-and-boards>`. Each version of this development board has JTAG interface already build in. No need for an external JTAG adapter and extra wiring / cable to connect JTAG to ESP32. WROVER KIT is using FT2232H JTAG interface operating at 20 MHz clock speed, which is difficult to achieve with an external adapter.
If you decide to use separate JTAG adapter, look for one that is compatible with both the voltage levels on the ESP32 as well as with the OpenOCD software. The JTAG port on the ESP32 is an industry-standard JTAG port which lacks (and does not need) the TRST pin. The JTAG I/O pins all are powered from the VDD_3P3_RTC pin (which normally would be powered by a 3.3V rail) so the JTAG adapter needs to be able to work with JTAG pins in that voltage range.
On the software side, OpenOCD supports a fair amount of JTAG adapters. See http://openocd.org/doc/html/Debug-Adapter-Hardware.html for an (unfortunately slightly incomplete) list of the adapters OpenOCD works with. This page lists SWD-compatible adapters as well; take note that the ESP32 does not support SWD. JTAG adapters that are hardcoded to a specific product line, e.g. STM32 debugging adapters, will not work.
The minimal signalling to get a working JTAG connection are TDI, TDO, TCK, TMS and GND. Some JTAG debuggers also need a connection from the ESP32 power line to a line called e.g. Vtar to set the working voltage. SRST can optionally be connected to the CH_PD of the ESP32, although for now, support in OpenOCD for that line is pretty minimal.
.. _jtag-debugging-setup-openocd:
Setup of OpenOCD
----------------
This step covers installation of OpenOCD binaries. If you like to build OpenOCS from sources then refer to section :ref:`jtag-debugging-building-openocd`. All OpenOCD files will be placed in ``~/esp/openocd-esp32`` directory. You may choose any other directory, but need to adjust respective paths used in examples.
.. toctree::
:hidden:
Windows <setup-openocd-windows>
Linux <setup-openocd-linux>
MacOS <setup-openocd-macos>
Pick up your OS below and follow provided instructions to setup OpenOCD.
+-------------------+-------------------+-------------------+
| |windows-logo| | |linux-logo| | |macos-logo| |
+-------------------+-------------------+-------------------+
| `Windows`_ | `Linux`_ | `Mac OS`_ |
+-------------------+-------------------+-------------------+
.. |windows-logo| image:: ../../_static/windows-logo.png
:target: ../jtag-debugging/setup-openocd-windows.html
.. |linux-logo| image:: ../../_static/linux-logo.png
:target: ../jtag-debugging/setup-openocd-linux.html
.. |macos-logo| image:: ../../_static/macos-logo.png
:target: ../jtag-debugging/setup-openocd-macos.html
.. _Windows: ../jtag-debugging/setup-openocd-windows.html
.. _Linux: ../jtag-debugging/setup-openocd-linux.html
.. _Mac OS: ../jtag-debugging/setup-openocd-macos.html
After installation is complete, get familiar with two key directories inside ``openocd-esp32`` installation folder:
* ``bin`` containing OpenOCD executable
* ``share\openocd\scripts`` containing configuration files invoked together with OpenOCD as command line parameters
.. note::
Directory names and structure above are specific to binary distribution of OpenOCD. They are used in examples of invoking OpenOCD throughout this guide. Directories for OpenOCD build from sources are different, so the way to invoke OpenOCD. For details see :ref:`jtag-debugging-building-openocd`.
.. _jtag-debugging-configuring-esp32-target:
Configuring ESP32 Target
------------------------
Once OpenOCD is installed, move to configuring ESP32 target (i.e ESP32 board with JTAG interface). You will do it in the following three steps:
* Configure and connect JTAG interface
* Run OpenOCD
* Upload application for debugging
Configure and connect JTAG interface
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This step depends on JTAG and ESP32 board you are using - see the two cases described below.
.. toctree::
:maxdepth: 1
configure-wrover
configure-other-jtag
.. _jtag-debugging-run-openocd:
Run OpenOCD
^^^^^^^^^^^
Once target is configured and connected to computer, you are ready to launch OpenOCD.
.. highlight:: bash
Open terminal, go to directory where OpenOCD is installed and start it up::
cd ~/esp/openocd-esp32
bin/openocd -s share/openocd/scripts -f interface/ftdi/esp32_devkitj_v1.cfg -f board/esp-wroom-32.cfg
.. note::
The files provided after ``-f`` above, are specific for ESP-WROVER-KIT with ESP-WROOM-32 module. You may need to provide different files depending on used hardware, For guidance see :ref:`jtag-debugging-tip-openocd-configure-target`.
.. highlight:: none
You should now see similar output (this log is for ESP32 WROVER KIT)::
user-name@computer-name:~/esp/openocd-esp32$ bin/openocd -s share/openocd/scripts -f interface/ftdi/esp32_devkitj_v1.cfg -f board/esp-wroom-32.cfg
Open On-Chip Debugger 0.10.0-dev-ged7b1a9 (2017-07-10-07:16)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
none separate
adapter speed: 20000 kHz
force hard breakpoints
Info : ftdi: if you experience problems at higher adapter clocks, try the command "ftdi_tdo_sample_edge falling"
Info : clock speed 20000 kHz
Info : JTAG tap: esp32.cpu0 tap/device found: 0x120034e5 (mfg: 0x272 (Tensilica), part: 0x2003, ver: 0x1)
Info : JTAG tap: esp32.cpu1 tap/device found: 0x120034e5 (mfg: 0x272 (Tensilica), part: 0x2003, ver: 0x1)
Info : esp32: Debug controller was reset (pwrstat=0x5F, after clear 0x0F).
Info : esp32: Core was reset (pwrstat=0x5F, after clear 0x0F).
* If there is an error indicating permission problems, please see the "Permissions delegation" bit in the OpenOCD README file in ``~/esp/openocd-esp32`` directory.
* In case there is an error finding configuration files, e.g. ``Can't find interface/ftdi/esp32_devkitj_v1.cfg``, check the path after ``-s``. This path is used by OpenOCD to look for the files specified after ``-f``. Also check if the file is indeed under provided path.
* If you see JTAG errors (...all ones/...all zeroes) please check your connections, whether no other signals are connected to JTAG besides ESP32's pins, and see if everything is powered on.
.. _jtag-upload-app-debug:
Upload application for debugging
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Build and upload your application to ESP32 as usual, see :ref:`get-started-build-flash`.
Another option is to write application image to flash using OpenOCD via JTAG with commands like this::
cd ~/esp/openocd-esp32
bin/openocd -s share/openocd/scripts -f interface/ftdi/esp32_devkitj_v1.cfg -f board/esp-wroom-32.cfg -c "program_esp32 filename.bin 0x10000 verify exit"
OpenOCD flashing command ``program_esp32`` has the following format:
``program_esp32 <image_file> <offset> [verify] [reset] [exit]``
- ``image_file`` - Path to program image file.
- ``offset`` - Offset in flash bank to write image.
- ``verify`` - Optional. Verify flash contents after writing.
- ``reset`` - Optional. Reset target after programing.
- ``exit`` - Optional. Finally exit OpenOCD.
You are now ready to start application debugging. Follow steps described in section below.
.. _jtag-debugging-launching-debugger:
Launching Debugger
------------------
The toolchain for ESP32 features GNU Debugger, in short GDB. It is available with other toolchain programs under filename ``xtensa-esp32-elf-gdb``. GDB can be called and operated directly from command line in a terminal. Another option is to call it from within IDE (like Eclipse, Visual Studio Code, etc.) and operate indirectly with help of GUI instead of typing commands in a terminal.
Both options of using debugger are discussed under links below.
* :ref:`jtag-debugging-using-debugger-eclipse`
* :ref:`jtag-debugging-using-debugger-command-line`
It is recommended to first check if debugger works from :ref:`jtag-debugging-using-debugger-command-line` and then move to using :ref:`jtag-debugging-using-debugger-eclipse`.
.. _jtag-debugging-examples:
Debugging Examples
------------------
This section is intended for users not familiar with GDB. It presents example debugging session from :ref:`jtag-debugging-examples-eclipse` using simple application available under :example:`get-started/blink` and covers the following debugging actions:
1. :ref:`jtag-debugging-examples-eclipse-01`
2. :ref:`jtag-debugging-examples-eclipse-02`
3. :ref:`jtag-debugging-examples-eclipse-03`
4. :ref:`jtag-debugging-examples-eclipse-04`
5. :ref:`jtag-debugging-examples-eclipse-05`
6. :ref:`jtag-debugging-examples-eclipse-06`
7. :ref:`jtag-debugging-examples-eclipse-07`
Similar debugging actions are provided using GDB from :ref:`jtag-debugging-examples-command-line`.
Before proceeding to examples, set up your ESP32 target and load it with :example:`get-started/blink`.
.. _jtag-debugging-building-openocd:
Building OpenOCD from Sources
-----------------------------
Please refer to separate documents listed below, that describe build process.
.. toctree::
:maxdepth: 1
Windows <building-openocd-windows>
Linux <building-openocd-linux>
MacOS <building-openocd-macos>
.. note::
Examples of invoking OpenOCD in this document assume using pre-built binary distribution described in section :ref:`jtag-debugging-setup-openocd`. To use binaries build locally from sources, change the path to OpenOCD executable to ``src/openocd`` and the path to configuration files to ``-s tcl``.
Example of invoking OpenOCD build locally from sources::
src/openocd -s tcl -f interface/ftdi/esp32_devkitj_v1.cfg -f board/esp-wroom-32.cfg
.. _jtag-debugging-tips-and-quirks:
Tips and Quirks
---------------
This section provides collection of links to all tips and quirks referred to from various parts of this guide.
* :ref:`jtag-debugging-tip-breakpoints`
* :ref:`jtag-debugging-tip-where-breakpoints`
* :ref:`jtag-debugging-tip-why-next-works-as-step`
* :ref:`jtag-debugging-tip-code-options`
* :ref:`jtag-debugging-tip-freertos-support`
* :ref:`jtag-debugging-tip-code-flash-voltage`
* :ref:`jtag-debugging-tip-optimize-jtag-speed`
* :ref:`jtag-debugging-tip-debugger-startup-commands`
* :ref:`jtag-debugging-tip-openocd-configure-target`
* :ref:`jtag-debugging-tip-reset-by-debugger`
* :ref:`jtag-debugging-tip-jtag-pins-reconfigured`
* :ref:`jtag-debugging-tip-reporting-issues`
Related Documents
-----------------
.. toctree::
:maxdepth: 1
using-debugger
debugging-examples
tips-and-quirks
../app_trace
@@ -0,0 +1,32 @@
***********************
Setup OpenOCD for Linux
***********************
Setup OpenOCD
=============
OpenOCD for 64-bit Linux is available for download from Espressif website:
https://dl.espressif.com/dl/openocd-esp32-linux64-a859564.tar.gz
Download this file, then extract it in ``~/esp/`` directory::
cd ~/esp
tar -xzf ~/Downloads/openocd-esp32-linux64-a859564.tar.gz
Next Steps
==========
To carry on with debugging environment setup, proceed to section :ref:`jtag-debugging-configuring-esp32-target`.
Related Documents
=================
.. toctree::
:maxdepth: 1
building-openocd-linux
@@ -0,0 +1,32 @@
***********************
Setup OpenOCD for MacOS
***********************
Setup OpenOCD
=============
OpenOCD for MacOS is available for download from Espressif website:
https://dl.espressif.com/dl/openocd-esp32-macos-a859564.tar.gz
Download this file, then extract it in ``~/esp`` directory::
cd ~/esp
tar -xzf ~/Downloads/openocd-esp32-macos-a859564.tar.gz
Next Steps
==========
To carry on with debugging environment setup, proceed to section :ref:`jtag-debugging-configuring-esp32-target`.
Related Documents
=================
.. toctree::
:maxdepth: 1
building-openocd-macos
@@ -0,0 +1,28 @@
*************************
Setup OpenOCD for Windows
*************************
Setup OpenOCD
=============
OpenOCD for Windows / MSYS2 is available for download from Espressif website:
https://dl.espressif.com/dl/openocd-esp32-win32-a859564.zip
Download this file and extract ``openocd-esp32`` folder inside to ``~/esp/`` directory.
Next Steps
==========
To carry on with debugging environment setup, proceed to section :ref:`jtag-debugging-configuring-esp32-target`.
Related Documents
=================
.. toctree::
:maxdepth: 1
building-openocd-windows
@@ -0,0 +1,262 @@
Tips and Quirks
---------------
This section provides collection of all tips and quirks referred to from various parts of this guide.
.. _jtag-debugging-tip-breakpoints:
Breakpoints and watchpoints available
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The ESP32 supports 2 hardware breakpoints. It also supports two watchpoints, so two variables can be watched for change or read by the GDB command ``watch myVariable``. Note that menuconfig option :ref:`CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK` uses the 2nd watchpoint and will not provide expected results, if you also try to use it within OpenOCD / GDB. See menuconfig's help for detailed description.
.. _jtag-debugging-tip-where-breakpoints:
What else should I know about breakpoints?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Normal GDB breakpoints (``b myFunction``) can only be set in IRAM, because that memory is writable. Setting these types of breakpoints in code in flash will not work. Instead, use a hardware breakpoint (``hb myFunction``).
.. _jtag-debugging-tip-why-next-works-as-step:
Why stepping with "next" does not bypass subroutine calls?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When stepping through the code with ``next`` command, GDB is internally setting a breakpoint (one out of two available) ahead in the code to bypass the subroutine calls. This functionality will not work, if the two available breakpoints are already set elsewhere in the code. If this is the case, delete breakpoints to have one "spare". With both breakpoints already used, stepping through the code with ``next`` command will work as like with ``step`` command and debugger will step inside subroutine calls.
.. _jtag-debugging-tip-code-options:
Support options for OpenOCD at compile time
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ESP-IDF has some support options for OpenOCD debugging which can be set at compile time:
* :ref:`CONFIG_ESP32_DEBUG_OCDAWARE` is enabled by default. If a panic or unhandled exception is thrown and a JTAG debugger is connected (ie openocd is running), ESP-IDF will break into the debugger.
* :ref:`CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK` (disabled by default) sets watchpoint index 1 (the second of two) at the end of any task stack. This is the most accurate way to debug task stack overflows. Click the link for more details.
Please see the :ref:`make menuconfig <get-started-configure>` menu for more details on setting compile-time options.
.. _jtag-debugging-tip-freertos-support:
FreeRTOS support
^^^^^^^^^^^^^^^^
OpenOCD has explicit support for the ESP-IDF FreeRTOS. GDB can see FreeRTOS tasks as threads. Viewing them all can be done using the GDB ``i threads`` command, changing to a certain task is done with ``thread n``, with ``n`` being the number of the thread. FreeRTOS detection can be disabled in target's configuration. For more details see :ref:`jtag-debugging-tip-openocd-configure-target`.
.. _jtag-debugging-tip-code-flash-voltage:
Why to set SPI flash voltage in OpenOCD configuration?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The MTDI pin of ESP32, being among four pins used for JTAG communication, is also one of ESP32's bootstrapping pins. On power up ESP32 is sampling binary level on MTDI to set it's internal voltage regulator used to supply power to external SPI flash chip. If binary level on MDTI pin on power up is low, the voltage regulator is set to deliver 3.3V, if it is high, then the voltage is set to 1.8V. The MTDI pin should have a pull-up or may rely on internal weak pull down resistor (see ESP32 Datasheet for details), depending on the type of SPI chip used. Once JTAG is connected, it overrides the pull-up or pull-down resistor that is supposed to do the bootstrapping.
To handle this issue OpenOCD's board configuration file (e.g. ``boards\esp-wroom-32.cfg`` for ESP-WROOM-32 module) provides ``ESP32_FLASH_VOLTAGE`` parameter to set the idle state of the ``TDO`` line to a specified binary level, therefore reducing the chance of a bad bootup of application due to incorrect flash voltage.
Check specification of ESP32 module connected to JTAG, what is the power supply voltage of SPI flash chip. Then set ``ESP32_FLASH_VOLTAGE`` accordingly. Most WROOM modules use 3.3V flash, while WROVER modules use 1.8V flash.
.. _jtag-debugging-tip-optimize-jtag-speed:
Optimize JTAG speed
^^^^^^^^^^^^^^^^^^^
In order to achieve higher data rates and minimize number of dropped packets it is recommended to optimize setting of JTAG clock frequency, so it is at maximum and still provides stable operation of JTAG. To do so use the following tips.
1. The upper limit of JTAG clock frequency is 20 MHz if CPU runs at 80 MHz, or 26 MHz if CPU runs at 160 MHz or 240 MHz.
2. Depending on particular JTAG adapter and the length of connecting cables, you may need to reduce JTAG frequency below 20 / 26 MHz.
3. In particular reduce frequency, if you get DSR/DIR errors (and they do not relate to OpenOCD trying to read from a memory range without physical memory being present there).
4. ESP-WROVER-KIT operates stable at 20 / 26 MHz.
.. _jtag-debugging-tip-debugger-startup-commands:
What is the meaning of debugger's startup commands?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
On startup, debugger is issuing sequence of commands to reset the chip and halt it at specific line of code. This sequence (shown below) is user defined to pick up at most convenient / appropriate line and start debugging.
* ``mon reset halt`` — reset the chip and keep the CPUs halted
* ``thb app_main`` — insert a temporary hardware breakpoint at ``app_main``, put here another function name if required
* ``x $a1=0`` — this is the tricky part. As far as we can tell, there is no way for a ``mon`` command to tell GDB that the target state has changed. GDB will assume that whatever stack the target had before ``mon reset halt`` will still be valid. In fact, after reset the target state will change and executing ``x $a1=0`` is a way to force GDB to get new state from the target.
* ``c`` — resume the program. It will then stop at breakpoint inserted at ``app_main``.
.. _jtag-debugging-tip-openocd-configure-target:
Configuration of OpenOCD for specific target
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
OpenOCD needs to be told what JTAG adapter **interface** to use, as well as what type of **board** and processor the JTAG adapter is connected to. To do so, use existing configuration files located in OpenOCD's ``share/openocd/scripts/interface`` and ``share/openocd/scripts/board`` folders.
For example, if you connect to ESP-WROVER-KIT with ESP-WROOM-32 module installed (see section :doc:`ESP32 WROVER KIT <../../hw-reference/modules-and-boards>`), use the following configuration files:
* ``interface/ftdi/esp32_devkitj_v1.cfg``
* ``board/esp-wroom-32.cfg``
Optionally prepare configuration by yourself. To do so, you can check existing files and modify them to match you specific hardware. Below is the summary of available configuration parameters for **board** configuration.
.. highlight:: none
Adapter's clock speed
""""""""""""""""""""""
::
adapter_khz 20000
See :ref:`jtag-debugging-tip-optimize-jtag-speed` for guidance how to set this value.
Single core debugging
"""""""""""""""""""""
::
set ESP32_ONLYCPU 1
Comment out this line for dual core debugging.
Disable RTOS support
""""""""""""""""""""
::
set ESP32_RTOS none
Comment out this line to have RTOS support.
Power supply voltage of ESP32's SPI flash chip
""""""""""""""""""""""""""""""""""""""""""""""
::
set ESP32_FLASH_VOLTAGE 1.8
Comment out this line to set 3.3V, ref: :ref:`jtag-debugging-tip-code-flash-voltage`
Configuration file for ESP32 targets
""""""""""""""""""""""""""""""""""""
::
source [find target/esp32.cfg]
.. note::
Do not change ``source [find target/esp32.cfg]`` line unless you are familiar with OpenOCD internals.
Currently ``target/esp32.cfg`` remains the only configuration file for ESP32 targets (esp108 and esp32). The matrix of supported configurations is as follows:
+---------------+---------------+---------------+
| Dual/single | RTOS | Target used |
+===============+===============+===============+
| dual | FreeRTOS | esp32 |
+---------------+---------------+---------------+
| single | FreeRTOS | esp108 (*) |
+---------------+---------------+---------------+
| dual | none | esp108 |
+---------------+---------------+---------------+
| single | none | esp108 |
+---------------+---------------+---------------+
(*) — we plan to fix this and add support for single core debugging with esp32 target in a subsequent commits.
Look inside ``board/esp-wroom-32.cfg`` for additional information provided in comments besides each configuration parameter.
.. _jtag-debugging-tip-reset-by-debugger:
How debugger resets ESP32?
^^^^^^^^^^^^^^^^^^^^^^^^^^
The board can be reset by entering ``mon reset`` or ``mon reset halt`` into GDB.
.. _jtag-debugging-tip-jtag-pins-reconfigured:
Do not use JTAG pins for something else
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Operation of JTAG may be disturbed, if some other h/w is connected to JTAG pins besides ESP32 module and JTAG adapter. ESP32 JTAG us using the following pins:
+---+----------------+-------------+
| | ESP32 JTAG Pin | JTAG Signal |
+===+================+=============+
| 1 | MTDO / GPIO15 | TDO |
+---+----------------+-------------+
| 2 | MTDI / GPIO12 | TDI |
+---+----------------+-------------+
| 3 | MTCK / GPIO13 | TCK |
+---+----------------+-------------+
| 4 | MTMS / GPIO14 | TMS |
+---+----------------+-------------+
JTAG communication will likely fail, if configuration of JTAG pins is changed by user application. If OpenOCD initializes correctly (detects the two Tensilica cores), but loses sync and spews out a lot of DTR/DIR errors when the program is ran, it is likely that the application reconfigures the JTAG pins to something else, or the user forgot to connect Vtar to a JTAG adapter that needed it.
.. highlight:: none
Below is an excerpt from series of errors reported by GDB after the application stepped into the code that reconfigured MTDO / GPIO15 to be an input::
cpu0: xtensa_resume (line 431): DSR (FFFFFFFF) indicates target still busy!
cpu0: xtensa_resume (line 431): DSR (FFFFFFFF) indicates DIR instruction generated an exception!
cpu0: xtensa_resume (line 431): DSR (FFFFFFFF) indicates DIR instruction generated an overrun!
cpu1: xtensa_resume (line 431): DSR (FFFFFFFF) indicates target still busy!
cpu1: xtensa_resume (line 431): DSR (FFFFFFFF) indicates DIR instruction generated an exception!
cpu1: xtensa_resume (line 431): DSR (FFFFFFFF) indicates DIR instruction generated an overrun!
.. _jtag-debugging-tip-reporting-issues:
Reporting issues with OpenOCD / GDB
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In case you encounter a problem with OpenOCD or GDB programs itself and do not find a solution searching available resources on the web, open an issue in the OpenOCD issue tracker under https://github.com/espressif/openocd-esp32/issues.
1. In issue report provide details of your configuration:
a. JTAG adapter type.
b. Release of ESP-IDF used to compile and load application that is being debugged.
c. Details of OS used for debugging.
d. Is OS running natively on a PC or on a virtual machine?
2. Create a simple example that is representative to observed issue. Describe steps how to reproduce it. In such an example debugging should not be affected by non-deterministic behaviour introduced by the Wi-Fi stack, so problems will likely be easier to reproduce, if encountered once.
.. highlight:: bash
3. Prepare logs from debugging session by adding additional parameters to start up commands.
OpenOCD:
::
bin/openocd -l openocd_log.txt -d 3 -s share/openocd/scripts -f interface/ftdi/esp32_devkitj_v1.cfg -f board/esp-wroom-32.cfg
Logging to a file this way will prevent information displayed on the terminal. This may be a good thing taken amount of information provided, when increased debug level ``-d 3`` is set. If you still like to see the log on the screen, then use another command instead:
::
bin/openocd -d 3 -s share/openocd/scripts -f interface/ftdi/esp32_devkitj_v1.cfg -f board/esp-wroom-32.cfg 2>&1 | tee openocd.log
.. note::
See :ref:`jtag-debugging-building-openocd` for slightly different command format, when running OpenOCD built from sources.
Debugger:
::
xtensa-esp32-elf-gdb -ex "set remotelogfile gdb_log.txt" <all other options>
Optionally add command ``remotelogfile gdb_log.txt`` to the ``gdbinit`` file.
4. Attach both ``openocd_log.txt`` and ``gdb_log.txt`` files to your issue report.
@@ -0,0 +1,183 @@
Using Debugger
--------------
This section covers configuration and running debugger either from :ref:`jtag-debugging-using-debugger-eclipse`
or :ref:`jtag-debugging-using-debugger-command-line`. It is recommended to first check if debugger works from :ref:`jtag-debugging-using-debugger-command-line` and then move to using Eclipse.
.. _jtag-debugging-using-debugger-eclipse:
Eclipse
^^^^^^^
Debugging functionality is provided out of box in standard Eclipse installation. Another option is to use pluggins like "GDB Hardware Debugging" plugin. We have found this plugin quite convenient and decided to use throughout this guide.
To begin with, install "GDB Hardware Debugging" plugin by opening Eclipse and going to `Help` > `Install` New Software.
Once installation is complete, configure debugging session following steps below. Please note that some of configuration parameters are generic and some are project specific. This will be shown below by configuring debugging for "blink" example project. If not done already, add this project to Eclipse workspace following guidance in section :doc:`Build and Flash with Eclipse IDE <../../get-started/eclipse-setup>`. The source of :example:`get-started/blink` application is available in :idf:`examples` directory of ESP-IDF repository.
1. In Eclipse go to `Run` > `Debug Configuration`. A new window will open. In the window's left pane double click "GDB Hardware Debugging" (or select "GDB Hardware Debugging" and press the "New" button) to create a new configuration.
2. In a form that will show up on the right, enter the "Name:" of this configuration, e.g. "Blink checking".
3. On the "Main" tab below, under "Project:", press "Browse" button and select the "blink" project.
4. In next line "C/C++ Application:" press "Browse" button and select "blink.elf" file. If "blink.elf" is not there, then likely this project has not been build yet. See :doc:`Build and Flash with Eclipse IDE <../../get-started/eclipse-setup>` how to do it.
5. Finally, under "Build (if required) before launching" click "Disable auto build".
A sample window with settings entered in points 1 - 5 is shown below.
.. figure:: ../../_static/hw-debugging-main-tab.jpg
:align: center
:alt: Configuration of GDB Hardware Debugging - Main tab
:figclass: align-center
Configuration of GDB Hardware Debugging - Main tab
6. Click "Debugger" tab. In field "GDB Command" enter ``xtensa-esp32-elf-gdb`` to invoke debugger.
7. Change default configuration of "Remote host" by entering ``3333`` under the "Port number".
Configuration entered in points 6 and 7 is shown on the following picture.
.. figure:: ../../_static/hw-debugging-debugger-tab.jpg
:align: center
:alt: Configuration of GDB Hardware Debugging - Debugger tab
:figclass: align-center
Configuration of GDB Hardware Debugging - Debugger tab
8. The last tab to that requires changing of default configuration is "Startup". Under "Initialization Commands" uncheck "Reset and Delay (seconds)" and "Halt"". Then, in entry field below, type ``mon reset halt`` and ``x $a1=0`` (in two separate lines).
.. note::
If you want to update image in the flash automatically before starting new debug session add the following lines of commands at the beginning of "Initialization Commands" textbox::
mon reset halt
mon program_esp32 ${workspace_loc:blink/build/blink.bin} 0x10000 verify
For description of ``program_esp32`` command see :ref:`jtag-upload-app-debug`.
9. Under "Load Image and Symbols" uncheck "Load image" option.
10. Further down on the same tab, establish an initial breakpoint to halt CPUs after they are reset by debugger. The plugin will set this breakpoint at the beginning of the function entered under "Set break point at:". Checkout this option and enter ``app_main`` in provided field.
11. Checkout "Resume" option. This will make the program to resume after ``mon reset halt`` is invoked per point 8. The program will then stop at breakpoint inserted at ``app_main``.
Configuration described in points 8 - 11 is shown below.
.. figure:: ../../_static/hw-debugging-startup-tab.jpg
:align: center
:alt: Configuration of GDB Hardware Debugging - Startup tab
:figclass: align-center
Configuration of GDB Hardware Debugging - Startup tab
If the "Startup" sequence looks convoluted and respective "Initialization Commands" are not clear to you, check :ref:`jtag-debugging-tip-debugger-startup-commands` for additional explanation.
12. If you previously completed :ref:`jtag-debugging-configuring-esp32-target` steps described above, so the target is running and ready to talk to debugger, go right to debugging by pressing "Debug" button. Otherwise press "Apply" to save changes, go back to :ref:`jtag-debugging-configuring-esp32-target` and return here to start debugging.
Once all 1 - 12 configuration steps are satisfied, the new Eclipse perspective called "Debug" will open as shown on example picture below.
.. figure:: ../../_static/debug-perspective.jpg
:align: center
:alt: Debug Perspective in Eclipse
:figclass: align-center
Debug Perspective in Eclipse
If you are not quite sure how to use GDB, check :ref:`jtag-debugging-examples-eclipse` example debugging session in section :ref:`jtag-debugging-examples`.
.. _jtag-debugging-using-debugger-command-line:
Command Line
^^^^^^^^^^^^
1. To be able start debugging session, the target should be up and running. If not done already, complete steps described under :ref:`jtag-debugging-configuring-esp32-target`.
.. highlight:: bash
2. Open a new terminal session and go to directory that contains project for debugging, e.g.
::
cd ~/esp/blink
.. highlight:: none
3. When launching a debugger, you will need to provide couple of configuration parameters and commands. Instead of entering them one by one in command line, create a configuration file and name it ``gdbinit``:
::
target remote :3333
mon reset halt
thb app_main
x $a1=0
c
Save this file in current directory.
For more details what's inside ``gdbinit`` file, see :ref:`jtag-debugging-tip-debugger-startup-commands`
.. highlight:: bash
4. Now you are ready to launch GDB. Type the following in terminal:
::
xtensa-esp32-elf-gdb -x gdbinit build/blink.elf
.. highlight:: none
5. If previous steps have been done correctly, you will see a similar log concluded with ``(gdb)`` prompt:
::
user-name@computer-name:~/esp/blink$ xtensa-esp32-elf-gdb -x gdbinit build/blink.elf
GNU gdb (crosstool-NG crosstool-ng-1.22.0-61-gab8375a) 7.10
Copyright (C) 2015 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-build_pc-linux-gnu --target=xtensa-esp32-elf".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from build/blink.elf...done.
0x400d10d8 in esp_vApplicationIdleHook () at /home/user-name/esp/esp-idf/components/esp32/./freertos_hooks.c:52
52 asm("waiti 0");
JTAG tap: esp32.cpu0 tap/device found: 0x120034e5 (mfg: 0x272 (Tensilica), part: 0x2003, ver: 0x1)
JTAG tap: esp32.slave tap/device found: 0x120034e5 (mfg: 0x272 (Tensilica), part: 0x2003, ver: 0x1)
esp32: Debug controller was reset (pwrstat=0x5F, after clear 0x0F).
esp32: Core was reset (pwrstat=0x5F, after clear 0x0F).
Target halted. PRO_CPU: PC=0x5000004B (active) APP_CPU: PC=0x00000000
esp32: target state: halted
esp32: Core was reset (pwrstat=0x1F, after clear 0x0F).
Target halted. PRO_CPU: PC=0x40000400 (active) APP_CPU: PC=0x40000400
esp32: target state: halted
Hardware assisted breakpoint 1 at 0x400db717: file /home/user-name/esp/blink/main/./blink.c, line 43.
0x0: 0x00000000
Target halted. PRO_CPU: PC=0x400DB717 (active) APP_CPU: PC=0x400D10D8
[New Thread 1073428656]
[New Thread 1073413708]
[New Thread 1073431316]
[New Thread 1073410672]
[New Thread 1073408876]
[New Thread 1073432196]
[New Thread 1073411552]
[Switching to Thread 1073411996]
Temporary breakpoint 1, app_main () at /home/user-name/esp/blink/main/./blink.c:43
43 xTaskCreate(&blink_task, "blink_task", 512, NULL, 5, NULL);
(gdb)
Note the third line from bottom that shows debugger halting at breakpoint established in ``gdbinit`` file at function ``app_main()``. Since the processor is halted, the LED should not be blinking. If this is what you see as well, you are ready to start debugging.
If you are not quite sure how to use GDB, check :ref:`jtag-debugging-examples-command-line` example debugging session in section :ref:`jtag-debugging-examples`.
+168
View File
@@ -0,0 +1,168 @@
Partition Tables
================
Overview
--------
A single ESP32's flash can contain multiple apps, as well as many different kinds of data (calibration data, filesystems, parameter storage, etc). For this reason a partition table is flashed to offset 0x8000 in the flash.
Partition table length is 0xC00 bytes (maximum 95 partition table entries). An MD5 checksum is appended after the table data. If the partition table is signed due to `secure boot`, the signature is appended after the partition table.
Each entry in the partition table has a name (label), type (app, data, or something else), subtype and the offset in flash where the partition is loaded.
The simplest way to use the partition table is to `make menuconfig` and choose one of the simple predefined partition tables:
* "Single factory app, no OTA"
* "Factory app, two OTA definitions"
In both cases the factory app is flashed at offset 0x10000. If you `make partition_table` then it will print a summary of the partition table.
Built-in Partition Tables
-------------------------
Here is the summary printed for the "Single factory app, no OTA" configuration::
# Espressif ESP32 Partition Table
# Name, Type, SubType, Offset, Size
nvs, data, nvs, 0x9000, 0x6000
phy_init, data, phy, 0xf000, 0x1000
factory, app, factory, 0x10000, 1M
* At a 0x10000 (64KB) offset in the flash is the app labelled "factory". The bootloader will run this app by default.
* There are also two data regions defined in the partition table for storing NVS library partition and PHY init data.
Here is the summary printed for the "Factory app, two OTA definitions" configuration::
# Espressif ESP32 Partition Table
# Name, Type, SubType, Offset, Size
nvs, data, nvs, 0x9000, 0x4000
otadata, data, ota, 0xd000, 0x2000
phy_init, data, phy, 0xf000, 0x1000
factory, 0, 0, 0x10000, 1M
ota_0, 0, ota_0, , 1M
ota_1, 0, ota_1, , 1M
* There are now three app partition definitions.
* The type of all three are set as "app", but the subtype varies between the factory app at 0x10000 and the next two "OTA" apps.
* There is also a new "ota data" slot, which holds the data for OTA updates. The bootloader consults this data in order to know which app to execute. If "ota data" is empty, it will execute the factory app.
Creating Custom Tables
----------------------
If you choose "Custom partition table CSV" in menuconfig then you can also enter the name of a CSV file (in the project directory) to use for your partition table. The CSV file can describe any number of definitions for the table you need.
The CSV format is the same format as printed in the summaries shown above. However, not all fields are required in the CSV. For example, here is the "input" CSV for the OTA partition table::
# Name, Type, SubType, Offset, Size
nvs, data, nvs, 0x9000, 0x4000
otadata, data, ota, 0xd000, 0x2000
phy_init, data, phy, 0xf000, 0x1000
factory, app, factory, 0x10000, 1M
ota_0, app, ota_0, , 1M
ota_1, app, ota_1, , 1M
* Whitespace between fields is ignored, and so is any line starting with # (comments).
* Each non-comment line in the CSV file is a partition definition.
* Only the offset for the first partition is supplied. The gen_esp32part.py tool fills in each remaining offset to start after the preceding partition.
Name field
~~~~~~~~~~
Name field can be any meaningful name. It is not significant to the ESP32. Names longer than 16 characters will be truncated.
Type field
~~~~~~~~~~
Partition type field can be specified as app (0) or data (1). Or it can be a number 0-254 (or as hex 0x00-0xFE). Types 0x00-0x3F are reserved for esp-idf core functions.
If your application needs to store data, please add a custom partition type in the range 0x40-0xFE.
The bootloader ignores any partition types other than app (0) & data (1).
Subtype
~~~~~~~
The 8-bit subtype field is specific to a given partition type.
esp-idf currently only specifies the meaning of the subtype field for "app" and "data" partition types.
App Subtypes
~~~~~~~~~~~~
When type is "app", the subtype field can be specified as factory (0), ota_0 (0x10) ... ota_15 (0x1F) or test (0x20).
- factory (0) is the default app partition. The bootloader will execute the factory app unless there it sees a partition of type data/ota, in which case it reads this partition to determine which OTA image to boot.
- OTA never updates the factory partition.
- If you want to conserve flash usage in an OTA project, you can remove the factory partition and use ota_0 instead.
- ota_0 (0x10) ... ota_15 (0x1F) are the OTA app slots. Refer to the :doc:`OTA documentation <../api-reference/system/ota>` for more details, which then use the OTA data partition to configure which app slot the bootloader should boot. If using OTA, an application should have at least two OTA application slots (ota_0 & ota_1). Refer to the :doc:`OTA documentation <../api-reference/system/ota>` for more details.
- test (0x2) is a reserved subtype for factory test procedures. It is not currently supported by the esp-idf bootloader.
Data Subtypes
~~~~~~~~~~~~~
When type is "data", the subtype field can be specified as ota (0), phy (1), nvs (2).
- ota (0) is the :ref:`OTA data partition <ota_data_partition>` which stores information about the currently selected OTA application. This partition should be 0x2000 bytes in size. Refer to the :ref:`OTA documentation <ota_data_partition>` for more details.
- phy (1) is for storing PHY initialisation data. This allows PHY to be configured per-device, instead of in firmware.
- In the default configuration, the phy partition is not used and PHY initialisation data is compiled into the app itself. As such, this partition can be removed from the partition table to save space.
- To load PHY data from this partition, run ``make menuconfig`` and enable :ref:`CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION` option. You will also need to flash your devices with phy init data as the esp-idf build system does not do this automatically.
- nvs (2) is for the :doc:`Non-Volatile Storage (NVS) API <../api-reference/storage/nvs_flash>`.
- NVS is used to store per-device PHY calibration data (different to initialisation data).
- NVS is used to store WiFi data if the :doc:`esp_wifi_set_storage(WIFI_STORAGE_FLASH) <../api-reference/wifi/esp_wifi>` initialisation function is used.
- The NVS API can also be used for other application data.
- It is strongly recommended that you include an NVS partition of at least 0x3000 bytes in your project.
- If using NVS API to store a lot of data, increase the NVS partition size from the default 0x6000 bytes.
Other data subtypes are reserved for future esp-idf uses.
Offset & Size
~~~~~~~~~~~~~
Only the first offset field is required (we recommend using 0x10000). Partitions with blank offsets will start after the previous partition.
App partitions have to be at offsets aligned to 0x10000 (64K). If you leave the offset field blank, the tool will automatically align the partition. If you specify an unaligned offset for an app partition, the tool will return an error.
Sizes and offsets can be specified as decimal numbers, hex numbers with the prefix 0x, or size multipliers K or M (1024 and 1024*1024 bytes).
Generating Binary Partition Table
---------------------------------
The partition table which is flashed to the ESP32 is in a binary format, not CSV. The tool :component_file:`partition_table/gen_esp32part.py` is used to convert between CSV and binary formats.
If you configure the partition table CSV name in ``make menuconfig`` and then ``make partition_table``, this conversion is done as part of the build process.
To convert CSV to Binary manually::
python gen_esp32part.py --verify input_partitions.csv binary_partitions.bin
To convert binary format back to CSV::
python gen_esp32part.py --verify binary_partitions.bin input_partitions.csv
To display the contents of a binary partition table on stdout (this is how the summaries displayed when running `make partition_table` are generated::
python gen_esp32part.py binary_partitions.bin
``gen_esp32part.py`` takes one optional argument, ``--verify``, which will also verify the partition table during conversion (checking for overlapping partitions, unaligned partitions, etc.)
MD5 checksum
~~~~~~~~~~~~
The binary format of the partition table contains an MD5 checksum computed based on the partition table. This checksum is used for checking the integrity of the partition table during the boot.
The MD5 checksum generation can be disabled by the ``--disable-md5sum`` option of ``gen_esp32part.py`` or by the :ref:`CONFIG_PARTITION_TABLE_MD5` option. This is useful for example when one uses a legacy bootloader which cannot process MD5 checksums and the boot fails with the error message ``invalid magic number 0xebeb``.
Flashing the partition table
----------------------------
* ``make partition_table-flash``: will flash the partition table with esptool.py.
* ``make flash``: Will flash everything including the partition table.
A manual flashing command is also printed as part of ``make partition_table``.
Note that updating the partition table doesn't erase data that may have been stored according to the old partition table. You can use ``make erase_flash`` (or ``esptool.py erase_flash``) to erase the entire flash contents.
.. _secure boot: security/secure-boot.rst
+114
View File
@@ -0,0 +1,114 @@
*****************
ESP32 ROM console
*****************
When an ESP32 is unable to boot from flash ROM (and the fuse disabling it hasn't been blown), it boots into a rom console. The console
is based on TinyBasic, and statements entered should be in the form of BASIC statements. As is common in the BASIC language, without a
preceeding line number, commands entered are executed immediately; lines with a prefixed line number are stored as part of a program.
Full list of supported statements and functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
System
------
- BYE - *exits Basic, reboots ESP32, retries booting from flash*
- END - *stops execution from the program, also "STOP"*
- MEM - *displays memory usage statistics*
- NEW - *clears the current program*
- RUN - *executes the current program*
IO, Documentation
-----------------
- PEEK( address ) - *get a 32-bit value from a memory address*
- POKE - *write a 32-bit value to memory*
- USR(addr, arg1, ..) - *Execute a machine language function*
- PRINT expression - *print out the expression, also "?"*
- PHEX expression - *print expression as a hex number*
- REM stuff - *remark/comment, also "'"*
Expressions, Math
-----------------
- A=V, LET A=V - *assign value to a variable*
- +, -, \*, / - *Math*
- <,<=,=,<>,!=,>=,> - *Comparisons*
- ABS( expression ) - *returns the absolute value of the expression*
- RSEED( v ) - *sets the random seed to v*
- RND( m ) - *returns a random number from 0 to m*
- A=1234 - * Assign a decimal value*
- A=&h1A2 - * Assign a hex value*
- A=&b1001 - *Assign a binary value*
Control
-------
- IF expression statement - *perform statement if expression is true*
- FOR variable = start TO end - *start for block*
- FOR variable = start TO end STEP value - *start for block with step*
- NEXT - *end of for block*
- GOTO linenumber - *continue execution at this line number*
- GOSUB linenumber - *call a subroutine at this line number*
- RETURN - *return from a subroutine*
- DELAY - *Delay a given number of milliseconds*
Pin IO
------
- IODIR - *Set a GPIO-pin as an output (1) or input (0)*
- IOSET - *Set a GPIO-pin, configured as output, to high (1) or low (0)*
- IOGET - *Get the value of a GPIO-pin*
Example programs
~~~~~~~~~~~~~~~~
Here are a few example commands and programs to get you started...
Read UART_DATE register of uart0
--------------------------------
::
> PHEX PEEK(&h3FF40078)
15122500
Set GPIO2 using memory writes to GPIO_OUT_REG
---------------------------------------------
Note: you can do this easier with the IOSET command
::
> POKE &h3FF44004,PEEK(&h3FF44004) OR &b100
Get value of GPIO0
------------------
::
> IODIR 0,0
> PRINT IOGET(0)
0
Blink LED
---------
Hook up an LED between GPIO2 and ground. When running the program, the LED should blink 10 times.
::
10 IODIR 2,1
20 FOR A=1 TO 10
30 IOSET 2,1
40 DELAY 250
50 IOSET 2,0
60 DELAY 250
70 NEXT A
RUN
Credits
~~~~~~~
The ROM console is based on "TinyBasicPlus" by Mike Field and Scott Lawrence, which is based on "68000 TinyBasic" by Gordon Brandly
+165
View File
@@ -0,0 +1,165 @@
ULP coprocessor programming
===========================
.. toctree::
:maxdepth: 1
Instruction set reference <ulp_instruction_set>
Programming using macros (legacy) <ulp_macros>
ULP (Ultra Low Power) coprocessor is a simple FSM which is designed to perform measurements using ADC, temperature sensor, and external I2C sensors, while main processors are in deep sleep mode. ULP coprocessor can access RTC_SLOW_MEM memory region, and registers in RTC_CNTL, RTC_IO, and SARADC peripherals. ULP coprocessor uses fixed-width 32-bit instructions, 32-bit memory addressing, and has 4 general purpose 16-bit registers.
Installing the toolchain
------------------------
ULP coprocessor code is written in assembly and compiled using the `binutils-esp32ulp toolchain`_.
1. Download the toolchain using the links listed on this page:
https://github.com/espressif/binutils-esp32ulp/wiki#downloads
2. Extract the toolchain into a directory, and add the path to the ``bin/`` directory of the toolchain to the ``PATH`` environment variable.
Compiling ULP code
------------------
To compile ULP code as part of a component, the following steps must be taken:
1. ULP code, written in assembly, must be added to one or more files with `.S` extension. These files must be placed into a separate directory inside component directory, for instance `ulp/`.
.. note: This directory should not be added to the ``COMPONENT_SRCDIRS`` environment variable. The logic behind this is that the ESP-IDF build system will compile files found in ``COMPONENT_SRCDIRS`` based on their extensions. For ``.S`` files, ``xtensa-esp32-elf-as`` assembler is used. This is not desirable for ULP assembly files, so the easiest way to achieve the distinction is by placing ULP assembly files into a separate directory.
2. Modify the component makefile, adding the following::
ULP_APP_NAME ?= ulp_$(COMPONENT_NAME)
ULP_S_SOURCES = $(COMPONENT_PATH)/ulp/ulp_source_file.S
ULP_EXP_DEP_OBJECTS := main.o
include $(IDF_PATH)/components/ulp/component_ulp_common.mk
Here is each line explained:
ULP_APP_NAME
Name of the generated ULP application, without an extension. This name is used for build products of the ULP application: ELF file, map file, binary file, generated header file, and generated linker export file.
ULP_S_SOURCES
List of assembly files to be passed to the ULP assembler. These must be absolute paths, i.e. start with ``$(COMPONENT_PATH)``. Consider using ``$(addprefix)`` function if more than one file needs to be listed. Paths are relative to component build directory, so prefixing them is not necessary.
ULP_EXP_DEP_OBJECTS
List of object files names within the component which include the generated header file. This list is needed to build the dependencies correctly and ensure that the generated header file is created before any of these files are compiled. See section below explaining the concept of generated header files for ULP applications.
include $(IDF_PATH)/components/ulp/component_ulp_common.mk
Includes common definitions of ULP build steps. Defines build targets for ULP object files, ELF file, binary file, etc.
3. Build the application as usual (e.g. `make app`)
Inside, the build system will take the following steps to build ULP program:
1. **Run each assembly file (foo.S) through C preprocessor.** This step generates the preprocessed assembly files (foo.ulp.pS) in the component build directory. This step also generates dependency files (foo.ulp.d).
2. **Run preprocessed assembly sources through assembler.** This produces objects (foo.ulp.o) and listing (foo.ulp.lst) files. Listing files are generated for debugging purposes and are not used at later stages of build process.
3. **Run linker script template through C preprocessor.** The template is located in components/ulp/ld directory.
4. **Link object files into an output ELF file** (ulp_app_name.elf). Map file (ulp_app_name.map) generated at this stage may be useful for debugging purposes.
5. **Dump contents of the ELF file into binary** (ulp_app_name.bin) for embedding into the application.
6. **Generate list of global symbols** (ulp_app_name.sym) in the ELF file using esp32ulp-elf-nm.
7. **Create LD export script and header file** (ulp_app_name.ld and ulp_app_name.h) containing the symbols from ulp_app_name.sym. This is done using esp32ulp_mapgen.py utility.
8. **Add the generated binary to the list of binary files** to be emedded into the application.
Accessing ULP program variables
-------------------------------
Global symbols defined in the ULP program may be used inside the main program.
For example, ULP program may define a variable ``measurement_count`` which will define the number of ADC measurements the program needs to make before waking up the chip from deep sleep::
.global measurement_count
measurement_count: .long 0
/* later, use measurement_count */
move r3, measurement_count
ld r3, r3, 0
Main program needs to initialize this variable before ULP program is started. Build system makes this possible by generating a ``$(ULP_APP_NAME).h`` and ``$(ULP_APP_NAME).ld`` files which define global symbols present in the ULP program. This files include each global symbol defined in the ULP program, prefixed with ``ulp_``.
The header file contains declaration of the symbol::
extern uint32_t ulp_measurement_count;
Note that all symbols (variables, arrays, functions) are declared as ``uint32_t``. For functions and arrays, take address of the symbol and cast to the appropriate type.
The generated linker script file defines locations of symbols in RTC_SLOW_MEM::
PROVIDE ( ulp_measurement_count = 0x50000060 );
To access ULP program variables from the main program, include the generated header file and use variables as one normally would::
#include "ulp_app_name.h"
// later
void init_ulp_vars() {
ulp_measurement_count = 64;
}
Note that ULP program can only use lower 16 bits of each 32-bit word in RTC memory, because the registers are 16-bit, and there is no instruction to load from high part of the word.
Likewise, ULP store instruction writes register value into the lower 16 bit part of the 32-bit word. Upper 16 bits are written with a value which depends on the address of the store instruction, so when reading variables written by the ULP, main application needs to mask upper 16 bits, e.g.::
printf("Last measurement value: %d\n", ulp_last_measurement & UINT16_MAX);
Starting the ULP program
------------------------
To run a ULP program, main application needs to load the ULP program into RTC memory using ``ulp_load_binary`` function, and then start it using ``ulp_run`` function.
Note that "Enable Ultra Low Power (ULP) Coprocessor" option must be enabled in menuconfig in order to reserve memory for the ULP. "RTC slow memory reserved for coprocessor" option must be set to a value sufficient to store ULP code and data. If the application components contain multiple ULP programs, then the size of the RTC memory must be sufficient to hold the largest one.
Each ULP program is embedded into the ESP-IDF application as a binary blob. Application can reference this blob and load it in the following way (suppose ULP_APP_NAME was defined to ``ulp_app_name``::
extern const uint8_t bin_start[] asm("_binary_ulp_app_name_bin_start");
extern const uint8_t bin_end[] asm("_binary_ulp_app_name_bin_end");
void start_ulp_program() {
ESP_ERROR_CHECK( ulp_load_binary(
0 /* load address, set to 0 when using default linker scripts */,
bin_start,
(bin_end - bin_start) / sizeof(uint32_t)) );
}
.. doxygenfunction:: ulp_load_binary
Once the program is loaded into RTC memory, application can start it, passing the address of the entry point to ``ulp_run`` function::
ESP_ERROR_CHECK( ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t)) );
.. doxygenfunction:: ulp_run
Declaration of the entry point symbol comes from the above mentioned generated header file, ``$(ULP_APP_NAME).h``. In assembly source of the ULP application, this symbol must be marked as ``.global``::
.global entry
entry:
/* code starts here */
ULP program flow
----------------
ULP coprocessor is started by a timer. The timer is started once ``ulp_run`` is called. The timer counts a number of RTC_SLOW_CLK ticks (by default, produced by an internal 150kHz RC oscillator). The number of ticks is set using ``SENS_ULP_CP_SLEEP_CYCx_REG`` registers (x = 0..4). When starting the ULP for the first time, ``SENS_ULP_CP_SLEEP_CYC0_REG`` will be used to set the number of timer ticks. Later the ULP program can select another ``SENS_ULP_CP_SLEEP_CYCx_REG`` register using ``sleep`` instruction.
The application can set ULP timer period values (SENS_ULP_CP_SLEEP_CYCx_REG, x = 0..4) using ``ulp_wakeup_period_set`` function.
.. doxygenfunction:: ulp_set_wakeup_period
Once the timer counts the number of ticks set in the selected ``SENS_ULP_CP_SLEEP_CYCx_REG`` register, ULP coprocessor powers up and starts running the program from the entry point set in the call to ``ulp_run``.
The program runs until it encounters a ``halt`` instruction or an illegal instruction. Once the program halts, ULP coprocessor powers down, and the timer is started again.
To disable the timer (effectively preventing the ULP program from running again), clear the ``RTC_CNTL_ULP_CP_SLP_TIMER_EN`` bit in the ``RTC_CNTL_STATE0_REG`` register. This can be done both from ULP code and from the main program.
.. _binutils-esp32ulp toolchain: https://github.com/espressif/binutils-esp32ulp
+903
View File
@@ -0,0 +1,903 @@
ULP coprocessor instruction set
===============================
This document provides details about the instructions used by ESP32 ULP coprocessor assembler.
ULP coprocessor has 4 16-bit general purpose registers, labeled R0, R1, R2, R3. It also has an 8-bit counter register (stage_cnt) which can be used to implement loops. Stage count regiter is accessed using special instructions.
ULP coprocessor can access 8k bytes of RTC_SLOW_MEM memory region. Memory is addressed in 32-bit word units. It can also access peripheral registers in RTC_CNTL, RTC_IO, and SENS peripherals.
All instructions are 32-bit. Jump instructions, ALU instructions, peripheral register and memory access instructions are executed in 1 cycle. Instructions which work with peripherals (TSENS, ADC, I2C) take variable number of cycles, depending on peripheral operation.
The instruction syntax is case insensitive. Upper and lower case letters can be used and intermixed arbitrarily. This is true both for register names and instruction names.
Note about addressing
---------------------
ESP32 ULP coprocessor's JUMP, ST, LD instructions which take register as an argument (jump address, store/load base address) expect the argument to be expressed in 32-bit words.
Consider the following example program::
entry:
NOP
NOP
NOP
NOP
loop:
MOVE R1, loop
JUMP R1
When this program is assembled and linked, address of label ``loop`` will be equal to 16 (expressed in bytes). However `JUMP` instruction expects the address stored in register to be expressed in 32-bit words. To account for this common use case, assembler will convert the address of label `loop` from bytes to words, when generating ``MOVE`` instruction, so the code generated code will be equivalent to::
0000 NOP
0004 NOP
0008 NOP
000c NOP
0010 MOVE R1, 4
0014 JUMP R1
The other case is when the argument of ``MOVE`` instruction is not a label but a constant. In this case assembler will use the value as is, without any conversion::
.set val, 0x10
MOVE R1, val
In this case, value loaded into R1 will be ``0x10``.
Similar considerations apply to ``LD`` and ``ST`` instructions. Consider the following code::
.global array
array: .long 0
.long 0
.long 0
.long 0
MOVE R1, array
MOVE R2, 0x1234
ST R2, R1, 0 // write value of R2 into the first array element,
// i.e. array[0]
ST R2, R1, 4 // write value of R2 into the second array element
// (4 byte offset), i.e. array[1]
ADD R1, R1, 2 // this increments address by 2 words (8 bytes)
ST R2, R1, 0 // write value of R2 into the third array element,
// i.e. array[2]
Note about instruction execution time
-------------------------------------
ULP coprocessor is clocked from RTC_FAST_CLK, which is normally derived from the internal 8MHz oscillator. Applications which need to know exact ULP clock frequency can calibrate it against the main XTAL clock::
#include "soc/rtc.h"
// calibrate 8M/256 clock against XTAL, get 8M/256 clock period
uint32_t rtc_8md256_period = rtc_clk_cal(RTC_CAL_8MD256, 100);
uint32_t rtc_fast_freq_hz = 1000000ULL * (1 << RTC_CLK_CAL_FRACT) * 256 / rtc_8md256_period;
ULP coprocessor needs 2 clock cycle to fetch each instuction (fetching is not pipelined), plus certain number of cycles to execute, depending on the instruction. See description of each instruction for details on the execution time.
Note that when accessing RTC memories and RTC registers, ULP coprocessor has lower priority than the main CPUs. This means that ULP coprocessor execution may be suspended while the main CPUs access same memory region as the ULP.
**NOP** - no operation
----------------------
**Syntax**
**NOP**
**Operands**
None
**Cycles**
2 (fetch) + 1 (execute)
**Description**
No operation is performed. Only the PC is incremented.
**Example**::
1: NOP
**ADD** - Add to register
-------------------------
**Syntax**
**ADD** *Rdst, Rsrc1, Rsrc2*
**ADD** *Rdst, Rsrc1, imm*
**Operands**
- *Rdst* - Register R[0..3]
- *Rsrc1* - Register R[0..3]
- *Rsrc2* - Register R[0..3]
- *Imm* - 16-bit signed value
**Cycles**
2 (fetch) + 2 (execute)
**Description**
The instruction adds source register to another source register or to a 16-bit signed value and stores result to the destination register.
**Examples**::
1: ADD R1, R2, R3 //R1 = R2 + R3
2: Add R1, R2, 0x1234 //R1 = R2 + 0x1234
3: .set value1, 0x03 //constant value1=0x03
Add R1, R2, value1 //R1 = R2 + value1
4: .global label //declaration of variable label
Add R1, R2, label //R1 = R2 + label
...
label: nop //definition of variable label
**SUB** - Subtract from register
--------------------------------
**Syntax**
**SUB** *Rdst, Rsrc1, Rsrc2*
**SUB** *Rdst, Rsrc1, imm*
**Operands**
- *Rdst* - Register R[0..3]
- *Rsrc1* - Register R[0..3]
- *Rsrc2* - Register R[0..3]
- *Imm* - 16-bit signed value
**Cycles**
2 (fetch) + 2 (execute)
**Description**
The instruction subtracts the source register from another source register or subtracts 16-bit signed value from a source register, and stores result to the destination register.
**Examples**::
1: SUB R1, R2, R3 //R1 = R2 - R3
2: sub R1, R2, 0x1234 //R1 = R2 - 0x1234
3: .set value1, 0x03 //constant value1=0x03
SUB R1, R2, value1 //R1 = R2 - value1
4: .global label //declaration of variable label
SUB R1, R2, label //R1 = R2 - label
....
label: nop //definition of variable label
**AND** - Logical AND of two operands
-------------------------------------
**Syntax**
**AND** *Rdst, Rsrc1, Rsrc2*
**AND** *Rdst, Rsrc1, imm*
**Operands**
- *Rdst* - Register R[0..3]
- *Rsrc1* - Register R[0..3]
- *Rsrc2* - Register R[0..3]
- *Imm* - 16-bit signed value
**Cycles**
2 (fetch) + 2 (execute)
**Description**
The instruction does logical AND of a source register and another source register or 16-bit signed value and stores result to the destination register.
**Examples**::
1: AND R1, R2, R3 //R1 = R2 & R3
2: AND R1, R2, 0x1234 //R1 = R2 & 0x1234
3: .set value1, 0x03 //constant value1=0x03
AND R1, R2, value1 //R1 = R2 & value1
4: .global label //declaration of variable label
AND R1, R2, label //R1 = R2 & label
...
label: nop //definition of variable label
**OR** - Logical OR of two operands
-----------------------------------
**Syntax**
**OR** *Rdst, Rsrc1, Rsrc2*
**OR** *Rdst, Rsrc1, imm*
**Operands**
- *Rdst* - Register R[0..3]
- *Rsrc1* - Register R[0..3]
- *Rsrc2* - Register R[0..3]
- *Imm* - 16-bit signed value
**Cycles**
2 (fetch) + 2 (execute)
**Description**
The instruction does logical OR of a source register and another source register or 16-bit signed value and stores result to the destination register.
**Examples**::
1: OR R1, R2, R3 //R1 = R2 \| R3
2: OR R1, R2, 0x1234 //R1 = R2 \| 0x1234
3: .set value1, 0x03 //constant value1=0x03
OR R1, R2, value1 //R1 = R2 \| value1
4: .global label //declaration of variable label
OR R1, R2, label //R1 = R2 \|label
...
label: nop //definition of variable label
**LSH** - Logical Shift Left
----------------------------
**Syntax**
**LSH** *Rdst, Rsrc1, Rsrc2*
**LSH** *Rdst, Rsrc1, imm*
**Operands**
- *Rdst* - Register R[0..3]
- *Rsrc1* - Register R[0..3]
- *Rsrc2* - Register R[0..3]
- *Imm* - 16-bit signed value
**Cycles**
2 (fetch) + 2 (execute)
**Description**
The instruction does logical shift to left of source register to number of bits from another source register or 16-bit signed value and store result to the destination register.
**Examples**::
1: LSH R1, R2, R3 //R1 = R2 << R3
2: LSH R1, R2, 0x03 //R1 = R2 << 0x03
3: .set value1, 0x03 //constant value1=0x03
LSH R1, R2, value1 //R1 = R2 << value1
4: .global label //declaration of variable label
LSH R1, R2, label //R1 = R2 << label
...
label: nop //definition of variable label
**RSH** - Logical Shift Right
-----------------------------
**Syntax**
**RSH** *Rdst, Rsrc1, Rsrc2*
**RSH** *Rdst, Rsrc1, imm*
**Operands**
*Rdst* - Register R[0..3]
*Rsrc1* - Register R[0..3]
*Rsrc2* - Register R[0..3]
*Imm* - 16-bit signed value
**Cycles**
2 (fetch) + 2 (execute)
**Description**
The instruction does logical shift to right of source register to number of bits from another source register or 16-bit signed value and store result to the destination register.
**Examples**::
1: RSH R1, R2, R3 //R1 = R2 >> R3
2: RSH R1, R2, 0x03 //R1 = R2 >> 0x03
3: .set value1, 0x03 //constant value1=0x03
RSH R1, R2, value1 //R1 = R2 >> value1
4: .global label //declaration of variable label
RSH R1, R2, label //R1 = R2 >> label
label: nop //definition of variable label
**MOVE** Move to register
---------------------------
**Syntax**
**MOVE** *Rdst, Rsrc*
**MOVE** *Rdst, imm*
**Operands**
- *Rdst* Register R[0..3]
- *Rsrc* Register R[0..3]
- *Imm* 16-bit signed value
**Cycles**
2 (fetch) + 2 (execute)
**Description**
The instruction move to destination register value from source register or 16-bit signed value.
Note that when a label is used as an immediate, the address of the label will be converted from bytes to words. This is because LD, ST, and JUMP instructions expect the address register value to be expressed in words rather than bytes. To avoid using an extra instruction
**Examples**::
1: MOVE R1, R2 //R1 = R2 >> R3
2: MOVE R1, 0x03 //R1 = R2 >> 0x03
3: .set value1, 0x03 //constant value1=0x03
MOVE R1, value1 //R1 = value1
4: .global label //declaration of label
MOVE R1, label //R1 = address_of(label) / 4
...
label: nop //definition of label
**ST** Store data to the memory
---------------------------------
**Syntax**
**ST** *Rsrc, Rdst, offset*
**Operands**
- *Rsrc* Register R[0..3], holds the 16-bit value to store
- *Rdst* Register R[0..3], address of the destination, in 32-bit words
- *Offset* 10-bit signed value, offset in bytes
**Cycles**
2 (fetch) + 4 (execute)
**Description**
The instruction stores the 16-bit value of Rsrc to the lower half-word of memory with address Rdst+offset. The upper half-word is written with the current program counter (PC), expressed in words, shifted left by 5 bits::
Mem[Rdst + offset / 4]{31:0} = {PC[10:0], 5'b0, Rsrc[15:0]}
The application can use higher 16 bits to determine which instruction in the ULP program has written any particular word into memory.
**Examples**::
1: ST R1, R2, 0x12 //MEM[R2+0x12] = R1
2: .data //Data section definition
Addr1: .word 123 // Define label Addr1 16 bit
.set offs, 0x00 // Define constant offs
.text //Text section definition
MOVE R1, 1 // R1 = 1
MOVE R2, Addr1 // R2 = Addr1
ST R1, R2, offs // MEM[R2 + 0] = R1
// MEM[Addr1 + 0] will be 32'h600001
**LD** Load data from the memory
----------------------------------
**Syntax**
**LD** *Rdst, Rsrc, offset*
**Operands**
*Rdst* Register R[0..3], destination
*Rsrc* Register R[0..3], holds address of destination, in 32-bit words
*Offset* 10-bit signed value, offset in bytes
**Cycles**
2 (fetch) + 4 (execute)
**Description**
The instruction loads lower 16-bit half-word from memory with address Rsrc+offset into the destination register Rdst::
Rdst[15:0] = Mem[Rsrc + offset / 4][15:0]
**Examples**::
1: LD R1, R2, 0x12 //R1 = MEM[R2+0x12]
2: .data //Data section definition
Addr1: .word 123 // Define label Addr1 16 bit
.set offs, 0x00 // Define constant offs
.text //Text section definition
MOVE R1, 1 // R1 = 1
MOVE R2, Addr1 // R2 = Addr1 / 4 (address of label is converted into words)
LD R1, R2, offs // R1 = MEM[R2 + 0]
// R1 will be 123
**JUMP** Jump to an absolute address
--------------------------------------
**Syntax**
**JUMP** *Rdst*
**JUMP** *ImmAddr*
**JUMP** *Rdst, Condition*
**JUMP** *ImmAddr, Condition*
**Operands**
- *Rdst* Register R[0..3] containing address to jump to (expressed in 32-bit words)
- *ImmAddr* 13 bits address (expressed in bytes), aligned to 4 bytes
- *Condition*:
- EQ jump if last ALU operation result was zero
- OV jump if last ALU has set overflow flag
**Cycles**
2 (fetch) + 2 (execute)
**Description**
The instruction makes jump to the specified address. Jump can be either unconditional or based on an ALU flag.
**Examples**::
1: JUMP R1 // Jump to address in R1 (address in R1 is in 32-bit words)
2: JUMP 0x120, EQ // Jump to address 0x120 (in bytes) if ALU result is zero
3: JUMP label // Jump to label
...
label: nop // Definition of label
4: .global label // Declaration of global label
MOVE R1, label // R1 = label (value loaded into R1 is in words)
JUMP R1 // Jump to label
...
label: nop // Definition of label
**JUMPR** Jump to a relative offset (condition based on R0)
-------------------------------------------------------------
**Syntax**
**JUMPR** *Step, Threshold, Condition*
**Operands**
- *Step* relative shift from current position, in bytes
- *Threshold* threshold value for branch condition
- *Condition*:
- *GE* (greater or equal) jump if value in R0 >= threshold
- *LT* (less than) jump if value in R0 < threshold
**Cycles**
2 (fetch) + 2 (execute)
**Description**
The instruction makes a jump to a relative address if condition is true. Condition is the result of comparison of R0 register value and the threshold value.
**Examples**::
1:pos: JUMPR 16, 20, GE // Jump to address (position + 16 bytes) if value in R0 >= 20
2: // Down counting loop using R0 register
MOVE R0, 16 // load 16 into R0
label: SUB R0, R0, 1 // R0--
NOP // do something
JUMPR label, 1, GE // jump to label if R0 >= 1
**JUMPS** Jump to a relative address (condition based on stage count)
-----------------------------------------------------------------------
**Syntax**
**JUMPS** *Step, Threshold, Condition*
**Operands**
- *Step* relative shift from current position, in bytes
- *Threshold* threshold value for branch condition
- *Condition*:
- *EQ* (equal) jump if value in stage_cnt == threshold
- *LT* (less than) jump if value in stage_cnt < threshold
- *GT* (greater than) jump if value in stage_cnt > threshold
**Cycles**
2 (fetch) + 2 (execute)
**Description**
The instruction makes a jump to a relative address if condition is true. Condition is the result of comparison of count register value and threshold value.
**Examples**::
1:pos: JUMPS 16, 20, EQ // Jump to (position + 16 bytes) if stage_cnt == 20
2: // Up counting loop using stage count register
STAGE_RST // set stage_cnt to 0
label: STAGE_INC 1 // stage_cnt++
NOP // do something
JUMPS label, 16, LT // jump to label if stage_cnt < 16
**STAGE_RST** Reset stage count register
------------------------------------------
**Syntax**
**STAGE_RST**
**Operands**
No operands
**Description**
The instruction sets the stage count register to 0
**Cycles**
2 (fetch) + 2 (execute)
**Examples**::
1: STAGE_RST // Reset stage count register
**STAGE_INC** Increment stage count register
----------------------------------------------
**Syntax**
**STAGE_INC** *Value*
**Operands**
- *Value* 8 bits value
**Cycles**
2 (fetch) + 2 (execute)
**Description**
The instruction increments stage count register by given value.
**Examples**::
1: STAGE_INC 10 // stage_cnt += 10
2: // Up counting loop example:
STAGE_RST // set stage_cnt to 0
label: STAGE_INC 1 // stage_cnt++
NOP // do something
JUMPS label, 16, LT // jump to label if stage_cnt < 16
**STAGE_DEC** Decrement stage count register
----------------------------------------------
**Syntax**
**STAGE_DEC** *Value*
**Operands**
- *Value* 8 bits value
**Cycles**
2 (fetch) + 2 (execute)
**Description**
The instruction decrements stage count register by given value.
**Examples**::
1: STAGE_DEC 10 // stage_cnt -= 10;
2: // Down counting loop exaple
STAGE_RST // set stage_cnt to 0
STAGE_INC 16 // increment stage_cnt to 16
label: STAGE_DEC 1 // stage_cnt--;
NOP // do something
JUMPS label, 0, GT // jump to label if stage_cnt > 0
**HALT** End the program
--------------------------
**Syntax**
**HALT**
**Operands**
No operands
**Cycles**
2 (fetch) + 2 (execute)
**Description**
The instruction halts the ULP coprocessor and restarts ULP wakeup timer, if it is enabled.
**Examples**::
1: HALT // Halt the coprocessor
**WAKE** Wake up the chip
---------------------------
**Syntax**
**WAKE**
**Operands**
No operands
**Cycles**
2 (fetch) + 2 (execute)
**Description**
The instruction sends an interrupt from ULP to RTC controller.
- If the SoC is in deep sleep mode, and ULP wakeup is enabled, this causes the SoC to wake up.
- If the SoC is not in deep sleep mode, and ULP interrupt bit (RTC_CNTL_ULP_CP_INT_ENA) is set in RTC_CNTL_INT_ENA_REG register, RTC interrupt will be triggered.
Note that before using WAKE instruction, ULP program may needs to wait until RTC controller is ready to wake up the main CPU. This is indicated using RTC_CNTL_RDY_FOR_WAKEUP bit of RTC_CNTL_LOW_POWER_ST_REG register. If WAKE instruction is executed while RTC_CNTL_RDY_FOR_WAKEUP is zero, it has no effect (wake up does not occur).
**Examples**::
1: is_rdy_for_wakeup: // Read RTC_CNTL_RDY_FOR_WAKEUP bit
READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP)
AND r0, r0, 1
JUMP is_rdy_for_wakeup, eq // Retry until the bit is set
WAKE // Trigger wake up
REG_WR 0x006, 24, 24, 0 // Stop ULP timer (clear RTC_CNTL_ULP_CP_SLP_TIMER_EN)
HALT // Stop the ULP program
// After these instructions, SoC will wake up,
// and ULP will not run again until started by the main program.
**SLEEP** set ULP wakeup timer period
---------------------------------------
**Syntax**
**SLEEP** *sleep_reg*
**Operands**
- *sleep_reg* 0..4, selects one of ``SENS_ULP_CP_SLEEP_CYCx_REG`` registers.
**Cycles**
2 (fetch) + 2 (execute)
**Description**
The instruction selects which of the ``SENS_ULP_CP_SLEEP_CYCx_REG`` (x = 0..4) register values is to be used by the ULP wakeup timer as wakeup period. By default, the value from ``SENS_ULP_CP_SLEEP_CYC0_REG`` is used.
**Examples**::
1: SLEEP 1 // Use period set in SENS_ULP_CP_SLEEP_CYC1_REG
2: .set sleep_reg, 4 // Set constant
SLEEP sleep_reg // Use period set in SENS_ULP_CP_SLEEP_CYC4_REG
**WAIT** wait some number of cycles
-------------------------------------
**Syntax**
**WAIT** *Cycles*
**Operands**
- *Cycles* number of cycles for wait
**Cycles**
2 (fetch) + *Cycles* (execute)
**Description**
The instruction delays for given number of cycles.
**Examples**::
1: WAIT 10 // Do nothing for 10 cycles
2: .set wait_cnt, 10 // Set a constant
WAIT wait_cnt // wait for 10 cycles
**TSENS** do measurement with temperature sensor
--------------------------------------------------
**Syntax**
- **TSENS** *Rdst, Wait_Delay*
**Operands**
- *Rdst* Destination Register R[0..3], result will be stored to this register
- *Wait_Delay* number of cycles used to perform the measurement
**Cycles**
2 (fetch) + *Wait_Delay* + 3 * TSENS_CLK
**Description**
The instruction performs measurement using TSENS and stores the result into a general purpose register.
**Examples**::
1: TSENS R1, 1000 // Measure temperature sensor for 1000 cycles,
// and store result to R1
**ADC** do measurement with ADC
---------------------------------
**Syntax**
- **ADC** *Rdst, Sar_sel, Mux*
- **ADC** *Rdst, Sar_sel, Mux, 0* — deprecated form
**Operands**
- *Rdst* Destination Register R[0..3], result will be stored to this register
- *Sar_sel* Select ADC: 0 = SARADC1, 1 = SARADC2
- *Mux* - selected PAD, SARADC Pad[Mux+1] is enabled
**Cycles**
2 (fetch) + 21 + max(1, SAR_AMP_WAIT1) + max(1, SAR_AMP_WAIT2) + max(1, SAR_AMP_WAIT3) + SARx_SAMPLE_CYCLE + SARx_SAMPLE_BIT
**Description**
The instruction makes measurements from ADC.
**Examples**::
1: ADC R1, 0, 1 // Measure value using ADC1 pad 2 and store result into R1
**I2C_RD** - read single byte from I2C slave
----------------------------------------------
**Syntax**
- **I2C_RD** *Sub_addr, High, Low, Slave_sel*
**Operands**
- *Sub_addr* Address within the I2C slave to read.
- *High*, *Low* — Define range of bits to read. Bits outside of [High, Low] range are masked.
- *Slave_sel* - Index of I2C slave address to use.
**Cycles**
2 (fetch) + I2C communication time
**Description**
``I2C_RD`` instruction reads one byte from I2C slave with index ``Slave_sel``. Slave address (in 7-bit format) has to be set in advance into `SENS_I2C_SLAVE_ADDRx` register field, where ``x == Slave_sel``.
8 bits of read result is stored into `R0` register.
**Examples**::
1: I2C_RD 0x10, 7, 0, 0 // Read byte from sub-address 0x10 of slave with address set in SENS_I2C_SLAVE_ADDR0
**I2C_WR** - write single byte to I2C slave
----------------------------------------------
**Syntax**
- **I2C_WR** *Sub_addr, Value, High, Low, Slave_sel*
**Operands**
- *Sub_addr* Address within the I2C slave to write.
- *Value* 8-bit value to be written.
- *High*, *Low* — Define range of bits to write. Bits outside of [High, Low] range are masked.
- *Slave_sel* - Index of I2C slave address to use.
**Cycles**
2 (fetch) + I2C communication time
**Description**
``I2C_WR`` instruction writes one byte to I2C slave with index ``Slave_sel``. Slave address (in 7-bit format) has to be set in advance into `SENS_I2C_SLAVE_ADDRx` register field, where ``x == Slave_sel``.
**Examples**::
1: I2C_WR 0x20, 0x33, 7, 0, 1 // Write byte 0x33 to sub-address 0x20 of slave with address set in SENS_I2C_SLAVE_ADDR1.
**REG_RD** read from peripheral register
------------------------------------------
**Syntax**
**REG_RD** *Addr, High, Low*
**Operands**
- *Addr* register address, in 32-bit words
- *High* High part of R0
- *Low* Low part of R0
**Cycles**
2 (fetch) + 6 (execute)
**Description**
The instruction reads up to 16 bits from a peripheral register into a general purpose register: ``R0 = REG[Addr][High:Low]``.
This instruction can access registers in RTC_CNTL, RTC_IO, SENS, and RTC_I2C peripherals. Address of the the register, as seen from the ULP,
can be calculated from the address of the same register on the DPORT bus as follows::
addr_ulp = (addr_dport - DR_REG_RTCCNTL_BASE) / 4
**Examples**::
1: REG_RD 0x120, 2, 0 // load 4 bits: R0 = {12'b0, REG[0x120][7:4]}
**REG_WR** write to peripheral register
-----------------------------------------
**Syntax**
**REG_WR** *Addr, High, Low, Data*
**Operands**
- *Addr* register address, in 32-bit words.
- *High* High part of R0
- *Low* Low part of R0
- *Data* value to write, 8 bits
**Cycles**
2 (fetch) + 10 (execute)
**Description**
The instruction writes up to 8 bits from a general purpose register into a peripheral register. ``REG[Addr][High:Low] = data``
This instruction can access registers in RTC_CNTL, RTC_IO, SENS, and RTC_I2C peripherals. Address of the the register, as seen from the ULP,
can be calculated from the address of the same register on the DPORT bus as follows::
addr_ulp = (addr_dport - DR_REG_RTCCNTL_BASE) / 4
**Examples**::
1: REG_WR 0x120, 7, 0, 0x10 // set 8 bits: REG[0x120][7:0] = 0x10
Convenience macros for peripheral registers access
--------------------------------------------------
ULP source files are passed through C preprocessor before the assembler. This allows certain macros to be used to facilitate access to peripheral registers.
Some existing macros are defined in ``soc/soc_ulp.h`` header file. These macros allow access to the fields of peripheral registers by their names.
Peripheral registers names which can be used with these macros are the ones defined in ``soc/rtc_cntl_reg.h``, ``soc/rtc_io_reg.h``, ``soc/sens_reg.h``, and ``soc/rtc_i2c_reg.h``.
READ_RTC_REG(rtc_reg, low_bit, bit_width)
Read up to 16 bits from rtc_reg[low_bit + bit_width - 1 : low_bit] into R0. For example::
#include "soc/soc_ulp.h"
#include "soc/rtc_cntl_reg.h"
/* Read 16 lower bits of RTC_CNTL_TIME0_REG into R0 */
READ_RTC_REG(RTC_CNTL_TIME0_REG, 0, 16)
READ_RTC_FIELD(rtc_reg, field)
Read from a field in rtc_reg into R0, up to 16 bits. For example::
#include "soc/soc_ulp.h"
#include "soc/sens_reg.h"
/* Read 8-bit SENS_TSENS_OUT field of SENS_SAR_SLAVE_ADDR3_REG into R0 */
READ_RTC_FIELD(SENS_SAR_SLAVE_ADDR3_REG, SENS_TSENS_OUT)
WRITE_RTC_REG(rtc_reg, low_bit, bit_width, value)
Write immediate value into rtc_reg[low_bit + bit_width - 1 : low_bit], bit_width <= 8. For example::
#include "soc/soc_ulp.h"
#include "soc/rtc_io_reg.h"
/* Set BIT(2) of RTC_GPIO_OUT_DATA_W1TS field in RTC_GPIO_OUT_W1TS_REG */
WRITE_RTC_REG(RTC_GPIO_OUT_W1TS_REG, RTC_GPIO_OUT_DATA_W1TS_S + 2, 1, 1)
WRITE_RTC_FIELD(rtc_reg, field, value)
Write immediate value into a field in rtc_reg, up to 8 bits. For example::
#include "soc/soc_ulp.h"
#include "soc/rtc_cntl_reg.h"
/* Set RTC_CNTL_ULP_CP_SLP_TIMER_EN field of RTC_CNTL_STATE0_REG to 0 */
WRITE_RTC_FIELD(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN, 0)
+1
View File
@@ -0,0 +1 @@
.. include:: ../../../components/ulp/README.rst
+155
View File
@@ -0,0 +1,155 @@
Unit Testing in ESP32
=====================
ESP-IDF comes with a unit test app based on Unity - unit test framework. Unit tests are integrated in the ESP-IDF repository and are placed in ``test`` subdirectory of each component respectively.
Add normal test cases
---------------------
Unit tests are added in the ``test`` subdirectory of the respective component.
Tests are added in C files, a single C file can include multiple test cases.
Test files start with the word "test".
The test file should include unity.h and the header for the C module to be tested.
Tests are added in a function in the C file as follows::
TEST_CASE("test name", "[module name]"
{
// Add test here
}
First argument is a descriptive name for the test, second argument is an identifier in square brackets.
Identifiers are used to group related test, or tests with specific properties.
There is no need to add a main function with ``UNITY_BEGIN()`` and ``UNITY_END()`` in each test case.
``unity_platform.c`` will run ``UNITY_BEGIN()``, run the tests cases, and then call ``UNITY_END()``.
Each `test` subdirectory needs to include component.mk file with at least the following line of code::
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
See http://www.throwtheswitch.org/unity for more information about writing tests in Unity.
Add multiple devices test cases
-------------------------------
The normal test cases will be executed on one DUT (Device Under Test). Components need to communicate with each other (like GPIO, SPI ...) can't be tested with normal test cases.
Multiple devices test cases support writing and running test with multiple DUTs.
Here's an example of multiple devices test case::
void gpio_master_test()
{
gpio_config_t slave_config = {
.pin_bit_mask = 1 << MASTER_GPIO_PIN,
.mode = GPIO_MODE_INPUT,
};
gpio_config(&slave_config);
unity_wait_for_signal("output high level");
TEST_ASSERT(gpio_get_level(MASTER_GPIO_PIN) == 1);
}
void gpio_slave_test()
{
gpio_config_t master_config = {
.pin_bit_mask = 1 << SLAVE_GPIO_PIN,
.mode = GPIO_MODE_OUTPUT,
};
gpio_config(&master_config);
gpio_set_level(SLAVE_GPIO_PIN, 1);
unity_send_signal("output high level");
}
TEST_CASE_MULTIPLE_DEVICES("gpio multiple devices test example", "[driver]", gpio_master_test, gpio_slave_test);
The macro ``TEST_CASE_MULTIPLE_DEVICES`` is used to declare multiple devices test cases.
First argument is test case name, second argument is test case description.
From the third argument, upto 5 test functions can be defined, each function will be the entry point of tests running on each DUT.
Running test cases from different DUTs could require synchronizing between DUTs. We provide ``unity_wait_for_signal`` and ``unity_send_signal`` to support synchronizing with UART.
As the secnario in the above example, slave should get GPIO level after master set level. DUT UART console will prompt and requires user interaction:
DUT1 (master) console::
Waiting for signal: [output high level]!
Please press "Enter" key to once any board send this signal.
DUT2 (slave) console::
Send signal: [output high level]!
Once the signal is set from DUT2, you need to press "Enter" on DUT1, then DUT1 unblocks from ``unity_wait_for_signal`` and starts to change GPIO level.
Building unit test app
----------------------
Follow the setup instructions in the top-level esp-idf README.
Make sure that IDF_PATH environment variable is set to point to the path of esp-idf top-level directory.
Change into tools/unit-test-app directory to configure and build it:
* `make menuconfig` - configure unit test app.
* `make TESTS_ALL=1` - build unit test app with tests for each component having tests in the ``test`` subdirectory.
* `make TEST_COMPONENTS='xxx'` - build unit test app with tests for specific components.
When the build finishes, it will print instructions for flashing the chip. You can simply run ``make flash`` to flash all build output.
You can also run ``make flash TESTS_ALL=1`` or ``make TEST_COMPONENTS='xxx'`` to build and flash. Everything needed will be rebuilt automatically before flashing.
Use menuconfig to set the serial port for flashing.
Running unit tests
------------------
After flashing reset the ESP32 and it will boot the unit test app.
When unit test app is idle, press "Enter" will make it print test menu with all available tests::
Here's the test menu, pick your combo:
(1) "esp_ota_begin() verifies arguments" [ota]
(2) "esp_ota_get_next_update_partition logic" [ota]
(3) "Verify bootloader image in flash" [bootloader_support]
(4) "Verify unit test app image" [bootloader_support]
(5) "can use new and delete" [cxx]
(6) "can call virtual functions" [cxx]
(7) "can use static initializers for non-POD types" [cxx]
(8) "can use std::vector" [cxx]
(9) "static initialization guards work as expected" [cxx]
(10) "global initializers run in the correct order" [cxx]
(11) "before scheduler has started, static initializers work correctly" [cxx]
(12) "adc2 work with wifi" [adc]
(13) "gpio master/slave test example" [ignore][misc][test_env=UT_T2_1]
(1) "gpio_master_test"
(2) "gpio_slave_test"
(14) "SPI Master clockdiv calculation routines" [spi]
(15) "SPI Master test" [spi][ignore]
(16) "SPI Master test, interaction of multiple devs" [spi][ignore]
(17) "SPI Master no response when switch from host1 (HSPI) to host2 (VSPI)" [spi]
(18) "SPI Master DMA test, TX and RX in different regions" [spi]
(19) "SPI Master DMA test: length, start, not aligned" [spi]
Normal case will print the case name and description. Master slave cases will also print the sub-menu (the registered test function names).
Test cases can be run by inputting one of the following:
- Test case name in quotation marks to run a single test case
- Test case index to run a single test case
- Module name in square brackets to run all test cases for a specific module
- An asterisk to run all test cases
After you select multiple devices test case, it will print sub menu::
Running gpio master/slave test example...
gpio master/slave test example
(1) "gpio_master_test"
(2) "gpio_slave_test"
You need to input number to select the test running on the DUT.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,9 @@
BT COMMON
=========
.. toctree::
:caption: Bluetooth Common Defines and APIs
Bluetooth DEFINE <esp_bt_defs>
Bluetooth MAIN <esp_bt_main>
Bluetooth DEVICE <esp_bt_device>
+11
View File
@@ -0,0 +1,11 @@
BT LE
=========
.. toctree::
:caption: Bluetooth LE
BLE GAP <esp_gap_ble>
BLE GATT DEFINE <esp_gatt_defs>
BLE GATT SERVER <esp_gatts>
BLE GATT CLIENT <esp_gattc>
BLE BLUFI <esp_blufi>
@@ -0,0 +1,10 @@
CLASSIC BT
==========
.. toctree::
:caption: Classic BT
BT GAP <esp_gap_bt>
BT A2DP <esp_a2dp>
BT AVRC <esp_avrc>
BT SPP <esp_spp>
@@ -0,0 +1,21 @@
Controller && VHCI
==================
Overview
--------
`Instructions`_
.. _Instructions: ../template.html
Application Example
-------------------
Check :example:`bluetooth` folder in ESP-IDF examples, which contains the following application:
* This is a BLE advertising demo with virtual HCI interface. Send Reset/ADV_PARAM/ADV_DATA/ADV_ENABLE HCI command for BLE advertising - :example:`bluetooth/ble_adv`.
API Reference
-------------
.. include:: /_build/inc/bt.inc
@@ -0,0 +1,22 @@
Bluetooth A2DP API
==================
Overview
--------
`Instructions`_
.. _Instructions: ../template.html
Application Example
-------------------
Check :example:`bluetooth` folder in ESP-IDF examples, which contains the following application:
* This is a A2DP sink client demo. This demo can be discovered and connected by A2DP source device and receive the audio stream from remote device - :example:`bluetooth/a2dp_sink`
API Reference
-------------
.. include:: /_build/inc/esp_a2dp_api.inc
@@ -0,0 +1,23 @@
BT AVRCP APIs
=============
Overview
--------
Bluetooth AVRCP reference APIs.
`Instructions`_
Application Example
-------------------
`Instructions`_
.. _Instructions: ../template.html
API Reference
-------------
.. include:: /_build/inc/esp_avrc_api.inc
@@ -0,0 +1,23 @@
BLUFI API
=========
Overview
--------
BLUFI is a profile based GATT to config ESP32 WIFI to connect/disconnect AP or setup a softap and etc.
Use should concern these things:
1. The event sent from profile. Then you need to do something as the event indicate.
2. Security reference. You can write your own Security functions such as symmetrical encryption/decryption and checksum functions. Even you can define the "Key Exchange/Negotiation" procedure.
Application Example
-------------------
Check :example:`bluetooth` folder in ESP-IDF examples, which contains the following application:
* This is a BLUFI demo. This demo can set ESP32's wifi to softap/station/softap&station mode and config wifi connections - :example:`bluetooth/blufi`
API Reference
-------------
.. include:: /_build/inc/esp_blufi_api.inc
@@ -0,0 +1,21 @@
BT GENERIC DEFINES
==================
Overview
--------
`Instructions`_
Application Example
-------------------
`Instructions`_
.. _Instructions: ../template.html
API Reference
-------------
.. include:: /_build/inc/esp_bt_defs.inc
@@ -0,0 +1,21 @@
BT DEVICE APIs
===============
Overview
--------
Bluetooth device reference APIs.
`Instructions`_
Application Example
-------------------
`Instructions`_
.. _Instructions: ../template.html
API Reference
-------------
.. include:: /_build/inc/esp_bt_device.inc
@@ -0,0 +1,22 @@
BT MAIN API
===========
Overview
--------
`Instructions`_
Application Example
-------------------
`Instructions`_
.. _Instructions: ../template.html
API Reference
-------------
.. include:: /_build/inc/esp_bt_main.inc
@@ -0,0 +1,30 @@
GAP API
=======
Overview
--------
`Instructions`_
.. _Instructions: ../template.html
Application Example
-------------------
Check :example:`bluetooth` folder in ESP-IDF examples, which contains the following demos and their tutorials:
* This is a SMP security client demo and its tutorial. This demo initiates its security parameters and acts as a GATT client, which can send a security request to the peer device and then complete the encryption procedure.
- :example:`bluetooth/gatt_security_client`
- :example_file:`GATT Security Client Example Walkthrough <bluetooth/gatt_security_client/tutorial/Gatt_Security_Client_Example_Walkthrough.md>`
* This is a SMP security server demo and its tutorial. This demo initiates its security parameters and acts as a GATT server, which can send a pair request to the peer device and then complete the encryption procedure.
- :example:`bluetooth/gatt_security_server`
- :example_file:`GATT Security Server Example Walkthrough <bluetooth/gatt_security_server/tutorial/Gatt_Security_Server_Example_Walkthrough.md>`
API Reference
-------------
.. include:: /_build/inc/esp_gap_ble_api.inc
@@ -0,0 +1,20 @@
CLASSIC BLUETOOTH GAP API
=========================
Overview
--------
`Instructions`_
Application Example
-------------------
`Instructions`_
.. _Instructions: ../template.html
API Reference
-------------
.. include:: /_build/inc/esp_gap_bt_api.inc
@@ -0,0 +1,21 @@
GATT DEFINES
============
Overview
--------
`Instructions`_
Application Example
-------------------
`Instructions`_
.. _Instructions: ../template.html
API Reference
-------------
.. include:: /_build/inc/esp_gatt_defs.inc
@@ -0,0 +1,34 @@
GATT CLIENT API
===============
Overview
--------
`Instructions`_
.. _Instructions: ../template.html
Application Example
-------------------
Check :example:`bluetooth` folder in ESP-IDF examples, which contains the following demos and their tutorials:
* This is a GATT client demo and its tutorial. This demo can scan for devices, connect to the GATT server and discover its services.
- :example:`bluetooth/gatt_client`
- :example_file:`GATT Client Example Walkthrough <bluetooth/gatt_client/tutorial/Gatt_Client_Example_Walkthrough.md>`
* This is a multiple connection demo and its tutorial. This demo can connect to multiple GATT server devices and discover their services.
- :example:`bluetooth/gattc_multi_connect`
- :example_file:`GATT Client Multi-connection Example Walkthrough <bluetooth/gattc_multi_connect/tutorial/Gatt_Client_Multi_Connection_Example_Walkthrough.md>`
* This is a BLE SPP-Like demo. This demo, which acts as a GATT client, can receive data from UART and then send the data to the peer device automatically.
- :example:`bluetooth/ble_spp_client`
API Reference
-------------
.. include:: /_build/inc/esp_gattc_api.inc
@@ -0,0 +1,34 @@
GATT SERVER API
===============
Overview
--------
`Instructions`_
.. _Instructions: ../template.html
Application Example
-------------------
Check :example:`bluetooth` folder in ESP-IDF examples, which contains the following demos and their tutorials:
* This is a GATT sever demo and its tutorial. This demo creates a GATT service with an attribute table, which releases the user from adding attributes one by one. This is the recommended method of adding attributes.
- :example:`bluetooth/gatt_server_service_table`
- :example_file:`GATT Server Service Table Example Walkthrough <bluetooth/gatt_server_service_table/tutorial/Gatt_Server_Service_Table_Example_Walkthrough.md>`
* This is a GATT server demo and its tutorial. This demo creates a GATT service by adding attributes one by one as defined by Bluedroid. The recommended method of adding attributes is presented in example above.
- :example:`bluetooth/gatt_server`
- :example_file:`GATT Server Example Walkthrough <bluetooth/gatt_server/tutorial/Gatt_Server_Example_Walkthrough.md>`
* This is a BLE SPP-Like demo. This demo, which acts as a GATT server, can receive data from UART and then send the data to the peer device automatically.
- :example:`bluetooth/ble_spp_server`
API Reference
-------------
.. include:: /_build/inc/esp_gatts_api.inc
@@ -0,0 +1,22 @@
SPP API
===============
Overview
--------
`Instructions`_
.. _Instructions: ../template.html
Application Example
-------------------
Check :example:`bluetooth` folder in ESP-IDF examples, which contains the following application:
* This is a SPP demo. This demo can discover the service, connect, send and recive SPP data :example:`bluetooth/bt_spp_acceptor`, :example:`bluetooth/bt_spp_initiator`
API Reference
-------------
.. include:: /_build/inc/esp_spp_api.inc
+27
View File
@@ -0,0 +1,27 @@
Bluetooth API
*************
.. toctree::
:maxdepth: 2
Bluetooth Controller && VHCI <controller_vhci>
Bluetooth Common <bt_common>
Bluetooth LE <bt_le>
Bluetooth Classic <classic_bt>
To see the overview of the ESP32 Bluetooth stack architecture, follow links below:
* `ESP32 Bluetooth Architecture (PDF) [English] <http://espressif.com/sites/default/files/documentation/esp32_bluetooth_architecture_en.pdf>`_
* `ESP32 Bluetooth Architecture (PDF) [中文] <http://espressif.com/sites/default/files/documentation/esp32_bluetooth_architecture_cn.pdf>`_
Example code for this API section is provided in :example:`bluetooth` directory of ESP-IDF examples.
Several examples contain detailed description. To see them please follow links below:
* :example_file:`GATT Client Example Walkthrough <bluetooth/gatt_client/tutorial/Gatt_Client_Example_Walkthrough.md>`
* :example_file:`GATT Server Service Table Example Walkthrough <bluetooth/gatt_server_service_table/tutorial/Gatt_Server_Service_Table_Example_Walkthrough.md>`
* :example_file:`GATT Server Example Walkthrough <bluetooth/gatt_server/tutorial/Gatt_Server_Example_Walkthrough.md>`
* :example_file:`GATT Security Client Example Walkthrough <bluetooth/gatt_security_client/tutorial/Gatt_Security_Client_Example_Walkthrough.md>`
* :example_file:`GATT Security Server Example Walkthrough <bluetooth/gatt_security_server/tutorial/Gatt_Security_Server_Example_Walkthrough.md>`
* :example_file:`GATT Client Multi-connection Example Walkthrough <bluetooth/gattc_multi_connect/tutorial/Gatt_Client_Multi_Connection_Example_Walkthrough.md>`
@@ -0,0 +1,47 @@
ETHERNET
========
Application Example
-------------------
Ethernet example: :example:`ethernet/ethernet`.
PHY Interfaces
--------------
The configured PHY model(s) are set in software by configuring the eth_config_t structure for the given PHY.
Headers include a default configuration structure. These default configurations will need some members overriden or re-set before they can be used for a particular PHY hardware configuration. Consult the Ethernet example to see how this is done.
* :component_file:`ethernet/include/eth_phy/phy.h` (common)
* :component_file:`ethernet/include/eth_phy/phy_tlk110.h`
* :component_file:`ethernet/include/eth_phy/phy_lan8720.h`
PHY Configuration Constants
^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. doxygenvariable:: phy_tlk110_default_ethernet_config
.. doxygenvariable:: phy_lan8720_default_ethernet_config
API Reference - Ethernet
------------------------
.. include:: /_build/inc/esp_eth.inc
API Reference - PHY Common
--------------------------
.. include:: /_build/inc/phy.inc
API Reference - PHY TLK110
--------------------------
.. include:: /_build/inc/phy_tlk110.inc
API Reference - PHY LAN8720
---------------------------
.. include:: /_build/inc/phy_lan8720.inc
+10
View File
@@ -0,0 +1,10 @@
Ethernet API
************
.. toctree::
:maxdepth: 1
Ethernet <esp_eth>
Example code for this API section is provided in :example:`ethernet` directory of ESP-IDF examples.
+16
View File
@@ -0,0 +1,16 @@
*************
API Reference
*************
.. toctree::
:maxdepth: 2
Wi-Fi <wifi/index>
Bluetooth <bluetooth/index>
Ethernet <ethernet/index>
Peripherals <peripherals/index>
Protocols <protocols/index>
Storage <storage/index>
System <system/index>
Configuration Options <kconfig>
+37
View File
@@ -0,0 +1,37 @@
Configuration Options
*********************
Introduction
============
ESP-IDF uses Kconfig_ system to provide a compile-time configuration mechanism. Kconfig is based around options of several types: integer, string, boolean. Kconfig files specify dependencies between options, default values of the options, the way the options are grouped together, etc.
Applications developers can use ``make menuconfig`` build target to edit components' configuration. This configuration is saved inside ``sdkconfig`` file in the project root directory. Based on ``sdkconfig``, application build targets will generate ``sdkconfig.h`` file in the build directory, and will make sdkconfig options available to component makefiles.
Using sdkconfig.defaults
========================
When updating ESP-IDF version, it is not uncommon to find that new Kconfig options are introduced. When this happens, application build targets will offer an interactive prompt to select values for the new options. New values are then written into ``sdkconfig`` file. To supress interactive prompts, applications can either define ``BATCH_BUILD`` environment variable, which will cause all prompts to be suppressed. This is the same effect as that of ``V`` or ``VERBOSE`` variables. Alternatively, ``defconfig`` build target can be used to update configuration for all new variables to the default values.
In some cases, such as when ``sdkconfig`` file is under revision control, the fact that ``sdkconfig`` file gets changed by the build system may be inconvenient. The build system offers a way to avoid this, in the form of ``sdkconfig.defaults`` file. This file is never touched by the build system, and must be created manually. It can contain all the options which matter for the given application. The format is the same as that of the ``sdkconfig`` file. Once ``sdkconfig.defaults`` is created, ``sdkconfig`` can be deleted and added to the ignore list of the revision control system (e.g. ``.gitignore`` file for git). Project build targets will automatically create ``sdkconfig`` file, populated with the settings from ``sdkconfig.defaults`` file, and the rest of the settings will be set to their default values. Note that when ``make defconfig`` is used, settings in sdkconfig will be overriden by the ones in ``sdkconfig.defaults``. For more information, see :ref:`custom-sdkconfig-defaults`.
Configuration Options Reference
===============================
Subsequent sections contain the list of available ESP-IDF options, automatically generated from Kconfig files. Note that depending on the options selected, some options listed here may not be visible by default in the interface of menuconfig.
By convention, all option names are upper case with underscores. When Kconfig generates sdkconfig and sdkconfig.h files, option names are prefixed with ``CONFIG_``. So if an option ``ENABLE_FOO`` is defined in a Kconfig file and selected in menuconfig, then sdkconfig and sdkconfig.h files will have ``CONFIG_ENABLE_FOO`` defined. In this reference, option names are also prefixed with ``CONFIG_``, same as in the source code.
.. include:: /_build/inc/kconfig.inc
Customisations
==============
Because IDF builds by default with :ref:`warn-undefined-variables`, when the Kconfig tool generates Makefiles (the ``auto.conf`` file) its behaviour has been customised. In normal Kconfig, a variable which is set to "no" is undefined. In IDF's version of Kconfig, this variable is defined in the Makefile but has an empty value.
(Note that ``ifdef`` and ``ifndef`` can still be used in Makefiles, because they test if a variable is defined *and has a non-empty value*.)
When generating header files for C & C++, the behaviour is not customised - so ``#ifdef`` can be used to test if a boolean config item is set or not.
.. _Kconfig: https://www.kernel.org/doc/Documentation/kbuild/kconfig-language.txt
+207
View File
@@ -0,0 +1,207 @@
Analog to Digital Converter
===========================
Overview
--------
ESP32 integrates two 12-bit SAR (`Successive Approximation Register <https://en.wikipedia.org/wiki/Successive_approximation_ADC>`_) ADCs (Analog to Digital Converters) and supports measurements on 18 channels (analog enabled pins). Some of these pins can be used to build a programmable gain amplifier which is used for the measurement of small analog signals.
The ADC driver API supports ADC1 (8 channels, attached to GPIOs 32 - 39), and ADC2 (10 channels, attached to GPIOs 0, 2, 4, 12 - 15 and 25 - 27).
However, there're some restrictions for the application to use ADC2:
1. The application can use ADC2 only when Wi-Fi driver is not started, since the ADC is also used by the Wi-Fi driver, which has higher priority.
2. Some of the ADC2 pins are used as strapping pins (GPIO 0, 2, 15), so they cannot be used freely. For examples, for official Develop Kits:
- `ESP32 Core Board V2 / ESP32 DevKitC <http://esp-idf.readthedocs.io/en/latest/hw-reference/modules-and-boards.html#esp32-core-board-v2-esp32-devkitc>`_: GPIO 0 cannot be used due to external auto program circuits.
- `ESP-WROVER-KIT V3 <http://esp-idf.readthedocs.io/en/latest/hw-reference/modules-and-boards.html#esp-wrover-kit-v3>`_: GPIO 0, 2, 4 and 15 cannot be used due to external connections for different purposes.
Configuration and Reading ADC
-----------------------------
The ADC should be configured before reading is taken.
- For ADC1, configure desired precision and attenuation by calling functions :cpp:func:`adc1_config_width` and :cpp:func:`adc1_config_channel_atten`.
- For ADC2, configure the attenuation by :cpp:func:`adc2_config_channel_atten`. The reading width of ADC2 is configured every time you take the reading.
Attenuation configuration is done per channel, see :cpp:type:`adc1_channel_t` and :cpp:type:`adc2_channel_t`, set as a parameter of above functions.
Then it is possible to read ADC conversion result with :cpp:func:`adc1_get_raw` and :cpp:func:`adc2_get_raw`. Reading width of ADC2 should be set as a parameter of :cpp:func:`adc2_get_raw` instead of in the configuration functions.
.. note:: Since the ADC2 is shared with the WIFI module, which has higher priority, reading operation of :cpp:func:`adc2_get_raw` will fail between :cpp:func:`esp_wifi_start()` and :cpp:func:`esp_wifi_stop()`. Use the return code to see whether the reading is successful.
It is also possible to read the internal hall effect sensor via ADC1 by calling dedicated function :cpp:func:`hall_sensor_read`. Note that even the hall sensor is internal to ESP32, reading from it uses channels 0 and 3 of ADC1 (GPIO 36 and 39). Do not connect anything else to these pins and do not change their configuration. Otherwise it may affect the measurement of low value signal from the sesnor.
This API provides convenient way to configure ADC1 for reading from :doc:`ULP <../../api-guides/ulp>`. To do so, call function :cpp:func:`adc1_ulp_enable` and then set precision and attenuation as discussed above.
There is another specific function :cpp:func:`adc2_vref_to_gpio` used to route internal reference voltage to a GPIO pin. It comes handy to calibrate ADC reading and this is discussed in section :ref:`adc-api-adc-calibration`.
Application Examples
--------------------
Reading voltage on ADC1 channel 0 (GPIO 36)::
#include <driver/adc.h>
...
adc1_config_width(ADC_WIDTH_BIT_12);
adc1_config_channel_atten(ADC1_CHANNEL_0,ADC_ATTEN_DB_0);
int val = adc1_get_raw(ADC1_CHANNEL_0);
The input voltage in above example is from 0 to 1.1V (0 dB attenuation). The input range can be extended by setting higher attenuation, see :cpp:type:`adc_atten_t`.
An example using the ADC driver including calibration (discussed below) is available in esp-idf: :example:`peripherals/adc`
Reading voltage on ADC2 channel 7 (GPIO 27)::
#include <driver/adc.h>
...
int read_raw;
adc2_config_channel_atten( ADC2_CHANNEL_7, ADC_ATTEN_0db );
esp_err_t r = adc2_get_raw( ADC2_CHANNEL_7, ADC_WIDTH_12Bit, &read_raw);
if ( r == ESP_OK ) {
printf("%d\n", read_raw );
} else if ( r == ESP_ERR_TIMEOUT ) {
printf("ADC2 used by Wi-Fi.\n");
}
The reading may fail due to collision with Wi-Fi, should check it.
An example using the ADC2 driver to read the output of DAC is available in esp-idf: :example:`peripherals/adc2`
Reading the internal hall effect sensor::
#include <driver/adc.h>
...
adc1_config_width(ADC_WIDTH_BIT_12);
int val = hall_sensor_read();
The value read in both these examples is 12 bits wide (range 0-4095).
.. _adc-api-adc-calibration:
Minimizing Noise
----------------
The ESP32 ADC can be sensitive to noise leading to large discrepancies in ADC readings. To minimize noise, users may connect a 0.1uF capacitor to the ADC input pad in use. Multisampling may also be used to further mitigate the effects of noise.
.. figure:: ../../_static/adc-noise-graph.jpg
:align: center
:alt: ADC noise mitigation
Graph illustrating noise mitigation using capacitor and multisampling of 64 samples.
ADC Calibration
---------------
The :component_file:`esp_adc_cal/include/esp_adc_cal.h` API provides functions to correct for differences in measured voltages caused by variation of ADC reference voltages (Vref) between chips. Per design the ADC reference voltage is 1100mV, however the true reference voltage can range from 1000mV to 1200mV amongst different ESP32s.
.. figure:: ../../_static/adc-vref-graph.jpg
:align: center
:alt: ADC reference voltage comparison
Graph illustrating effect of differing reference voltages on the ADC voltage curve.
Correcting ADC readings using this API involves characterizing one of the ADCs at a given attenuation to obtain a characteristics curve (ADC-Voltage curve) that takes into account the difference in ADC reference voltage. The characteristics curve is in the form of ``y = coeff_a * x + coeff_b`` and is used to convert ADC readings to voltages in mV. Calculation of the characteristics curve is based on calibration values which can be stored in eFuse or provided by the user.
Calibration Values
^^^^^^^^^^^^^^^^^^
Calibration values are used to generate characteristic curves that account for the unique ADC reference voltage of a particular ESP32. There are currently three sources of calibration values. The availability of these calibration values will depend on the type and production date of the ESP32 chip/module.
**Two Point** values represent each of the ADCs readings at 150mV and 850mV. These values are measured and burned into eFuse ``BLOCK3`` during factory calibration.
**eFuse Vref** represents the true ADC reference voltage. This value is measured and burned into eFuse ``BLOCK0`` during factory calibration.
**Default Vref** is an estimate of the ADC reference voltage provided by the user as a parameter during characterization. If Two Point or eFuse Vref values are unavailable, **Default Vref** will be used.
Application Example
^^^^^^^^^^^^^^^^^^^
For a full example see esp-idf: :example:`peripherals/adc`
Characterizing an ADC at a particular attenuation::
#include "driver/adc.h"
#include "esp_adc_cal.h"
...
//Characterize ADC at particular atten
esp_adc_cal_characteristics_t *adc_chars = calloc(1, sizeof(esp_adc_cal_characteristics_t));
esp_adc_cal_value_t val_type = esp_adc_cal_characterize(unit, atten, ADC_WIDTH_BIT_12, DEFAULT_VREF, adc_chars);
//Check type of calibration value used to characterize ADC
if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) {
printf("eFuse Vref");
} else if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) {
printf("Two Point");
} else {
printf("Default");
}
Reading an ADC then converting the reading to a voltage::
#include "driver/adc.h"
#include "esp_adc_cal.h"
...
uint32_t reading = adc1_get_raw(ADC1_CHANNEL_5);
uint32_t voltage = esp_adc_cal_raw_to_voltage(reading, adc_chars);
Routing ADC reference voltage to GPIO, so it can be manually measured (for **Default Vref**)::
#include "driver/adc.h"
...
esp_err_t status = adc2_vref_to_gpio(GPIO_NUM_25);
if (status == ESP_OK) {
printf("v_ref routed to GPIO\n");
} else {
printf("failed to route v_ref\n");
}
GPIO Lookup Macros
------------------
There are macros available to specify the GPIO number of a ADC channel, or vice versa.
e.g.
1. ``ADC1_CHANNEL_0_GPIO_NUM`` is the GPIO number of ADC1 channel 0 (36);
2. ``ADC1_GPIO32_CHANNEL`` is the ADC1 channel number of GPIO 32 (ADC1 channel 4).
API Reference
-------------
This reference covers three components:
* :ref:`adc-api-reference-adc-driver`
* :ref:`adc-api-reference-adc-calibration`
* :ref:`adc-api-reference-gpio-lookup-macros`
.. _adc-api-reference-adc-driver:
ADC driver
^^^^^^^^^^
.. include:: /_build/inc/adc.inc
.. _adc-api-reference-adc-calibration:
ADC Calibration
^^^^^^^^^^^^^^^
.. include:: /_build/inc/esp_adc_cal.inc
.. _adc-api-reference-gpio-lookup-macros:
GPIO Lookup Macros
^^^^^^^^^^^^^^^^^^
.. include:: /_build/inc/adc_channel.inc
+41
View File
@@ -0,0 +1,41 @@
Digital To Analog Converter
===========================
Overview
--------
ESP32 has two 8-bit DAC (digital to analog converter) channels, connected to GPIO25 (Channel 1) and GPIO26 (Channel 2).
The DAC driver allows these channels to be set to arbitrary voltages.
The DAC channels can also be driven with DMA-style written sample data, via the :doc:`I2S driver <i2s>` when using the "built-in DAC mode".
For other analog output options, see the :doc:`Sigma-delta Modulation module <sigmadelta>` and the :doc:`LED Control module <ledc>`. Both these modules produce high frequency PWM output, which can be hardware low-pass filtered in order to generate a lower frequency analog output.
Application Example
-------------------
Setting DAC channel 1 (GPIO 25) voltage to approx 0.78 of VDD_A voltage (VDD * 200 / 255). For VDD_A 3.3V, this is 2.59V::
#include <driver/dac.h>
...
dac_output_enable(DAC_CHANNEL_1);
dac_output_voltage(DAC_CHANNEL_1, 200);
API Reference
-------------
.. include:: /_build/inc/dac.inc
GPIO Lookup Macros
^^^^^^^^^^^^^^^^^^
Some useful macros can be used to specified the GPIO number of a DAC channel, or vice versa.
e.g.
1. ``DAC_CHANNEL_1_GPIO_NUM`` is the GPIO number of channel 1 (25);
2. ``DAC_GPIO26_CHANNEL`` is the channel number of GPIO 26 (channel 2).
.. include:: /_build/inc/dac_channel.inc
@@ -0,0 +1,28 @@
GPIO & RTC GPIO
===============
Overview
--------
The ESP32 chip features 40 physical GPIO pads. Some GPIO pads cannot be used or do not have the corresponding pin on the chip package(refer to technical reference manual). Each pad can be used as a general purpose I/O or can be connected to an internal peripheral signal.
- Note that GPIO6-11 are usually used for SPI flash.
- GPIO34-39 can only be set as input mode and do not have software pullup or pulldown functions.
There is also separate "RTC GPIO" support, which functions when GPIOs are routed to the "RTC" low-power and analog subsystem. These pin functions can be used when in deep sleep, when the :doc:`Ultra Low Power co-processor <../../api-guides/ulp>` is running, or when analog functions such as ADC/DAC/etc are in use.
Application Example
-------------------
GPIO output and input interrupt example: :example:`peripherals/gpio`.
API Reference - Normal GPIO
---------------------------
.. include:: /_build/inc/gpio.inc
API Reference - RTC GPIO
------------------------
.. include:: /_build/inc/rtc_io.inc
+337
View File
@@ -0,0 +1,337 @@
I2C
===
An I2C (Inter-Integrated Circuit) bus can be used for communication with several external devices connected to the same bus as ESP32. There are two I2C controllers on board of the ESP32, each of which can be set to master mode or slave mode.
Overview
--------
The following sections will walk you through typical steps to configure and operate the I2C driver:
1. :ref:`i2c-api-configure-driver` - select driver's parameters like master or slave mode, set specific GPIO pins to act as SDA and SCL, set the clock speed, etc.
2. :ref:`i2c-api-install-driver`- activate driver in master or slave mode to operate on one of the two I2C controllers available on ESP32.
3. :ref:`i2c-api-run-communication`:
a) :ref:`i2c-api-master-mode` - run communication acting as a master
b) :ref:`i2c-api-slave-mode` - get slave responding to messages from the master
4. :ref:`i2c-api-interrupt-handling` - configure and service I2C interrupts.
5. :ref:`i2c-api-going-beyond-defaults` - adjust timing, pin configuration and other parameters of the I2C communication.
6. :ref:`i2c-api-error-handling` - how to recognize and handle driver configuration and communication errors.
7. :ref:`i2c-api-delete-driver`- on communication end to free resources used by the I2C driver.
The top level identification of an I2C driver is one of the two port numbers selected from :cpp:type:`i2c_port_t`. The mode of operation for a given port is provided during driver configuration by selecting either "master" or "slave" from :cpp:type:`i2c_mode_t`.
.. _i2c-api-configure-driver:
Configure Driver
^^^^^^^^^^^^^^^^
The first step to establishing I2C communication is to configure the driver. This is done by setting several parameters contained in :cpp:type:`i2c_config_t` structure:
* I2C **operation mode** - select either slave or master from :cpp:type:`i2c_opmode_t`
* Settings of the **communication pins**:
* GPIO pin numbers assigned to the SDA and SCL signals
* Whether to enable ESP32's internal pull up for respective pins
* I2C **clock speed**, if this configuration concerns the master mode
* If this configuration concerns the slave mode:
* Whether **10 bit address mode** should be enabled
* The **slave address**
Then, to initialize configuration for a given I2C port, call function :cpp:func:`i2c_param_config` with the port number and :cpp:type:`i2c_config_t` structure as the function call parameters.
At this stage :cpp:func:`i2c_param_config` also sets "behind the scenes" couple of other I2C configuration parameters to commonly used default values. To check what are the values and how to change them, see :ref:`i2c-api-going-beyond-defaults`.
.. _i2c-api-install-driver:
Install Driver
^^^^^^^^^^^^^^
Having the configuration initialized, the next step is to install the I2C driver by calling :cpp:func:`i2c_driver_install`. This function call requires the following parameters:
* The port number, one of the two ports available, selected from :cpp:type:`i2c_port_t`
* The operation mode, slave or master selected from :cpp:type:`i2c_opmode_t`
* Sizes of buffers that will be allocated for sending and receiving data **in the slave mode**
* Flags used to allocate the interrupt
.. _i2c-api-run-communication:
Run Communication
^^^^^^^^^^^^^^^^^
With the I2C driver installed, ESP32 is ready to communicate with other I2C devices. Programming of communication depends on whether selected I2C port operates in a master or a slave mode.
.. _i2c-api-master-mode:
Master Mode
"""""""""""
ESP32's I2C port working in the master made is responsible for establishing communication with slave I2C devices and sending commands to trigger actions by slaves, like doing a measurement and sending back a result.
To organize this process the driver provides a container, called a "command link", that should be populated with a sequence of commands and then passed to the I2C controller for execution.
**Master Write**
An example of building a commend link for I2C master sending n bytes to slave is shown below:
.. blockdiag::
:scale: 75
:caption: I2C command link - master write example
:align: center
blockdiag i2c-command-link-master-write {
# global properties
span_width = 5;
span_height = 5;
node_height = 25;
default_group_color = lightgrey;
class spacer [shape=none, width=10];
class cmdlink [colwidth=2, width=180];
class cjoint [shape=none, width=40];
# all the rows
0 -- a0 -- f0 [style=none];
1 -- a1 -- b1 -- c1 -- d1 -- e1 -- f1 -- g1 -- h1 [style=none];
2 -- a2 -- b2 -- c2 -- d2 -- e2 -- f2 -- g2 [style=none];
3 -- a3 -- d3 -- f3 [style=none];
4 -- a4 [style=none];
5 -- a5 [style=none];
6 -- a6 -- c6 [style=none];
7 -- a7 -- c7 -- d7 [style=none];
8 -- a8 -- c8 -- f8 [style=none];
9 -- a9 -- c9 -- h9 [style=none];
10 -- a10 [style=none];
11 -- a11 [style=none];
# separator row
3, a3, d3, f3 [shape=none, height=5];
# tuning node properties and connections
0 [class=spacer]; a0 [shape=none, colwidth=5]; f0 [shape=note, colwidth=2];
1 [class=spacer]; a1 [shape=none]; b1; c1 [width=40]; e1 [shape=none, width=30]; g1 [shape=none, width=30]; h1 [width=40];
2 [class=spacer]; a2 [shape=none]; b2; c2 [class=cjoint]; d2 [shape=none]; e2 [width=30]; f2 [shape=none]; g2 [width=30];
3 [class=spacer]; a3 [shape=none, colwidth=3]; d3 [colwidth=2]; f3 [colwidth=2];
4 [class=spacer]; a4 [class=cmdlink]
5 [class=spacer]; a5 [class=cmdlink];
6 [class=spacer]; a6 [class=cmdlink]; c6 [class=cjoint]; a6 -- c6 [style=solid]; c6 -- c2 -> c1 [folded];
7 [class=spacer]; a7 [class=cmdlink]; c7 [class=cjoint]; d7 [shape=none, colwidth=2]; a7 -- c7 -- d7 [style=solid]; d7 -> d3 [folded];
8 [class=spacer]; a8 [class=cmdlink]; c8 [class=cjoint, colwidth=3]; f8 [shape=none, colwidth=2]; a8 -- c8 -- f8 [style=solid]; f8 -> f3 [folded];
9 [class=spacer]; a9 [class=cmdlink]; c9 [class=cjoint, colwidth=5]; h9 [shape=none, width=40]; a9 -- c9 -- h9 [style=solid]; h9 -> h1 [folded];
10 [class=spacer]; a10 [class=cmdlink];
11 [class=spacer]; a11 [class=cmdlink];
# labels
f0 [label="Data n times", shape=note, color=yellow];
b1 [label=Master, shape=note, color=lightyellow]; c1 [label=START]; d1 [label="Slave Address"]; f1 [label=Data]; h1 [label=STOP];
b2 [label=Slave, shape=note, color=lightyellow]; e2 [label=ACK]; g2 [label=ACK];
a4 [shape=note, label=Commands, color=yellow];
a5 [label="cmd = i2c_cmd_link_create()", numbered = 1];
a6 [label="i2c_master_start(cmd)", numbered = 2];
a7 [label="i2c_master_write_byte(cmd, Address, ACK)", numbered = 3];
a8 [label="i2c_master_write(Data, n, ACK)", numbered = 4];
a9 [label="i2c_master_stop(cmd)", numbered = 5];
a10 [label="i2c_master_cmd_begin(I2c_port, cmd, wait)", numbered = 6];
a11 [label="i2c_cmd_link_delete(cmd)", numbered = 7];
# Slave Address
group { d1; e1; }
group { d2; e2; d3; }
# Data x n times
group { f1; g1;}
group { f2; g2; f3; }
}
The following describes how the command link for a "master write" is set up and what comes inside:
1. The first step is to create a command link with :cpp:func:`i2c_cmd_link_create`.
Then the command link is populated with series of data to be sent to the slave:
2. **Start bit** - :cpp:func:`i2c_master_start`
3. Single byte **slave address** - :cpp:func:`i2c_master_write_byte`. The address is provided as an argument of this function call.
4. One or more bytes of **data** as an argument of :cpp:func:`i2c_master_write`.
5. **Stop bit** - :cpp:func:`i2c_master_stop`
Both :cpp:func:`i2c_master_write_byte` and :cpp:func:`i2c_master_write` commands have additional argument defining whether slave should **acknowledge** received data or not.
6. Execution of command link by I2C controller is triggered by calling :cpp:func:`i2c_master_cmd_begin`.
7. As the last step, after sending of the commands is finished, the resources used by the command link are released by calling :cpp:func:`i2c_cmd_link_delete`.
**Master Read**
There is a similar sequence of steps for the master to read the data from a slave.
.. blockdiag::
:scale: 100
:caption: I2C command link - master read example
:align: center
blockdiag i2c-command-link-master-read {
# global properties
span_width = 5;
span_height = 5;
node_height = 25;
default_group_color = lightgrey;
class spacer [shape=none, width=10];
class cmdlink [colwidth=2, width=180];
class cjoint [shape=none, width=40];
# all the rows
0 -- a0 -- f0 [style=none];
1 -- a1 -- b1 -- c1 -- d1 -- e1 -- f1 -- g1 -- h1 -- i1 -- j1 [style=none];
2 -- a2 -- b2 -- c2 -- d2 -- e2 -- f2 -- g2 -- h2 -- i2 [style=none];
3 -- a3 -- d3 -- f3 -- h3 [style=none];
4 -- a4 [style=none];
5 -- a5 [style=none];
6 -- a6 -- c6 [style=none];
7 -- a7 -- c7 -- d7 [style=none];
8 -- a8 -- c8 -- f8 [style=none];
9 -- a9 -- c9 -- h9 [style=none];
10 -- a10 -- c10 -- j10 [style=none];
11 -- a11 [style=none];
12 -- a12 [style=none];
# separator row
3, a3, d3, f3, h3 [shape=none, height=5];
# tuning node properties and connections
0 [class=spacer]; a0 [shape=none, colwidth=5]; f0 [shape=note, colwidth=2];
1 [class=spacer]; a1 [shape=none]; b1; c1 [width=40]; e1 [shape=none, width=30]; f1 [shape=none]; g1 [width=30]; h1 [shape=none]; i1 [width=30]; j1 [width=40];
2 [class=spacer]; a2 [shape=none]; b2; c2 [class=cjoint]; d2 [shape=none]; e2 [width=30]; g2 [shape=none, width=30]; i2 [shape=none, width=30];
3 [class=spacer]; a3 [shape=none, colwidth=3]; d3 [colwidth=2]; f3 [colwidth=2]; h3 [colwidth=2];
4 [class=spacer]; a4 [class=cmdlink]
5 [class=spacer]; a5 [class=cmdlink];
6 [class=spacer]; a6 [class=cmdlink]; c6 [class=cjoint]; a6 -- c6 [style=solid]; c6 -- c2 -> c1 [folded];
7 [class=spacer]; a7 [class=cmdlink]; c7 [class=cjoint]; d7 [shape=none, colwidth=2]; a7 -- c7 -- d7 [style=solid]; d7 -> d3 [folded];
8 [class=spacer]; a8 [class=cmdlink]; c8 [class=cjoint, colwidth=3]; f8 [shape=none, colwidth=2]; a8 -- c8 -- f8 [style=solid]; f8 -> f3 [folded];
9 [class=spacer]; a9 [class=cmdlink]; c9 [class=cjoint, colwidth=5]; h9 [shape=none, colwidth=2]; a9 -- c9 -- h9 [style=solid]; h9 -> h3 [folded];
10 [class=spacer]; a10 [class=cmdlink]; c10 [class=cjoint, colwidth=7]; j10 [shape=none, width=40]; a10 -- c10 -- j10 [style=solid]; j10 -> j1 [folded];
11 [class=spacer]; a11 [class=cmdlink];
12 [class=spacer]; a12 [class=cmdlink];
# labels
f0 [label="Data (n-1) times", shape=note, color=yellow];
b1 [label=Master, shape=note, color=lightyellow]; c1 [label=START]; d1 [label="Slave Address"]; g1 [label=ACK]; i1 [label=NAK]; j1 [label=STOP];
b2 [label=Slave, shape=note, color=lightyellow]; e2 [label=ACK]; f2 [label=Data]; h2 [label=Data];
a4 [shape=note, label=Commands, color=yellow];
a5 [label="cmd = i2c_cmd_link_create()", numbered = 1];
a6 [label="i2c_master_start(cmd)", numbered = 2];
a7 [label="i2c_master_write_byte(cmd, Address, ACK)", numbered = 3];
a8 [label="i2c_master_read(Data, n-1, ACK)", numbered = 4];
a9 [label="i2c_master_read(Data, 1, NAK)", numbered = 5];
a10 [label="i2c_master_stop(cmd)", numbered = 6];
a11 [label="i2c_master_cmd_begin(I2c_port, cmd, wait)", numbered = 7];
a12 [label="i2c_cmd_link_delete(cmd)", numbered = 8];
# Slave Address
group { d1; e1; }
group { d2; e2; d3; }
# Data x (n - 1) times
group { f1; g1;}
group { f2; g2; f3; }
# Data
group { h1; i1; }
group { h2; i2; h3; }
}
When reading the data, instead of "i2c_master_read...", the command link is populated with :cpp:func:`i2c_master_read_byte` and / or :cpp:func:`i2c_master_read`. Also, the last read is configured for not providing an acknowledge by the master.
**Master Write or Read?**
After sending a slave's address, see step 3 on pictures above, the master either writes to or reads from the slave. The information what the master will actually do is hidden in the least significant bit of the slave's address.
Therefore the command link instructing the slave that the master will write the data contains the address like ``(ESP_SLAVE_ADDR << 1) | I2C_MASTER_WRITE`` and looks as follows::
i2c_master_write_byte(cmd, (ESP_SLAVE_ADDR << 1) | I2C_MASTER_WRITE, ACK_CHECK_EN)
By similar token the command link to read from the slave looks as follows::
i2c_master_write_byte(cmd, (ESP_SLAVE_ADDR << 1) | I2C_MASTER_READ, ACK_CHECK_EN)
.. _i2c-api-slave-mode:
Slave Mode
""""""""""
The API provides functions to read and write data by the slave - * :cpp:func:`i2c_slave_read_buffer` and :cpp:func:`i2c_slave_write_buffer`. An example of using these functions is provided in :example:`peripherals/i2c`.
.. _i2c-api-interrupt-handling:
Interrupt Handling
^^^^^^^^^^^^^^^^^^
To register an interrupt handler, call function :cpp:func:`i2c_isr_register`, to delete the handler call :cpp:func:`i2c_isr_free`. Description of interrupts triggered by I2C controller is provided in the `ESP32 Technical Reference Manual (PDF) <https://espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf>`_.
.. _i2c-api-going-beyond-defaults:
Going Beyond Defaults
^^^^^^^^^^^^^^^^^^^^^
There are couple of I2C communication parameters setup during driver configuration (when calling :cpp:func:`i2c_param_config`, see :ref:`i2c-api-configure-driver`), to some default commonly used values. Some parameters are also already configured in registers of the I2C controller. These parameters can be changed to user defined values by calling dedicated functions:
* Period of SCL pulses being high and low - :cpp:func:`i2c_set_period`
* SCL and SDA signal timing used during generation of start / stop signals - :cpp:func:`i2c_set_start_timing` / :cpp:func:`i2c_set_stop_timing`
* Timing relationship between SCL and SDA signals when sampling by slave, as well as when transmitting by master - :cpp:func:`i2c_set_data_timing`
* I2C timeout - :cpp:func:`i2c_set_timeout`
.. note::
The timing values are defined in APB clock cycles. The frequency of APB is specified in :cpp:type:`I2C_APB_CLK_FREQ`.
* What bit, LSB or MSB, is transmitted / received first - :cpp:func:`i2c_set_data_mode` selectable out of modes defined in :cpp:type:`i2c_trans_mode_t`
Each one of the above functions has a *_get_* counterpart to check the currently set value.
To see the default values of parameters setup during driver configuration, please refer to file :component_file:`driver/i2c.c` looking up defines with ``_DEFAULT`` suffix.
With function :cpp:func:`i2c_set_pin` it is also possible to select different SDA and SCL pins and alter configuration of pull ups, changing what has been already entered with :cpp:func:`i2c_param_config`.
.. note::
ESP32's internal pull ups are in the range of some tens of kOhm, and as such in most cases insufficient for use as I2C pull ups by themselves. We suggest to add external pull ups as well, with values as described in the I2C standard.
.. _i2c-api-error-handling:
Error Handling
^^^^^^^^^^^^^^
Most of driver's function return the ``ESP_OK`` on successful completion or a specific error code on a failure. It is a good practice to always check the returned values and implement the error handling. The driver is also printing out log messages, when e.g. checking the correctness of entered configuration, that contain explanation of errors. For details please refer to file :component_file:`driver/i2c.c` looking up defines with ``_ERR_STR`` suffix.
Use dedicated interrupts to capture communication failures. For instance there is ``I2C_TIME_OUT_INT`` interrupt triggered when I2C takes too long to receive data. See :ref:`i2c-api-interrupt-handling` for related information.
To reset internal hardware buffers in case of communication failure, you can use :cpp:func:`i2c_reset_tx_fifo` and :cpp:func:`i2c_reset_rx_fifo`.
.. _i2c-api-delete-driver:
Delete Driver
^^^^^^^^^^^^^
If the I2C communication is established with :cpp:func:`i2c_driver_install` for some specific period of time and then not required, the driver may be removed to free allocated resources by calling :cpp:func:`i2c_driver_delete`.
Application Example
-------------------
I2C master and slave example: :example:`peripherals/i2c`.
API Reference
-------------
.. include:: /_build/inc/i2c.inc
+98
View File
@@ -0,0 +1,98 @@
I2S
===
Overview
--------
ESP32 contains two I2S peripherals. These peripherals can be configured to input and output sample data via the I2S driver.
The I2S peripheral supports DMA meaning it can stream sample data without requiring each sample to be read or written by the CPU.
I2S output can also be routed directly to the Digital/Analog Converter output channels (GPIO 25 & GPIO 26) to produce analog output directly, rather than via an external I2S codec.
.. note:: For high accuracy clock applications, APLL clock source can be used with `.use_apll = true` and ESP32 will automatically calculate APLL parameter.
.. note:: If `use_apll = true` and `fixed_mclk > 0`, then the Master clock output for I2S is fixed and equal to the fixed_mclk value. The audio clock rate (LRCK) is always the MCLK divisor and 0 < MCLK/LRCK/channels/bits_per_sample < 64
Application Example
-------------------
A full I2S example is available in esp-idf: :example:`peripherals/i2s`.
Short example of I2S configuration:
.. highlight:: c
::
#include "driver/i2s.h"
#include "freertos/queue.h"
static const int i2s_num = 0; // i2s port number
static const i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_TX,
.sample_rate = 44100,
.bits_per_sample = 16,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
.communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB,
.intr_alloc_flags = 0, // default interrupt priority
.dma_buf_count = 8,
.dma_buf_len = 64,
.use_apll = false
};
static const i2s_pin_config_t pin_config = {
.bck_io_num = 26,
.ws_io_num = 25,
.data_out_num = 22,
.data_in_num = I2S_PIN_NO_CHANGE
};
...
i2s_driver_install(i2s_num, &i2s_config, 0, NULL); //install and start i2s driver
i2s_set_pin(i2s_num, &pin_config);
i2s_set_sample_rates(i2s_num, 22050); //set sample rates
i2s_driver_uninstall(i2s_num); //stop & destroy i2s driver
Short example configuring I2S to use internal DAC for analog output::
#include "driver/i2s.h"
#include "freertos/queue.h"
static const int i2s_num = 0; // i2s port number
static const i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN,
.sample_rate = 44100,
.bits_per_sample = 16, /* the DAC module will only take the 8bits from MSB */
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
.communication_format = I2S_COMM_FORMAT_I2S_MSB,
.intr_alloc_flags = 0, // default interrupt priority
.dma_buf_count = 8,
.dma_buf_len = 64,
.use_apll = false
};
...
i2s_driver_install(i2s_num, &i2s_config, 0, NULL); //install and start i2s driver
i2s_set_pin(i2s_num, NULL); //for internal DAC, this will enable both of the internal channels
//You can call i2s_set_dac_mode to set built-in DAC output mode.
//i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
i2s_set_sample_rates(i2s_num, 22050); //set sample rates
i2s_driver_uninstall(i2s_num); //stop & destroy i2s driver
API Reference
-------------
.. include:: /_build/inc/i2s.inc
@@ -0,0 +1,24 @@
Peripherals API
***************
.. toctree::
:maxdepth: 1
ADC <adc>
DAC <dac>
GPIO (including RTC low power I/O) <gpio>
I2C <i2c>
I2S <i2s>
LED Control <ledc>
MCPWM <mcpwm>
Pulse Counter <pcnt>
Remote Control <rmt>
SD/MMC Card Host <../storage/sdmmc>
Sigma-delta Modulation <sigmadelta>
SPI Master <spi_master>
SPI Slave <spi_slave>
Timer <timer>
Touch Sensor <touch_pad>
UART <uart>
Example code for this API section is provided in :example:`peripherals` directory of ESP-IDF examples.
+175
View File
@@ -0,0 +1,175 @@
LED Control
===========
Introduction
------------
The LED control (LEDC) module is primarily designed to control the intensity of LEDs, although it can be used to generate PWM signals for other purposes as well. It has 16 channels which can generate independent waveforms, that can be used to drive e.g. RGB LED devices.
Half of all LEDC's channels provide high speed mode of operation. This mode offers implemented in hardware, automatic and glitch free change of PWM duty cycle. The other half of channels operate in a low speed mode, where the moment of change depends on the application software. Each group of channels is also able to use different clock sources but this feature is not implemented in the API.
The PWM controller also has the ability to automatically increase or decrease the duty cycle gradually, allowing for fades without any processor interference.
Functionality Overview
----------------------
Getting LEDC to work on specific channel in either :ref:`high or low speed mode <ledc-api-high_low_speed_mode>` is done in three steps:
1. :ref:`ledc-api-configure-timer` to determine PWM signal's frequency and the a number (resolution of duty range).
2. :ref:`ledc-api-configure-channel` by associating it with the timer and GPIO to output the PWM signal.
3. :ref:`ledc-api-change-pwm-signal` that drives the output to change LED's intensity. This may be done under full control by software or with help of hardware fading functions.
In an optional step it is also possible to set up an interrupt on the fade end.
.. figure:: ../../_static/ledc-api-settings.jpg
:align: center
:alt: Key Settings of LED PWM Controller's API
:figclass: align-center
Key Settings of LED PWM Controller's API
.. _ledc-api-configure-timer:
Configure Timer
^^^^^^^^^^^^^^^
Setting of the timer is done by calling function :cpp:func:`ledc_timer_config`. This function should be provided with a data structure :cpp:type:`ledc_timer_config_t` that contains the following configuration settings:
* The timer number :cpp:type:`ledc_timer_t` and a speed mode :cpp:type:`ledc_mode_t`.
* The PWM signal's frequency and resolution of PWM's duty value changes.
The frequency and the duty resolution are interdependent. The higher the PWM frequency, the lower duty resolution is available and vice versa. This relationship may became important, if you are planning to use this API for purposes other that changing intensity of LEDs. Check section :ref:`ledc-api-supported-range-frequency-duty-resolution` for more details.
.. _ledc-api-configure-channel:
Configure Channel
^^^^^^^^^^^^^^^^^
Having set up the timer, the next step is to configure selected channel (one out of :cpp:type:`ledc_channel_t`). This is done by calling function :cpp:func:`ledc_channel_config`.
In similar way, like with the timer configuration, the channel setup function should be provided with specific structure :cpp:type:`ledc_channel_config_t`, that contains channel's configuration parameters.
At this point channel should became operational and start generating PWM signal of frequency determined by the timer settings and the duty on selected GPIO, as configured in :cpp:type:`ledc_channel_config_t`. The channel operation / the signal generation may be suspended at any time by calling function :cpp:func:`ledc_stop`.
.. _ledc-api-change-pwm-signal:
Change PWM Signal
^^^^^^^^^^^^^^^^^
Once the channel is operational and generating the PWM signal of constant duty and frequency, there are couple of ways to change this signal. When driving LEDs we are changing primarily the duty to vary the light intensity. See the two section below how to change the duty by software or with hardware fading. If required, we can change signal's frequency as well and this is covered in section :ref:`ledc-api-change-pwm-frequency`.
Change PWM Duty by Software
"""""""""""""""""""""""""""
Setting of the duty is done by first calling dedicated function :cpp:func:`ledc_set_duty` and then calling :cpp:func:`ledc_update_duty` to make the change effective. To check the value currently set, there is a corresponding ``_get_`` function :cpp:func:`ledc_get_duty`.
Another way to set the duty, and some other channel parameters as well, is by calling :cpp:func:`ledc_channel_config` discussed in the previous section.
The range of the duty value entered into functions depends on selected ``duty_resolution`` and should be from 0 to (2 ** duty_resolution) - 1. For example, if selected duty resolution is 10, then the duty range is from 0 to 1023. This provides the resolution of ~0.1%.
Change PWM Duty with Hardware Fading
""""""""""""""""""""""""""""""""""""
The LEDC hardware provides the means to gradually fade from one duty value to another. To use this functionality first enable fading with :cpp:func:`ledc_fade_func_install`. Then configure it by calling one of available fading functions:
* :cpp:func:`ledc_set_fade_with_time`
* :cpp:func:`ledc_set_fade_with_step`
* :cpp:func:`ledc_set_fade`
Finally start fading with :cpp:func:`ledc_fade_start`.
If not required anymore, fading and associated interrupt may be disabled with :cpp:func:`ledc_fade_func_uninstall`.
.. _ledc-api-change-pwm-frequency:
Change PWM Frequency
""""""""""""""""""""
The LEDC API provides several means to change the PWM frequency "on the fly".
* One of options is to call :cpp:func:`ledc_set_freq`. There is a corresponding function :cpp:func:`ledc_get_freq` to check what frequency is currently set.
* Another option to change the frequency, and the duty resolution as well, is by calling :cpp:func:`ledc_bind_channel_timer` to bind other timer to the channel.
* Finally the channel's timer may be changed by calling :cpp:func:`ledc_channel_config`.
More Control Over PWM
"""""""""""""""""""""
There are couple of lower level timer specific functions, that may be used to provide additional means to change the PWM settings:
* :cpp:func:`ledc_timer_set`
* :cpp:func:`ledc_timer_rst`
* :cpp:func:`ledc_timer_pause`
* :cpp:func:`ledc_timer_resume`
The first two functions are called "behind the scenes" by :cpp:func:`ledc_channel_config` to provide "clean" start up of a timer after is it configured.
Use Interrupts
^^^^^^^^^^^^^^
When configuring a LEDC channel, one of parameters selected within :cpp:type:`ledc_channel_config_t` is :cpp:type:`ledc_intr_type_t` and allows to enable an interrupt on fade completion.
Registration of a handler to service this interrupt is done by calling :cpp:func:`ledc_isr_register`.
.. _ledc-api-high_low_speed_mode:
LEDC High and Low Speed Mode
----------------------------
Out of the total 8 timers and 16 channels available in the LED PWM Controller, half of them are dedicated to operate in the high speed mode and the other half in the low speed mode. Selection of the low or high speed "capable" timer or the channel is done with parameter :cpp:type:`ledc_mode_t` that is present in applicable function calls.
The advantage of the high speed mode is h/w supported, glitch-free changeover of the timer settings. This means that if the timer settings are modified, the changes will be applied automatically after the next overflow interrupt of the timer. In contrast, when updating the low-speed timer, the change of settings should be specifically triggered by software. The LEDC API is doing it "behind the scenes", e.g. when :cpp:func:`ledc_timer_config` or :cpp:func:`ledc_timer_set` is called.
For additional details regarding speed modes please refer to `ESP32 Technical Reference Manual <https://espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf>`_ (PDF). Note that support for ``SLOW_CLOCK`` mentioned in this manual is not implemented in the LEDC API.
.. _ledc-api-supported-range-frequency-duty-resolution:
Supported Range of Frequency and Duty Resolution
------------------------------------------------
The LED PWM Controller is designed primarily to drive LEDs and provides wide resolution of PWM duty settings. For instance for the PWM frequency at 5 kHz, the maximum duty resolution is 13 bits. It means that the duty may be set anywhere from 0 to 100% with resolution of ~0.012% (13 ** 2 = 8192 discrete levels of the LED intensity).
The LEDC may be used for providing signals at much higher frequencies to clock other devices, e.g. a digital camera module. In such a case the maximum available frequency is 40 MHz with duty resolution of 1 bit. This means that duty is fixed at 50% and cannot be adjusted.
The API is designed to report an error when trying to set a frequency and a duty resolution that is out of the range of LEDC's hardware. For example, an attempt to set the frequency at 20 MHz and the duty resolution of 3 bits will result in the following error reported on a serial monitor:
.. highlight:: none
::
E (196) ledc: requested frequency and duty resolution can not be achieved, try reducing freq_hz or duty_resolution. div_param=128
In such a case either the duty resolution or the frequency should be reduced. For example setting the duty resolution at 2 will resolve this issue and provide possibility to set the duty with 25% steps, i.e. at 25%, 50% or 75%.
The LEDC API will also capture and report an attempt to configure frequency / duty resolution combination that is below the supported minimum, e.g.:
::
E (196) ledc: requested frequency and duty resolution can not be achieved, try increasing freq_hz or duty_resolution. div_param=128000000
Setting of the duty resolution is normally done using :cpp:type:`ledc_timer_bit_t`. This enumeration covers the range from 10 to 15 bits. If a smaller duty resolution is required (below 10 down to 1), enter the equivalent numeric values directly.
Application Example
-------------------
The LEDC change duty cycle and fading control example: :example:`peripherals/ledc`.
API Reference
-------------
.. include:: /_build/inc/ledc.inc
@@ -0,0 +1,60 @@
MCPWM
=====
Overview
--------
ESP32 has two MCPWM units which can be used to control different motors.
Block Diagram
-------------
The block diagram of MCPWM unit is as shown.
::
__________________________________________________________________________
| SYNCSIG FAULT SIG CAPTURE SIG |
| 0 1 2 0 1 2 0 1 2 |
|___________________________________________________________________ G |
INTERRUPTS<-----+ | | | | | | | | | | P |
| | | | | | | | | | | I |
________|_|___|___|_____________|___|___|_________|___|___|_________ | O |
| | | | | | | | | | | | |
| | | | | | | | | | | | M |
| | | | __v___v___v__ __v___v___v__ | | A |
| | | | | | | | | | T |
| | | | | FAULT | | CAPTURE | | | R |
| | | | | HANDLER | | | | | I |
| | | | | | |___________| | | X |
| | | | |___________| | | |
| | | | | | |
| ____v___v___v____ ____________________ | | |
| | +---------+ | | +------------+ |--------->|PWM0A|
| | | Timer 0 | | | | Operator 0 | | | | |
| | +---------+ | | +------------+ |--------->|PWM0B|
| | | | | | | |
| | +---------+ | | +------------+ |--------->|PWM1A|
| | | Timer 1 | |------------------->| | Operator 1 | | | | |
| | +---------+ | | +------------+ |--------->|PWM1B|
| | | | | | | |
| | +---------+ | | +------------+ |--------->|PWM2A|
| | | Timer 2 | | | | Operator 2 | | | | |
| | +---------+ | | +------------+ |--------->|PWM2B|
| |_______________| |__________________| | |_____|
| |
| MCPWM-UNIT 0/1 |
|___________________________________________________________________|
Application Example
-------------------
Examples of using MCPWM for motor control: :example:`peripherals/mcpwm`.
API Reference
-------------
.. include:: /_build/inc/mcpwm.inc
@@ -0,0 +1,96 @@
Pulse Counter
=============
Introduction
------------
The PCNT (Pulse Counter) module is designed to count the number of rising and/or falling edges of an input signal. Each pulse counter unit has a 16-bit signed counter register and two channels that can be configured to either increment or decrement the counter. Each channel has a signal input that accepts signal edges to be detected, as well as a control input that can be used to enable or disable the signal input. The inputs have optional filters that can be used to discard unwanted glitches in the signal.
Functionality Overview
----------------------
Description of functionality of this API has been broken down into four sections:
* :ref:`pcnt-api-configuration` - describes counter's configuration parameters and how to setup the counter.
* :ref:`pcnt-api-operating-the-counter` - provides information on control functions to pause, measure and clear the counter.
* :ref:`pcnt-api-filtering-pulses` - describes options to filtering pulses and the counter control signals.
* :ref:`pcnt-api-using-interrupts` - presents how to trigger interrupts on specific states of the counter.
.. _pcnt-api-configuration:
Configuration
-------------
The PCNT module has eight independent counting "units" numbered from 0 to 7. In the API they are referred to using :cpp:type:`pcnt_unit_t`. Each unit has two independent channels numbered as 0 and 1 and specified with :cpp:type:`pcnt_channel_t`.
The configuration is provided separately per unit's channel using :cpp:type:`pcnt_config_t` and covers:
* The unit and the channel number this configuration refers to.
* GPIO numbers of the pulse input and the pulse gate input.
* Two pairs of parameters: :cpp:type:`pcnt_ctrl_mode_t` and :cpp:type:`pcnt_count_mode_t` to define how the counter reacts depending on the the status of control signal and how counting is done positive / negative edge of the pulses.
* Two limit values (minimum / maximum) that are used to establish watchpoints and trigger interrupts when the pulse count is meeting particular limit.
Setting up of particular channel is then done by calling a function :cpp:func:`pcnt_unit_config` with above :cpp:type:`pcnt_config_t` as the input parameter.
To disable the pulse or the control input pin in configuration, provide :cpp:type:`PCNT_PIN_NOT_USED` instead of the GPIO number.
.. _pcnt-api-operating-the-counter:
Operating the Counter
---------------------
After doing setup with :cpp:func:`pcnt_unit_config`, the counter immediately starts to operate. The accumulated pulse count can be checked by calling :cpp:func:`pcnt_get_counter_value`.
There are couple of functions that allow to control the counter's operation: :cpp:func:`pcnt_counter_pause`, :cpp:func:`pcnt_counter_resume` and :cpp:func:`pcnt_counter_clear`
It is also possible to dynamically change the previously set up counter modes with :cpp:func:`pcnt_unit_config` by calling :cpp:func:`pcnt_set_mode`.
If desired, the pulse input pin and the control input pin may be changed "on the fly" using :cpp:func:`pcnt_set_pin`. To disable particular input provide as a function parameter :cpp:type:`PCNT_PIN_NOT_USED` instead of the GPIO number.
.. note::
For the counter not to miss any pulses, the pulse duration should be longer than one APB_CLK cycle (12.5 ns). The pulses are sampled on the edges of the APB_CLK clock and may be missed, if fall between the edges. This applies to counter operation with or without a :ref:`filer <pcnt-api-filtering-pulses>`.
.. _pcnt-api-filtering-pulses:
Filtering Pulses
----------------
The PCNT unit features filters on each of the pulse and control inputs, adding the option to ignore short glitches in the signals.
The length of ignored pulses is provided in APB_CLK clock cycles by calling :cpp:func:`pcnt_set_filter_value`. The current filter setting may be checked with :cpp:func:`pcnt_get_filter_value`. The APB_CLK clock is running at 80 MHz.
The filter is put into operation / suspended by calling :cpp:func:`pcnt_filter_enable` / :cpp:func:`pcnt_filter_disable`.
.. _pcnt-api-using-interrupts:
Using Interrupts
----------------
There are five counter state watch events, defined in :cpp:type:`pcnt_evt_type_t`, that are able to trigger an interrupt. The event happens on the pulse counter reaching specific values:
* Minimum or maximum count values: :cpp:member:`counter_l_lim` or :cpp:member:`counter_h_lim` provided in :cpp:type:`pcnt_config_t` as discussed in :ref:`pcnt-api-configuration`
* Threshold 0 or Threshold 1 values set using function :cpp:func:`pcnt_set_event_value`.
* Pulse count = 0
To register, enable or disable an interrupt to service the above events, call :cpp:func:`pcnt_isr_register`, :cpp:func:`pcnt_intr_enable`. and :cpp:func:`pcnt_intr_disable`. To enable or disable events on reaching threshold values, you will also need to call functions :cpp:func:`pcnt_event_enable` and :cpp:func:`pcnt_event_disable`.
In order to check what are the threshold values currently set, use function :cpp:func:`pcnt_get_event_value`.
Application Example
-------------------
Pulse counter with control signal and event interrupt example: :example:`peripherals/pcnt`.
API Reference
-------------
.. include:: /_build/inc/pcnt.inc
+267
View File
@@ -0,0 +1,267 @@
RMT
===
The RMT (Remote Control) module driver can be used to send and receive infrared remote control signals. Due to flexibility of RMT module, the driver can also be used to generate or receive many other types of signals.
The signal, which consists of a series of pulses, is generated by RMT's transmitter based on a list of values. The values define the pulse duration and a binary level, see below. The transmitter can also provide a carrier and modulate it with provided pulses.
.. blockdiag::
:scale: 100
:caption: RMT Transmitter Overview
:align: center
blockdiag rmt_tx {
node_width = 80;
node_height = 60;
default_group_color = lightgrey;
a -> b -> c -> d;
e -> f -> g -- h;
d -> o [label=GPIO];
h -> d [folded];
a [style=none, width=100, label="{11,high,7,low},\n{5,high,5,low},\n..."]
b [label="Waveform\nGenerator"]
c [style=none, label="", background="_static/rmt-waveform.png"]
d [shape=beginpoint, label="mod"]
e [style=none, width=60, height=40, label="Carrier\nenable"]
f [label="Carrier\nGenerator"]
g [style=none, label="", background="_static/rmt-carrier.png"]
h [shape=none]
o [style=none, label="", background="_static/rmt-waveform-modulated.png"]
group {
label = Input
a,e;
}
group {
label = "RMT Transmitter"
b,f,c,g,d,h;
}
group {
label = Output
o;
}
}
The reverse operation is performed by the receiver, where a series of pulses is decoded into a list of values containing the pulse duration and binary level. A filter may be applied to remove high frequency noise from the input signal.
.. blockdiag::
:scale: 90
:caption: RMT Receiver Overview
:align: center
blockdiag rmt_rx {
node_width = 80;
node_height = 60;
default_group_color = lightgrey;
a -> b [label=GPIO];
b -> c -> d;
e -- f;
f -> b [folded];
a [style=none, label="", background="_static/rmt-waveform.png"]
b [label=Filter]
c [label="Edge\nDetect"]
d [style=none, width=100, label="{11,high,7,low},\n{5,high,5,low},\n..."]
e [style=none, width=60, height=40, label="Filter\nenable"]
f [shape=none, label=""]
group {
label = Input
a,e;
}
group {
label = "RMT Receiver"
b,c;
}
group {
label = Output
d;
}
}
There couple of typical steps to setup and operate the RMT and they are discussed in the following sections:
1. `Configure Driver`_
2. `Transmit Data`_ or `Receive Data`_
3. `Change Operation Parameters`_
4. `Use Interrupts`_
The RMT has eight channels numbered from zero to seven. Each channel is able to independently transmit or receive data. They are referred to using indexes defined in structure :cpp:type:`rmt_channel_t`.
Configure Driver
----------------
There are several parameters that define how particular channel operates. Most of these parameters are configured by setting specific members of :cpp:type:`rmt_config_t` structure. Some of the parameters are common to both transmit or receive mode, and some are mode specific. They are all discussed below.
Common Parameters
^^^^^^^^^^^^^^^^^
* The **channel** to be configured, select one from the :cpp:type:`rmt_channel_t` enumerator.
* The RMT **operation mode** - whether this channel is used to transmit or receive data, selected by setting a **rmt_mode** members to one of the values from :cpp:type:`rmt_mode_t`.
* What is the **pin number** to transmit or receive RMT signals, selected by setting **gpio_num**.
* How many **memory blocks** will be used by the channel, set with **mem_block_num**.
* A **clock divider**, that will determine the range of pulse length generated by the RMT transmitter or discriminated by the receiver. Selected by setting **clk_div** to a value within [1 .. 255] range. The RMT source clock is typically APB CLK, 80Mhz by default.
.. note::
The period of a square wave after the clock divider is called a 'tick'. The length of the pulses generated by the RMT transmitter or discriminated by the receiver is configured in number of 'ticks'.
There are also couple of specific parameters that should be set up depending if selected channel is configured in `Transmit Mode`_ or `Receive Mode`_:
Transmit Mode
^^^^^^^^^^^^^
When configuring channel in transmit mode, set **tx_config** and the following members of :cpp:type:`rmt_tx_config_t`:
* Transmit the currently configured data items in a loop - **loop_en**
* Enable the RMT carrier signal - **carrier_en**
* Frequency of the carrier in Hz - **carrier_freq_hz**
* Duty cycle of the carrier signal in percent (%) - **carrier_duty_percent**
* Level of the RMT output, when the carrier is applied - **carrier_level**
* Enable the RMT output if idle - **idle_output_en**
* Set the signal level on the RMT output if idle - **idle_level**
Receive Mode
^^^^^^^^^^^^
In receive mode, set **rx_config** and the following members of :cpp:type:`rmt_rx_config_t`:
* Enable a filter on the input of the RMT receiver - **filter_en**
* A threshold of the filter, set in the number of ticks - **filter_ticks_thresh**. Pulses shorter than this setting will be filtered out. Note, that the range of entered tick values is [0..255].
* A pulse length threshold that will turn the RMT receiver idle, set in number of ticks - **idle_threshold**. The receiver will ignore pulses longer than this setting.
Finalize Configuration
^^^^^^^^^^^^^^^^^^^^^^
Once the :cpp:type:`rmt_config_t` structure is populated with parameters, it should be then invoked with :cpp:func:`rmt_config` to make the configuration effective.
The last configuration step is installation of the driver in memory by calling :cpp:func:`rmt_driver_install`. If :cpp:type:`rx_buf_size` parameter of this function is > 0, then a ring buffer for incoming data will be allocated. A default ISR handler will be installed, see a note in `Use Interrupts`_.
Now, depending on how the channel is configured, we are ready to either `Transmit Data`_ or `Receive Data`_. This is described in next two sections.
Transmit Data
-------------
Before being able to transmit some RMT pulses, we need to define the pulse pattern. The minimum pattern recognized by the RMT controller, later called an 'item', is provided in a structure :cpp:type:`rmt_item32_t`, see :component_file:`soc/esp32/include/soc/rmt_struct.h`. Each item consists of two pairs of two values. The first value in a pair describes the signal duration in ticks and is 15 bits long, the second provides the signal level (high or low) and is contained in a single bit. A block of couple of items and the structure of an item is presented below.
.. packetdiag::
:caption: Structure of RMT items (L - signal level)
:align: center
packetdiag rmt_items {
colwidth = 32
node_width = 10
node_height = 24
default_fontsize = 12
0-14: Period (15)
15: L
16-30: Period (15)
31: L
32-95: ... [colheight=2]
96-110: Period (15)
111: L
112-126: Period (15)
127: L
}
For a simple example how to define a block of items see :example:`peripherals/rmt_tx`.
The items are provided to the RMT controller by calling function :cpp:func:`rmt_write_items`. This function also automatically triggers start of transmission. It may be called to wait for transmission completion or exit just after transmission start. In such case you can wait for the transmission end by calling :cpp:func:`rmt_wait_tx_done`. This function does not limit the number of data items to transmit. It is using an interrupt to successively copy the new data chunks to RMT's internal memory as previously provided data are sent out.
Another way to provide data for transmission is by calling :cpp:func:`rmt_fill_tx_items`. In this case transmission is not started automatically. To control the transmission process use :cpp:func:`rmt_tx_start` and :cpp:func:`rmt_tx_stop`. The number of items to sent is restricted by the size of memory blocks allocated in the RMT controller's internal memory, see :cpp:func:`rmt_set_mem_block_num`.
Receive Data
------------
Before starting the receiver we need some storage for incoming items. The RMT controller has 512 x 32-bits of internal RAM shared between all eight channels. In typical scenarios it is not enough as an ultimate storage for all incoming (and outgoing) items. Therefore this API supports retrieval of incoming items on the fly to save them in a ring buffer of a size defined by the user. The size is provided when calling :cpp:func:`rmt_driver_install` discussed above. To get a handle to this buffer call :cpp:func:`rmt_get_ringbuf_handle`.
With the above steps complete we can start the receiver by calling :cpp:func:`rmt_rx_start` and then move to checking what's inside the buffer. To do so, you can use common FreeRTOS functions that interact with the ring buffer. Please see an example how to do it in :example:`peripherals/rmt_nec_tx_rx`.
To stop the receiver, call :cpp:func:`rmt_rx_stop`.
Change Operation Parameters
---------------------------
Previously described function :cpp:func:`rmt_config` provides a convenient way to set several configuration parameters in one shot. This is usually done on application start. Then, when the application is running, the API provides an alternate way to update individual parameters by calling dedicated functions. Each function refers to the specific RMT channel provided as the first input parameter. Most of the functions have `_get_` counterpart to read back the currently configured value.
Parameters Common to Transmit and Receive Mode
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Selection of a GPIO pin number on the input or output of the RMT - :cpp:func:`rmt_set_pin`
* Number of memory blocks allocated for the incoming or outgoing data - :cpp:func:`rmt_set_mem_pd`
* Setting of the clock divider - :cpp:func:`rmt_set_clk_div`
* Selection of the clock source, note that currently one clock source is supported, the APB clock which is 80Mhz - :cpp:func:`rmt_set_source_clk`
Transmit Mode Parameters
^^^^^^^^^^^^^^^^^^^^^^^^
* Enable or disable the loop back mode for the transmitter - :cpp:func:`rmt_set_tx_loop_mode`
* Binary level on the output to apply the carrier - :cpp:func:`rmt_set_tx_carrier`, selected from :cpp:type:`rmt_carrier_level_t`
* Determines the binary level on the output when transmitter is idle - :cpp:func:`rmt_set_idle_level()`, selected from :cpp:type:`rmt_idle_level_t`
Receive Mode Parameters
^^^^^^^^^^^^^^^^^^^^^^^
* The filter setting - :cpp:func:`rmt_set_rx_filter`
* The receiver threshold setting - :cpp:func:`rmt_set_rx_idle_thresh`
* Whether the transmitter or receiver is entitled to access RMT's memory - :cpp:func:`rmt_set_memory_owner`, selection is from :cpp:type:`rmt_mem_owner_t`.
Use Interrupts
--------------
Registering of an interrupt handler for the RMT controller is done be calling :cpp:func:`rmt_isr_register`.
.. note::
When calling :cpp:func:`rmt_driver_install` to use the system RMT driver, a default ISR is being installed. In such a case you cannot register a generic ISR handler with :cpp:func:`rmt_isr_register`.
The RMT controller triggers interrupts on four specific events describes below. To enable interrupts on these events, the following functions are provided:
* The RMT receiver has finished receiving a signal - :cpp:func:`rmt_set_rx_intr_en`
* The RMT transmitter has finished transmitting the signal - :cpp:func:`rmt_set_tx_intr_en`
* The number of events the transmitter has sent matches a threshold value :cpp:func:`rmt_set_tx_thr_intr_en`
* Ownership to the RMT memory block has been violated - :cpp:func:`rmt_set_err_intr_en`
Setting or clearing an interrupt enable mask for specific channels and events may be also done by calling :cpp:func:`rmt_set_intr_enable_mask` or :cpp:func:`rmt_clr_intr_enable_mask`.
When servicing an interrupt within an ISR, the interrupt need to explicitly cleared. To do so, set specific bits described as ``RMT.int_clr.val.chN_event_name`` and defined as a ``volatile struct`` in :component_file:`soc/esp32/include/soc/rmt_struct.h`, where N is the RMT channel number [0, 7] and the ``event_name`` is one of four events described above.
If you do not need an ISR anymore, you can deregister it by calling a function :cpp:func:`rmt_isr_deregister`.
Uninstall Driver
----------------
If the RMT driver has been installed with :cpp:func:`rmt_driver_install` for some specific period of time and then not required, the driver may be removed to free allocated resources by calling :cpp:func:`rmt_driver_uninstall`.
Application Examples
--------------------
* A simple RMT TX example: :example:`peripherals/rmt_tx`.
* NEC remote control TX and RX example: :example:`peripherals/rmt_nec_tx_rx`.
API Reference
-------------
.. include:: /_build/inc/rmt.inc
@@ -0,0 +1,32 @@
Sigma-delta Modulation
======================
Introduction
------------
ESP32 has a second-order sigma-delta modulation module. This driver configures the channels of the sigma-delta module.
Functionality Overview
----------------------
There are eight independent sigma-delta modulation channels identified with :cpp:type:`sigmadelta_channel_t`. Each channel is capable to output the binary, hardware generated signal with the sigma-delta modulation.
Selected channel should be set up by providing configuration parameters in :cpp:type:`sigmadelta_config_t` and then applying this configuration with :cpp:func:`sigmadelta_config`.
Another option is to call individual functions, that will configure all required parameters one by one:
* **Prescaler** of the sigma-delta generator - :cpp:func:`sigmadelta_set_prescale`
* **Duty** of the output signal - :cpp:func:`sigmadelta_set_duty`
* **GPIO pin** to output modulated signal - :cpp:func:`sigmadelta_set_pin`
The range of the 'duty' input parameter of :cpp:func:`sigmadelta_set_duty` is from -128 to 127 (eight bit signed integer). If zero value is set, then the output signal's duty will be about 50%, see description of :cpp:func:`sigmadelta_set_duty`.
Application Example
-------------------
Sigma-delta Modulation example: :example:`peripherals/sigmadelta`.
API Reference
-------------
.. include:: /_build/inc/sigmadelta.inc
@@ -0,0 +1,159 @@
SPI Master driver
=================
Overview
--------
The ESP32 has four SPI peripheral devices, called SPI0, SPI1, HSPI and VSPI. SPI0 is entirely dedicated to
the flash cache the ESP32 uses to map the SPI flash device it is connected to into memory. SPI1 is
connected to the same hardware lines as SPI0 and is used to write to the flash chip. HSPI and VSPI
are free to use. SPI1, HSPI and VSPI all have three chip select lines, allowing them to drive up to
three SPI devices each as a master.
The spi_master driver
^^^^^^^^^^^^^^^^^^^^^
The spi_master driver allows easy communicating with SPI slave devices, even in a multithreaded environment.
It fully transparently handles DMA transfers to read and write data and automatically takes care of
multiplexing between different SPI slaves on the same master
Terminology
^^^^^^^^^^^
The spi_master driver uses the following terms:
* Host: The SPI peripheral inside the ESP32 initiating the SPI transmissions. One of SPI, HSPI or VSPI. (For
now, only HSPI or VSPI are actually supported in the driver; it will support all 3 peripherals
somewhere in the future.)
* Bus: The SPI bus, common to all SPI devices connected to one host. In general the bus consists of the
miso, mosi, sclk and optionally quadwp and quadhd signals. The SPI slaves are connected to these
signals in parallel.
- miso - Also known as q, this is the input of the serial stream into the ESP32
- mosi - Also known as d, this is the output of the serial stream from the ESP32
- sclk - Clock signal. Each data bit is clocked out or in on the positive or negative edge of this signal
- quadwp - Write Protect signal. Only used for 4-bit (qio/qout) transactions.
- quadhd - Hold signal. Only used for 4-bit (qio/qout) transactions.
* Device: A SPI slave. Each SPI slave has its own chip select (CS) line, which is made active when
a transmission to/from the SPI slave occurs.
* Transaction: One instance of CS going active, data transfer from and/or to a device happening, and
CS going inactive again. Transactions are atomic, as in they will never be interrupted by another
transaction.
SPI transactions
^^^^^^^^^^^^^^^^
A transaction on the SPI bus consists of five phases, any of which may be skipped:
* The command phase. In this phase, a command (0-16 bit) is clocked out.
* The address phase. In this phase, an address (0-64 bit) is clocked out.
* The write phase. The master sends data to the slave.
* The dummy phase. The phase is configurable, used to meet the timing requirements.
* The read phase. The slave sends data to the master.
In full duplex, the read and write phases are combined, causing the SPI host to read and
write data simultaneously. The total transaction length is decided by
``command_bits + address_bits + trans_conf.length``, while the ``trans_conf.rx_length``
only determins length of data received into the buffer.
In half duplex, the length of write phase and read phase are decided by ``trans_conf.length`` and
``trans_conf.rx_length`` respectively. ** Note that a half duplex transaction with both a read and
write phase is not supported when using DMA. ** If such transaction is needed, you have to use one
of the alternative solutions:
1. use full-duplex mode instead.
2. disable the DMA by set the last parameter to 0 in bus initialization function just as belows:
``ret=spi_bus_initialize(VSPI_HOST, &buscfg, 0);``
this may prohibit you from transmitting and receiving data longer than 32 bytes.
3. try to use command and address field to replace the write phase.
The command and address phase are optional in that not every SPI device will need to be sent a command
and/or address. This is reflected in the device configuration: when the ``command_bits`` or ``address_bits``
fields are set to zero, no command or address phase is done.
Something similar is true for the read and write phase: not every transaction needs both data to be written
as well as data to be read. When ``rx_buffer`` is NULL (and SPI_USE_RXDATA) is not set) the read phase
is skipped. When ``tx_buffer`` is NULL (and SPI_USE_TXDATA) is not set) the write phase is skipped.
Using the spi_master driver
^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Initialize a SPI bus by calling ``spi_bus_initialize``. Make sure to set the correct IO pins in
the ``bus_config`` struct. Take care to set signals that are not needed to -1.
- Tell the driver about a SPI slave device connected to the bus by calling spi_bus_add_device.
Make sure to configure any timing requirements the device has in the ``dev_config`` structure.
You should now have a handle for the device, to be used when sending it a transaction.
- To interact with the device, fill one or more spi_transaction_t structure with any transaction
parameters you need. Either queue all transactions by calling ``spi_device_queue_trans``, later
quering the result using ``spi_device_get_trans_result``, or handle all requests synchroneously
by feeding them into ``spi_device_transmit``.
- Optional: to unload the driver for a device, call ``spi_bus_remove_device`` with the device
handle as an argument
- Optional: to remove the driver for a bus, make sure no more drivers are attached and call
``spi_bus_free``.
Command and address phases
^^^^^^^^^^^^^^^^^^^^^^^^^^
During the command and address phases, ``cmd`` and ``addr`` field in the
``spi_transaction_t`` struct are sent to the bus, while nothing is read at the
same time. The default length of command and address phase are set in the
``spi_device_interface_config_t`` and by ``spi_bus_add_device``. When the the
flag ``SPI_TRANS_VARIABLE_CMD`` and ``SPI_TRANS_VARIABLE_ADDR`` are not set in
the ``spi_transaction_t``,the driver automatically set the length of these
phases to the default value as set when the device is initialized respectively.
If the length of command and address phases needs to be variable, declare a
``spi_transaction_ext_t`` descriptor, set the flag ``SPI_TRANS_VARIABLE_CMD``
or/and ``SPI_TRANS_VARIABLE_ADDR`` in the ``flags`` of ``base`` member and
configure the rest part of ``base`` as usual. Then the length of each phases
will be ``command_bits`` and ``address_bits`` set in the ``spi_transaction_ext_t``.
Write and read phases
^^^^^^^^^^^^^^^^^^^^^
Normally, data to be transferred to or from a device will be read from or written to a chunk of memory
indicated by the ``rx_buffer`` and ``tx_buffer`` members of the transaction structure.
When DMA is enabled for transfers, these buffers are highly recommended to meet the requirements as belows:
1. allocated in DMA-capable memory using ``pvPortMallocCaps(size, MALLOC_CAP_DMA)``;
2. 32-bit aligned (start from the boundary and have length of multiples of 4 bytes).
If these requirements are not satisfied, efficiency of the transaction will suffer due to the allocation and
memcpy of temporary buffers.
Sometimes, the amount of data is very small making it less than optimal allocating a separate buffer
for it. If the data to be transferred is 32 bits or less, it can be stored in the transaction struct
itself. For transmitted data, use the ``tx_data`` member for this and set the ``SPI_USE_TXDATA`` flag
on the transmission. For received data, use ``rx_data`` and set ``SPI_USE_RXDATA``. In both cases, do
not touch the ``tx_buffer`` or ``rx_buffer`` members, because they use the same memory locations
as ``tx_data`` and ``rx_data``.
Application Example
-------------------
Display graphics on the 320x240 LCD of WROVER-Kits: :example:`peripherals/spi_master`.
API Reference - SPI Common
--------------------------
.. include:: /_build/inc/spi_common.inc
API Reference - SPI Master
--------------------------
.. include:: /_build/inc/spi_master.inc
@@ -0,0 +1,98 @@
SPI Slave driver
=================
Overview
--------
The ESP32 has four SPI peripheral devices, called SPI0, SPI1, HSPI and VSPI. SPI0 is entirely dedicated to
the flash cache the ESP32 uses to map the SPI flash device it is connected to into memory. SPI1 is
connected to the same hardware lines as SPI0 and is used to write to the flash chip. HSPI and VSPI
are free to use, and with the spi_slave driver, these can be used as a SPI slave, driven from a
connected SPI master.
The spi_slave driver
^^^^^^^^^^^^^^^^^^^^^
The spi_slave driver allows using the HSPI and/or VSPI peripheral as a full-duplex SPI slave. It can make
use of DMA to send/receive transactions of arbitrary length.
Terminology
^^^^^^^^^^^
The spi_slave driver uses the following terms:
* Host: The SPI peripheral inside the ESP32 initiating the SPI transmissions. One of HSPI or VSPI.
* Bus: The SPI bus, common to all SPI devices connected to a master. In general the bus consists of the
miso, mosi, sclk and optionally quadwp and quadhd signals. The SPI slaves are connected to these
signals in parallel. Each SPI slave is also connected to one CS signal.
- miso - Also known as q, this is the output of the serial stream from the ESP32 to the SPI master
- mosi - Also known as d, this is the output of the serial stream from the SPI master to the ESP32
- sclk - Clock signal. Each data bit is clocked out or in on the positive or negative edge of this signal
- cs - Chip Select. An active Chip Select delineates a single transaction to/from a slave.
* Transaction: One instance of CS going active, data transfer from and to a master happening, and
CS going inactive again. Transactions are atomic, as in they will never be interrupted by another
transaction.
SPI transactions
^^^^^^^^^^^^^^^^
A full-duplex SPI transaction starts with the master pulling CS low. After this happens, the master
starts sending out clock pulses on the CLK line: every clock pulse causes a data bit to be shifted from
the master to the slave on the MOSI line and vice versa on the MISO line. At the end of the transaction,
the master makes CS high again.
Using the spi_slave driver
^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Initialize a SPI peripheral as a slave by calling ``spi_slave_initialize``. Make sure to set the
correct IO pins in the ``bus_config`` struct. Take care to set signals that are not needed to -1.
A DMA channel (either 1 or 2) must be given if transactions will be larger than 32 bytes, if not
the dma_chan parameter may be 0.
- To set up a transaction, fill one or more spi_transaction_t structure with any transaction
parameters you need. Either queue all transactions by calling ``spi_slave_queue_trans``, later
quering the result using ``spi_slave_get_trans_result``, or handle all requests synchroneously
by feeding them into ``spi_slave_transmit``. The latter two functions will block until the
master has initiated and finished a transaction, causing the queued data to be sent and received.
- Optional: to unload the SPI slave driver, call ``spi_slave_free``.
Transaction data and master/slave length mismatches
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Normally, data to be transferred to or from a device will be read from or written to a chunk of memory
indicated by the ``rx_buffer`` and ``tx_buffer`` members of the transaction structure. The SPI driver
may decide to use DMA for transfers, so these buffers should be allocated in DMA-capable memory using
``pvPortMallocCaps(size, MALLOC_CAP_DMA)``.
The amount of data written to the buffers is limited by the ``length`` member of the transaction structure:
the driver will never read/write more data than indicated there. The ``length`` cannot define the actual
length of the SPI transaction; this is determined by the master as it drives the clock and CS lines. The actual length
transferred can be read from the ``trans_len`` member of the ``spi_slave_transaction_t`` structure after transaction.
In case the length of the transmission is larger than the buffer length, only the start of the transmission
will be sent and received, and the ``trans_len`` is set to ``length`` instead of the actual length. It's recommended to
set ``length`` longer than the maximum length expected if the ``trans_len`` is required. In case the transmission
length is shorter than the buffer length, only data up to the length of the buffer will be exchanged.
Warning: Due to a design peculiarity in the ESP32, if the amount of bytes sent by the master or the length
of the transmission queues in the slave driver, in bytes, is not both larger than eight and dividable by
four, the SPI hardware can fail to write the last one to seven bytes to the receive buffer.
Application Example
-------------------
Slave/master communication: :example:`peripherals/spi_slave`.
API Reference
-------------
.. include:: /_build/inc/spi_slave.inc
+100
View File
@@ -0,0 +1,100 @@
TIMER
=====
Introduction
------------
The ESP32 chip contains two hardware timer groups. Each group has two general-purpose hardware timers. They are all 64-bit generic timers based on 16-bit prescalers and 64-bit auto-reload-capable up / down counters.
Functional Overview
-------------------
Typical steps to configure an operate the timer are described in the following sections:
* :ref:`timer-api-timer-initialization` - what parameters should be set up to get the timer working and what specific functionality is provided depending on the set up.
* :ref:`timer-api-timer-control` - how to read the timer's value, pause / start the timer, and change how it operates.
* :ref:`timer-api-alarms` - setting and using alarms.
* :ref:`timer-api-interrupts`- how to enable and use interrupts.
.. _timer-api-timer-initialization:
Timer Initialization
^^^^^^^^^^^^^^^^^^^^
The two timer groups on-board of the ESP32 are identified using :cpp:type:`timer_group_t`. Individual timers in a group are identified with :cpp:type:`timer_idx_t`. The two groups, each having two timers, provide the total of four individual timers to our disposal.
Before starting the timer, it should be initialized by calling :cpp:func:`timer_init`. This function should be provided with a structure :cpp:type:`timer_config_t` to define how timer should operate. In particular the following timer's parameters may be set:
* **Divider**: How quickly the timer's counter is "ticking". This depends on the setting of :cpp:member:`divider`, that will be used as divisor of the incoming 80 MHz APB_CLK clock.
* **Mode**: If the the counter is incrementing or decrementing, defined using :cpp:member:`counter_dir` by selecting one of values from :cpp:type:`timer_count_dir_t`.
* **Counter Enable**: If the counter is enabled, then it will start incrementing / decrementing immediately after calling :cpp:func:`timer_init`. This action is set using :cpp:member:`counter_en` by selecting one of vales from :cpp:type:`timer_start_t`.
* **Alarm Enable**: Determined by the setting of :cpp:member:`alarm_en`.
* **Auto Reload**: Whether the counter should :cpp:member:`auto_reload` a specific initial value on the timer's alarm, or continue incrementing or decrementing.
* **Interrupt Type**: Whether an interrupt is triggered on timer's alarm. Set the value defined in :cpp:type:`timer_intr_mode_t`.
To get the current values of the timers settings, use function :cpp:func:`timer_get_config`.
.. _timer-api-timer-control:
Timer Control
^^^^^^^^^^^^^
Once the timer is configured and enabled, it is already "ticking". To check it's current value call :cpp:func:`timer_get_counter_value` or :cpp:func:`timer_get_counter_time_sec`. To set the timer to specific starting value call :cpp:func:`timer_set_counter_value`.
The timer may be paused at any time by calling :cpp:func:`timer_pause`. To start it again call :cpp:func:`timer_start`.
To change how the timer operates you can call once more :cpp:func:`timer_init` described in section :ref:`timer-api-timer-initialization`. Another option is to use dedicated functions to change individual settings:
* **Divider** value - :cpp:func:`timer_set_divider`. **Note:** the timer should be paused when changing the divider to avoid unpredictable results. If the timer is already running, :cpp:func:`timer_set_divider` will first pause the timer, change the divider, and finally start the timer again.
* **Mode** (whether the counter incrementing or decrementing) - :cpp:func:`timer_set_counter_mode`
* **Auto Reload** counter on alarm - :cpp:func:`timer_set_auto_reload`
.. _timer-api-alarms:
Alarms
^^^^^^
To set an alarm, call function :cpp:func:`timer_set_alarm_value` and then enable it with :cpp:func:`timer_set_alarm`. The alarm may be also enabled during the timer initialization stage, when :cpp:func:`timer_init` is called.
After the alarm is enabled and the timer reaches the alarm value, depending on configuration, the following two actions may happen:
* An interrupt will be triggered, if previously configured. See section :ref:`timer-api-interrupts` how to configure interrupts.
* When :cpp:member:`auto_reload` is enabled, the timer's counter will be reloaded to start counting from specific initial value. The value to start should be set in advance with :cpp:func:`timer_set_counter_value`.
.. note::
The alarm will be triggered immediately, if an alarm value is set and the timer has already passed this value.
To check what alarm value has been set up, call :cpp:func:`timer_get_alarm_value`.
.. _timer-api-interrupts:
Interrupts
^^^^^^^^^^
Registration of the interrupt handler for a specific timer group and timer is done be calling :cpp:func:`timer_isr_register`.
To enable interrupts for a timer group call :cpp:func:`timer_group_intr_enable`. To do it for a specific timer, call :cpp:func:`timer_enable_intr`. Disabling of interrupts is done with corresponding functions :cpp:func:`timer_group_intr_disable` and :cpp:func:`timer_disable_intr`.
When servicing an interrupt within an ISR, the interrupt need to explicitly cleared. To do so, set the ``TIMERGN.int_clr_timers.tM`` structure defined in :component_file:`soc/esp32/include/soc/timer_group_struct.h`, where N is the timer group number [0, 1] and M is the timer number [0, 1]. For example to clear an interrupt for the timer 1 in the timer group 0, call the following::
TIMERG0.int_clr_timers.t1 = 1
See the application example below how to use interrupts.
Application Example
-------------------
The 64-bit hardware timer example: :example:`peripherals/timer_group`.
API Reference
-------------
.. include:: /_build/inc/timer.inc
@@ -0,0 +1,170 @@
Touch Sensor
============
Introduction
------------
A touch-sensor system is built on a substrate which carries electrodes and relevant connections under a protective flat surface. When a user touches the surface, the capacitance variation is triggered and a binary signal is generated to indicate whether the touch is valid.
ESP32 can provide up to 10 capacitive touch pads / GPIOs. The sensing pads can be arranged in different combinations (e.g. matrix, slider), so that a larger area or more points can be detected. The touch pad sensing process is under the control of a hardware-implemented finite-state machine (FSM) which is initiated by software or a dedicated hardware timer.
Design, operation and control registers of touch sensor are discussed in `ESP32 Technical Reference Manual <https://espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf>`_ (PDF). Please refer to it for additional details how this subsystem works.
Functionality Overview
----------------------
Description of API is broken down into groups of functions to provide quick overview of features like:
- Initialization of touch pad driver
- Configuration of touch pad GPIO pins
- Taking measurements
- Adjusting parameters of measurements
- Filtering measurements
- Touch detection methods
- Setting up interrupts to report touch detection
- Waking up from sleep mode on interrupt
For detailed description of particular function please go to section :ref:`touch_pad-api-reference`. Practical implementation of this API is covered in section :ref:`touch_pad-api-examples`.
Initialization
^^^^^^^^^^^^^^
Touch pad driver should be initialized before use by calling function :cpp:func:`touch_pad_init`. This function sets several ``.._DEFAULT`` driver parameters listed in :ref:`touch_pad-api-reference` under "Macros". It also clears information what pads have been touched before (if any) and disables interrupts.
If not required anymore, driver can be disabled by calling :cpp:func:`touch_pad_deinit`.
Configuration
^^^^^^^^^^^^^
Enabling of touch sensor functionality for particular GPIO is done with :cpp:func:`touch_pad_config`.
The function :cpp:func:`touch_pad_set_fsm_mode` is used to select whether touch pad measurement (operated by FSM) is started automatically by hardware timer, or by software. If software mode is selected, then use :cpp:func:`touch_pad_sw_start` to start of the FSM.
Touch State Measurements
^^^^^^^^^^^^^^^^^^^^^^^^
The following two functions come handy to read raw or filtered measurements from the sensor:
* :cpp:func:`touch_pad_read`
* :cpp:func:`touch_pad_read_filtered`
They may be used to characterize particular touch pad design by checking the range of sensor readings when a pad is touched or released. This information can be then used to establish the touch threshold.
.. note::
Start and configure filter before using :cpp:func:`touch_pad_read_filtered` by calling specific filter functions described down below.
To see how to use both read functions check :example:`peripherals/touch_pad_read` application example.
Optimization of Measurements
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Touch sensor has several configurable parameters to match characteristics of particular touch pad design. For instance, to sense smaller capacity changes, it is possible to narrow the reference voltage range within which the touch pads are charged / discharged. The high and low reference voltages are set using function :cpp:func:`touch_pad_set_voltage`. A positive side effect, besides ability to discern smaller capacity changes, will be reduction of power consumption for low power applications. A likely negative effect will be increase of measurement noise. If dynamic rage of obtained readings is still satisfactory, then further reduction of power consumption may be done by lowering the measurement time with :cpp:func:`touch_pad_set_meas_time`.
The following summarizes available measurement parameters and corresponding 'set' functions:
* Touch pad charge / discharge parameters:
* voltage range: :cpp:func:`touch_pad_set_voltage`
* speed (slope): :cpp:func:`touch_pad_set_cnt_mode`
* Measure time: :cpp:func:`touch_pad_set_meas_time`
Relationship between voltage range (high / low reference voltages), speed (slope) and measure time is shown on figure below.
.. figure:: ../../_static/touch_pad-measurement-parameters.jpg
:align: center
:alt: Touch Pad - relationship between measurement parameters
:figclass: align-center
Touch Pad - relationship between measurement parameters
The last chart "Output" represents the touch sensor reading, i.e. the count of pulses collected within measure time.
All functions are provided in pairs to 'set' specific parameter and to 'get' the current parameter's value, e.g. :cpp:func:`touch_pad_set_voltage` and :cpp:func:`touch_pad_get_voltage`.
.. _touch_pad-api-filtering-of-measurements:
Filtering of Measurements
^^^^^^^^^^^^^^^^^^^^^^^^^
If measurements are noisy, you may filter them with provided API. The filter should be started before first use by calling :cpp:func:`touch_pad_filter_start`.
The filter type is IIR (Infinite Impulse Response) and it has configurable period that can be set with function :cpp:func:`touch_pad_set_filter_period`.
You can stop the filter with :cpp:func:`touch_pad_filter_stop`. If not required anymore, the filter may be deleted by invoking :cpp:func:`touch_pad_filter_delete`.
Touch Detection
^^^^^^^^^^^^^^^
Touch detection is implemented in ESP32's hardware basing on user configured threshold and raw measurements executed by FSM. Use function :cpp:func:`touch_pad_get_status` to check what pads have been touched and :cpp:func:`touch_pad_clear_status` to clear the touch status information.
Hardware touch detection may be also wired to interrupts and this is described in next section.
If measurements are noisy and capacity changes small, then hardware touch detection may be not reliable. To resolve this issue, instead of using hardware detection / provided interrupts, implement measurement filtering and perform touch detection in your own application. See :example:`peripherals/touch_pad_interrupt` for sample implementation of both methods of touch detection.
Touch Triggered Interrupts
^^^^^^^^^^^^^^^^^^^^^^^^^^
Before enabling an interrupt on touch detection, user should establish touch detection threshold. Use functions described above to read and display sensor measurements when pad is touched and released. Apply a filter when measurements are noisy and relative changes are small. Depending on your application and environmental conditions, test the influence of temperature and power supply voltage changes on measured values.
Once detection threshold is established, it may be set on initialization with :cpp:func:`touch_pad_config` or at the runtime with :cpp:func:`touch_pad_set_thresh`.
In next step configure how interrupts are triggered. They may be triggered below or above threshold and this is set with function :cpp:func:`touch_pad_set_trigger_mode`.
Finally configure and manage interrupt calls using the following functions:
* :cpp:func:`touch_pad_isr_register` / :cpp:func:`touch_pad_isr_deregister`
* :cpp:func:`touch_pad_intr_enable` / :cpp:func:`touch_pad_intr_disable`
When interrupts are operational, you can obtain information what particular pad triggered interrupt by invoking :cpp:func:`touch_pad_get_status` and clear pad status with :cpp:func:`touch_pad_clear_status`.
.. note::
Interrupts on touch detection operate on raw / unfiltered measurements checked against user established threshold and are implemented in hardware. Enabling software filtering API (see :ref:`touch_pad-api-filtering-of-measurements`) does not affect this process.
Wakeup from Sleep Mode
^^^^^^^^^^^^^^^^^^^^^^
If touch pad interrupts are used to wakeup the chip from a sleep mode, then user can select certain configuration of pads (SET1 or both SET1 and SET2), that should be touched to trigger the interrupt and cause subsequent wakeup. To do so, use function :cpp:func:`touch_pad_set_trigger_source`.
Configuration of required bit patterns of pads may be managed for each 'SET' with:
* :cpp:func:`touch_pad_set_group_mask` / :cpp:func:`touch_pad_get_group_mask`
* :cpp:func:`touch_pad_clear_group_mask`
.. _touch_pad-api-examples:
Application Examples
--------------------
- Touch sensor read example: :example:`peripherals/touch_pad_read`.
- Touch sensor interrupt example: :example:`peripherals/touch_pad_interrupt`.
.. _touch_pad-api-reference:
API Reference
-------------
.. include:: /_build/inc/touch_pad.inc
GPIO Lookup Macros
^^^^^^^^^^^^^^^^^^
Some useful macros can be used to specified the GPIO number of a touchpad channel, or vice versa.
e.g.
1. ``TOUCH_PAD_NUM5_GPIO_NUM`` is the GPIO number of channel 5 (12);
2. ``TOUCH_PAD_GPIO4_CHANNEL`` is the channel number of GPIO 4 (channel 0).
.. include:: /_build/inc/touch_channel.inc
+162
View File
@@ -0,0 +1,162 @@
UART
====
Overview
--------
An Universal Asynchronous Receiver/Transmitter (UART) is a component known to handle the timing requirements for a variety of widely-adapted protocols (RS232, RS485, RS422, ...). An UART provides a widely adopted and cheap method to realize full-duplex data exchange among different devices.
There are three UART controllers available on the ESP32 chip. They are compatible with UART-enabled devices from various manufacturers. All UART controllers integrated in the ESP32 feature an identical set of registers for ease of programming and flexibility. In this documentation, these controllers are referred to as UART0, UART1, and UART2.
Functional Overview
-------------------
The following overview describes functions and data types used to establish communication between ESP32 and some other UART device. The overview reflects a typical workflow when programming ESP32's UART driver and is broken down into the following sections:
1. :ref:`uart-api-setting-communication-parameters` - baud rate, data bits, stop bits, etc,
2. :ref:`uart-api-setting-communication-pins` - pins the other UART is connected to
3. :ref:`uart-api-driver-installation` - allocate ESP32's resources for the UART driver
4. :ref:`uart-api-running-uart-communication` - send / receive the data
5. :ref:`uart-api-using-interrupts` - trigger interrupts on specific communication events
6. :ref:`uart-api-deleting-driver` - release ESP32's resources, if UART communication is not required anymore
The minimum to make the UART working is to complete the first four steps, the last two steps are optional.
The driver is identified by :cpp:type:`uart_port_t`, that corresponds to one of the tree UART controllers. Such identification is present in all the following function calls.
.. _uart-api-setting-communication-parameters:
Setting Communication Parameters
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
There are two ways to set the communications parameters for UART. One is to do it in one shot by calling :cpp:func:`uart_param_config` provided with configuration parameters in :cpp:type:`uart_config_t` structure.
The alternate way is to configure specific parameters individually by calling dedicated functions:
* Baud rate - :cpp:func:`uart_set_baudrate`
* Number of transmitted bits - :cpp:func:`uart_set_word_length` selected out of :cpp:type:`uart_word_length_t`
* Parity control - :cpp:func:`uart_set_parity` selected out of :cpp:type:`uart_parity_t`
* Number of stop bits - :cpp:func:`uart_set_stop_bits` selected out of :cpp:type:`uart_stop_bits_t`
* Hardware flow control mode - :cpp:func:`uart_set_hw_flow_ctrl` selected out of `uart_hw_flowcontrol_t`
All the above functions have a ``_get_`` equivalent to retrieve the current setting, e.g. :cpp:func:`uart_get_baudrate`.
.. _uart-api-setting-communication-pins:
Setting Communication Pins
^^^^^^^^^^^^^^^^^^^^^^^^^^
In next step, after configuring communication parameters, we are setting physical GPIO pin numbers the other UART will be connected to. This is done in a single step by calling function :cpp:func:`uart_set_pin` and providing it with GPIO numbers, that driver should use for the Tx, Rx, RTS and CTS signals.
Instead of GPIO pin number we can enter a macro :cpp:type:`UART_PIN_NO_CHANGE` and the currently allocated pin will not be changed. The same macro should be entered if certain pin will not be used.
.. _uart-api-driver-installation:
Driver Installation
^^^^^^^^^^^^^^^^^^^
Once configuration of driver is complete, we can install it by calling :cpp:func:`uart_driver_install`. As result several resources required by the UART will be allocated. The type / size of resources are specified as function call parameters and concern:
* size of the send buffer
* size of the receive buffer
* the event queue handle and size
* flags to allocate an interrupt
If all above steps have been complete, we are ready to connect the other UART device and check the communication.
.. _uart-api-running-uart-communication:
Running UART Communication
^^^^^^^^^^^^^^^^^^^^^^^^^^
The processes of serial communication are under control of UART's hardware FSM. The data to be sent should be put into Tx FIFO buffer, FSM will serialize them and sent out. A similar process, but in reverse order, is done to receive the data. Incoming serial stream is processed by FSM and moved to the Rx FIFO buffer. Therefore the task of API's communication functions is limited to writing and reading the data to / from the respective buffer. This is reflected in some function names, e.g.: :cpp:func:`uart_write_bytes` to transmit the data out, or :cpp:func:`uart_read_bytes` to read the incoming data.
Transmitting
""""""""""""
The basic API function to write the data to Tx FIFO buffer is :cpp:func:`uart_tx_chars`. If the buffer contains not sent characters, this function will write what fits into the empty space and exit reporting the number of bytes actually written.
There is a 'companion' function :cpp:func:`uart_wait_tx_done` that waits until all the data are transmitted out and the Tx FIFO is empty.
An easier to work with function is :cpp:func:`uart_write_bytes`. It sets up an intermediate ring buffer and exits after copying the data to this buffer. When there is an empty space in the FIFO, the data are moved from the ring buffer to the FIFO in the background by an ISR.
There is a similar function as above that adds a serial break signal after sending the data - :cpp:func:`uart_write_bytes_with_break`. The 'serial break signal' means holding TX line low for period longer than one data frame.
Receiving
"""""""""
To retrieve the data received by UART and saved in Rx FIFO, use function :cpp:func:`uart_read_bytes`. You can check in advance what is the number of bytes available in Rx FIFO by calling :cpp:func:`uart_get_buffered_data_len`.
If the data in Rx FIFO is not required and should be discarded, call :cpp:func:`uart_flush`.
Software Flow Control
"""""""""""""""""""""
When the hardware flow control is disabled, then use :cpp:func:`uart_set_rts` and :cpp:func:`uart_set_dtr` to manually set the levels of the RTS and DTR signals.
.. _uart-api-using-interrupts:
Using Interrupts
^^^^^^^^^^^^^^^^
There are nineteen interrupts reported on specific states of UART or on detected errors. The full list of available interrupts is described in `ESP32 Technical Reference Manual <https://espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf>`_ (PDF). To enable specific interrupts call :cpp:func:`uart_enable_intr_mask`, to disable call :cpp:func:`uart_disable_intr_mask`. The mask of all interrupts is available as :cpp:type:`UART_INTR_MASK`. Registration of an handler to service interrupts is done with :cpp:func:`uart_isr_register`, freeing the handler with :cpp:func:`uart_isr_free`. To clear the interrupt status bits once the handler is called use :cpp:func:`uart_clear_intr_status`.
The API provides a convenient way to handle specific interrupts discussed above by wrapping them into dedicated functions:
* **Event detection** - there are several events defined in :cpp:type:`uart_event_type_t` that may be reported to user application using FreeRTOS queue functionality. You can enable this functionality when calling :cpp:func:`uart_driver_install` described in :ref:`uart-api-driver-installation`. Example how to use it is covered in :example:`peripherals/uart_events`.
* **FIFO space threshold or transmission timeout reached** - the interrupts on TX or Rx FIFO buffer being filled with specific number of characters or on a timeout of sending or receiving data. To use these interrupts, first configure respective threshold values of the buffer length and the timeout by entering them in :cpp:type:`uart_intr_config_t` structure and calling :cpp:func:`uart_intr_config`. Then enable interrupts with functions :cpp:func:`uart_enable_rx_intr` and :cpp:func:`uart_enable_tx_intr`. To disable these interrupts there are corresponding functions :cpp:func:`uart_disable_rx_intr` or :cpp:func:`uart_disable_tx_intr`.
* **Pattern detection** - an interrupt triggered on detecting a 'pattern' of the same character being sent number of times. The functions that allow to configure, enable and disable this interrupt are :cpp:func:`uart_enable_pattern_det_intr` and cpp:func:`uart_disable_pattern_det_intr`.
Macros
^^^^^^
The API provides several macros to define configuration parameters, e.g. :cpp:type:`UART_FIFO_LEN` to define the length of the hardware FIFO buffers, :cpp:type:`UART_BITRATE_MAX` that gives the maximum baud rate supported by UART, etc.
.. _uart-api-deleting-driver:
Deleting Driver
^^^^^^^^^^^^^^^
If communication is established with :cpp:func:`uart_driver_install` for some specific period of time and then not required, the driver may be removed to free allocated resources by calling :cpp:func:`uart_driver_delete`.
Application Examples
--------------------
Configure UART settings and install UART driver to read/write using UART1 interface: :example:`peripherals/uart_echo`.
Demonstration of how to report various communication events and how to use patern detection interrupts: :example:`peripherals/uart_events`.
Transmitting and receiveing with the same UART in two separate FreeRTOS tasks: :example:`peripherals/uart_async_rxtxtasks`.
API Reference
-------------
.. include:: /_build/inc/uart.inc
GPIO Lookup Macros
^^^^^^^^^^^^^^^^^^
You can use macros to specify the **direct** GPIO (UART module connected to pads through direct IO mux without the GPIO mux) number of a UART channel, or vice versa. The pin name can be omitted if the channel of a GPIO number is specified, e.g.:
1. ``UART_NUM_2_TXD_DIRECT_GPIO_NUM`` is the GPIO number of UART channel 2 TXD pin (17);
2. ``UART_GPIO19_DIRECT_CHANNEL`` is the UART channel number of GPIO 19 (channel 0);
3. ``UART_CTS_GPIO19_DIRECT_CHANNEL`` is the UART channel number of GPIO 19, and GPIO 19 must be a CTS pin (channel 0).
.. include:: /_build/inc/uart_channel.inc
+10
View File
@@ -0,0 +1,10 @@
Protocols API
*************
.. toctree::
:maxdepth: 1
mDNS <mdns>
Example code for this API section is provided in :example:`protocols` directory of ESP-IDF examples.
+188
View File
@@ -0,0 +1,188 @@
mDNS Service
============
Overview
--------
mDNS is a multicast UDP service that is used to provide local network service and host discovery.
mDNS is installed by default on most operating systems or is available as separate package. On ``Mac OS`` it is installed by default and is called ``Bonjour``. Apple releases an installer for ``Windows`` that can be found `on Apple's support page <https://support.apple.com/downloads/bonjour%2520for%2520windows>`_. On ``Linux``, mDNS is provided by `avahi <https://github.com/lathiat/avahi>`_ and is usually installed by default.
mDNS Properties
^^^^^^^^^^^^^^^
* ``hostname``: the hostname that the device will respond to. If not set, the ``hostname`` will be read from the interface. Example: ``my-esp32`` will resolve to ``my-esp32.local``
* ``default_instance``: friendly name for your device, like ``Jhon's ESP32 Thing``. If not set, ``hostname`` will be used.
Example method to start mDNS for the STA interface and set ``hostname`` and ``default_instance``:
.. highlight:: c
::
void start_mdns_service()
{
//initialize mDNS service
esp_err_t err = mdns_init();
if (err) {
printf("MDNS Init failed: %d\n", err);
return;
}
//set hostname
mdns_hostname_set("my-esp32");
//set default instance
mdns_instance_name_set("Jhon's ESP32 Thing");
}
mDNS Services
^^^^^^^^^^^^^
mDNS can advertise information about network services that your device offers. Each service is defined by a few properties.
* ``instance_name``: friendly name for your service, like ``Jhon's ESP32 Web Server``. If not defined, ``default_instance`` will be used.
* ``service_type``: (required) service type, prepended with underscore. Some common types can be found `here <http://www.dns-sd.org/serviceTypes.html>`_.
* ``proto``: (required) protocol that the service runs on, prepended with underscore. Example: ``_tcp`` or ``_udp``
* ``port``: (required) network port that the service runs on
* ``txt``: ``{var, val}`` array of strings, used to define properties for your service
Example method to add a few services and different properties::
void add_mdns_services()
{
//add our services
mdns_service_add(NULL, "_http", "_tcp", 80, NULL, 0);
mdns_service_add(NULL, "_arduino", "_tcp", 3232, NULL, 0);
mdns_service_add(NULL, "_myservice", "_udp", 1234, NULL, 0);
//NOTE: services must be added before their properties can be set
//use custom instance for the web server
mdns_service_instance_name_set("_http", "_tcp", "Jhon's ESP32 Web Server");
mdns_txt_item_t serviceTxtData[3] = {
{"board","esp32"},
{"u","user"},
{"p","password"}
};
//set txt data for service (will free and replace current data)
mdns_service_txt_set("_http", "_tcp", serviceTxtData, 3);
//change service port
mdns_service_port_set("_myservice", "_udp", 4321);
}
mDNS Query
^^^^^^^^^^
mDNS provides methods for browsing for services and resolving host's IP/IPv6 addresses.
Results for services are returned as a linked list of ``mdns_result_t`` objects.
Example method to resolve host IPs::
void resolve_mdns_host(const char * host_name)
{
printf("Query A: %s.local", host_name);
struct ip4_addr addr;
addr.addr = 0;
esp_err_t err = mdns_query_a(host_name, 2000, &addr);
if(err){
if(err == ESP_ERR_NOT_FOUND){
printf("Host was not found!");
return;
}
printf("Query Failed");
return;
}
printf(IPSTR, IP2STR(&addr));
}
Example method to resolve local services::
static const char * if_str[] = {"STA", "AP", "ETH", "MAX"};
static const char * ip_protocol_str[] = {"V4", "V6", "MAX"};
void mdns_print_results(mdns_result_t * results){
mdns_result_t * r = results;
mdns_ip_addr_t * a = NULL;
int i = 1, t;
while(r){
printf("%d: Interface: %s, Type: %s\n", i++, if_str[r->tcpip_if], ip_protocol_str[r->ip_protocol]);
if(r->instance_name){
printf(" PTR : %s\n", r->instance_name);
}
if(r->hostname){
printf(" SRV : %s.local:%u\n", r->hostname, r->port);
}
if(r->txt_count){
printf(" TXT : [%u] ", r->txt_count);
for(t=0; t<r->txt_count; t++){
printf("%s=%s; ", r->txt[t].key, r->txt[t].value);
}
printf("\n");
}
a = r->addr;
while(a){
if(a->addr.type == MDNS_IP_PROTOCOL_V6){
printf(" AAAA: " IPV6STR "\n", IPV62STR(a->addr.u_addr.ip6));
} else {
printf(" A : " IPSTR "\n", IP2STR(&(a->addr.u_addr.ip4)));
}
a = a->next;
}
r = r->next;
}
}
void find_mdns_service(const char * service_name, const char * proto)
{
ESP_LOGI(TAG, "Query PTR: %s.%s.local", service_name, proto);
mdns_result_t * results = NULL;
esp_err_t err = mdns_query_ptr(service_name, proto, 3000, 20, &results);
if(err){
ESP_LOGE(TAG, "Query Failed");
return;
}
if(!results){
ESP_LOGW(TAG, "No results found!");
return;
}
mdns_print_results(results);
mdns_query_results_free(results);
}
Example of using the methods above::
void my_app_some_method(){
//search for esp32-mdns.local
resolve_mdns_host("esp32-mdns");
//search for HTTP servers
find_mdns_service("_http", "_tcp");
//or file servers
find_mdns_service("_smb", "_tcp"); //windows sharing
find_mdns_service("_afpovertcp", "_tcp"); //apple sharing
find_mdns_service("_nfs", "_tcp"); //NFS server
find_mdns_service("_ftp", "_tcp"); //FTP server
//or networked printer
find_mdns_service("_printer", "_tcp");
find_mdns_service("_ipp", "_tcp");
}
Application Example
-------------------
mDNS server/scanner example: :example:`protocols/mdns`.
API Reference
-------------
.. include:: /_build/inc/mdns.inc
+60
View File
@@ -0,0 +1,60 @@
FAT Filesystem Support
======================
ESP-IDF uses `FatFs <http://elm-chan.org/fsw/ff/00index_e.html>`_ library to work with FAT filesystems. FatFs library resides in ``fatfs`` component. Although it can be used directly, many of its features can be accessed via VFS using C standard library and POSIX APIs.
Additionally, FatFs has been modified to support run-time pluggable disk IO layer. This allows mapping of FatFs drives to physical disks at run-time.
Using FatFs with VFS
--------------------
``esp_vfs_fat.h`` header file defines functions to connect FatFs with VFS. ``esp_vfs_fat_register`` function allocates a ``FATFS`` structure, and registers a given path prefix in VFS. Subsequent operations on files starting with this prefix are forwarded to FatFs APIs. ``esp_vfs_fat_unregister_path`` function deletes the registration with VFS, and frees the ``FATFS`` structure.
Most applications will use the following flow when working with ``esp_vfs_fat_`` functions:
1. Call ``esp_vfs_fat_register``, specifying path prefix where the filesystem has to be mounted (e.g. ``"/sdcard"``, ``"/spiflash"``), FatFs drive number, and a variable which will receive a pointer to ``FATFS`` structure.
2. Call ``ff_diskio_register`` function to register disk IO driver for the drive number used in step 1.
3. Call ``f_mount`` function (and optionally ``f_fdisk``, ``f_mkfs``) to mount the filesystem using the same drive number which was passed to ``esp_vfs_fat_register``. See FatFs documentation for more details.
4. Call POSIX and C standard library functions to open, read, write, erase, copy files, etc. Use paths starting with the prefix passed to ``esp_vfs_register`` (such as ``"/sdcard/hello.txt"``).
5. Optionally, call FatFs library functions directly. Use paths without a VFS prefix in this case (``"/hello.txt"``).
6. Close all open files.
7. Call ``f_mount`` function for the same drive number, with NULL ``FATFS*`` argument, to unmount the filesystem.
8. Call ``ff_diskio_register`` with NULL ``ff_diskio_impl_t*`` argument and the same drive number.
9. Call ``esp_vfs_fat_unregister_path`` with the path where the file system is mounted to remove FatFs from VFS, and free the ``FATFS`` structure allocated on step 1.
Convenience functions, ``esp_vfs_fat_sdmmc_mount`` and ``esp_vfs_fat_sdmmc_unmount``, which wrap these steps and also handle SD card initialization, are described in the next section.
.. doxygenfunction:: esp_vfs_fat_register
.. doxygenfunction:: esp_vfs_fat_unregister_path
Using FatFs with VFS and SD cards
---------------------------------
``esp_vfs_fat.h`` header file also provides a convenience function to perform steps 13 and 79, and also handle SD card initialization: ``esp_vfs_fat_sdmmc_mount``. This function does only limited error handling. Developers are encouraged to look at its source code and incorporate more advanced versions into production applications. ``esp_vfs_fat_sdmmc_unmount`` function unmounts the filesystem and releases resources acquired by ``esp_vfs_fat_sdmmc_mount``.
.. doxygenfunction:: esp_vfs_fat_sdmmc_mount
.. doxygenstruct:: esp_vfs_fat_mount_config_t
:members:
.. doxygenfunction:: esp_vfs_fat_sdmmc_unmount
FatFS disk IO layer
-------------------
FatFs has been extended with an API to register disk IO driver at runtime.
Implementation of disk IO functions for SD/MMC cards is provided. It can be registered for the given FatFs drive number using ``ff_diskio_register_sdmmc`` function.
.. doxygenfunction:: ff_diskio_register
.. doxygenstruct:: ff_diskio_impl_t
:members:
.. doxygenfunction:: ff_diskio_register_sdmmc
+16
View File
@@ -0,0 +1,16 @@
Storage API
***********
.. toctree::
:maxdepth: 1
SPI Flash and Partition APIs <spi_flash>
SD/MMC Card Host <sdmmc>
Non-Volatile Storage <nvs_flash>
Virtual Filesystem <vfs>
FAT Filesystem <fatfs>
Wear Levelling <wear-levelling>
SPIFFS Filesystem <spiffs>
Example code for this API section is provided in :example:`storage` directory of ESP-IDF examples.
@@ -0,0 +1,33 @@
.. include:: ../../../../components/nvs_flash/README.rst
Application Example
-------------------
Two examples are provided in :example:`storage` directory of ESP-IDF examples:
:example:`storage/nvs_rw_value`
Demonstrates how to read and write a single integer value using NVS.
The value holds the number of ESP32 module restarts. Since it is written to NVS, the value is preserved between restarts.
Example also shows how to check if read / write operation was successful, or certain value is not initialized in NVS. Diagnostic is provided in plain text to help track program flow and capture any issues on the way.
:example:`storage/nvs_rw_blob`
Demonstrates how to read and write a single integer value and a blob (binary large object) using NVS to preserve them between ESP32 module restarts.
* value - tracks number of ESP32 module soft and hard restarts.
* blob - contains a table with module run times. The table is read from NVS to dynamically allocated RAM. New run time is added to the table on each manually triggered soft restart and written back to NVS. Triggering is done by pulling down GPIO0.
Example also shows how to implement diagnostics if read / write operation was successful.
API Reference
-------------
.. include:: /_build/inc/nvs_flash.inc
.. include:: /_build/inc/nvs.inc
+129
View File
@@ -0,0 +1,129 @@
SDMMC Host Peripheral
=====================
Overview
--------
SDMMC peripheral supports SD and MMC memory cards and SDIO cards. SDMMC software builds on top of SDMMC driver and consists of the following parts:
1. SDMMC host driver (``driver/sdmmc_host.h``) — this driver provides APIs to send commands to the slave device(s), send and receive data, and handling error conditions on the bus.
2. SDMMC protocol layer (``sdmmc_cmd.h``) — this component handles specifics of SD protocol such as card initialization and data transfer commands. Despite the name, only SD (SDSC/SDHC/SDXC) cards are supported at the moment. Support for MCC/eMMC cards can be added in the future.
Protocol layer works with the host via ``sdmmc_host_t`` structure. This structure contains pointers to various functions of the host.
In addition to SDMMC Host peripheral, ESP32 has SPI peripherals which can also be used to work with SD cards. This is supported using a variant of the host driver, ``driver/sdspi_host.h``. This driver has the same interface as SDMMC host driver, and the protocol layer can use either of two.
Application Example
-------------------
An example which combines SDMMC driver with FATFS library is provided in ``examples/storage/sd_card`` directory. This example initializes the card, writes and reads data from it using POSIX and C library APIs. See README.md file in the example directory for more information.
Protocol layer APIs
-------------------
Protocol layer is given ``sdmmc_host_t`` structure which describes the SD/MMC host driver, lists its capabilites, and provides pointers to functions of the driver. Protocol layer stores card-specific information in ``sdmmc_card_t`` structure. When sending commands to the SD/MMC host driver, protocol layer uses ``sdmmc_command_t`` structure to describe the command, argument, expected return value, and data to transfer, if any.
Normal usage of the protocol layer is as follows:
1. Call the host driver functions to initialize the host (e.g. ``sdmmc_host_init``, ``sdmmc_host_init_slot``).
2. Call ``sdmmc_card_init`` to initialize the card, passing it host driver information (``host``) and a pointer to ``sdmmc_card_t`` structure which will be filled in (``card``).
3. To read and write sectors of the card, use ``sdmmc_read_sectors`` and ``sdmmc_write_sectors``, passing the pointer to card information structure (``card``).
4. When card is not used anymore, call the host driver function to disable SDMMC host peripheral and free resources allocated by the driver (e.g. ``sdmmc_host_deinit``).
Most applications need to use the protocol layer only in one task; therefore the protocol layer doesn't implement any kind of locking on the ``sdmmc_card_t`` structure, or when accessing SDMMC host driver. Such locking has to be implemented in the higher layer, if necessary (e.g. in the filesystem driver).
.. doxygenstruct:: sdmmc_host_t
:members:
.. doxygendefine:: SDMMC_HOST_FLAG_1BIT
.. doxygendefine:: SDMMC_HOST_FLAG_4BIT
.. doxygendefine:: SDMMC_HOST_FLAG_8BIT
.. doxygendefine:: SDMMC_HOST_FLAG_SPI
.. doxygendefine:: SDMMC_FREQ_DEFAULT
.. doxygendefine:: SDMMC_FREQ_HIGHSPEED
.. doxygendefine:: SDMMC_FREQ_PROBING
.. doxygenstruct:: sdmmc_command_t
:members:
.. doxygenstruct:: sdmmc_card_t
:members:
.. doxygenstruct:: sdmmc_csd_t
:members:
.. doxygenstruct:: sdmmc_cid_t
:members:
.. doxygenstruct:: sdmmc_scr_t
:members:
.. doxygenfunction:: sdmmc_card_init
.. doxygenfunction:: sdmmc_write_sectors
.. doxygenfunction:: sdmmc_read_sectors
SDMMC host driver APIs
----------------------
On the ESP32, SDMMC host peripheral has two slots:
- Slot 0 (``SDMMC_HOST_SLOT_0``) is an 8-bit slot. It uses ``HS1_*`` signals in the PIN MUX.
- Slot 1 (``SDMMC_HOST_SLOT_1``) is a 4-bit slot. It uses ``HS2_*`` signals in the PIN MUX.
Card Detect and Write Protect signals can be routed to arbitrary pins using GPIO matrix. To use these pins, set ``gpio_cd`` and ``gpio_wp`` members of ``sdmmc_slot_config_t`` structure when calling ``sdmmc_host_init_slot``.
Of all the funtions listed below, only ``sdmmc_host_init``, ``sdmmc_host_init_slot``, and ``sdmmc_host_deinit`` will be used directly by most applications. Other functions, such as ``sdmmc_host_set_bus_width``, ``sdmmc_host_set_card_clk``, and ``sdmmc_host_do_transaction`` will be called by the SD/MMC protocol layer via function pointers in ``sdmmc_host_t`` structure.
.. doxygenfunction:: sdmmc_host_init
.. doxygendefine:: SDMMC_HOST_SLOT_0
.. doxygendefine:: SDMMC_HOST_SLOT_1
.. doxygendefine:: SDMMC_HOST_DEFAULT
.. doxygendefine:: SDMMC_SLOT_WIDTH_DEFAULT
.. doxygenfunction:: sdmmc_host_init_slot
.. doxygenstruct:: sdmmc_slot_config_t
:members:
.. doxygendefine:: SDMMC_SLOT_NO_CD
.. doxygendefine:: SDMMC_SLOT_NO_WP
.. doxygendefine:: SDMMC_SLOT_CONFIG_DEFAULT
.. doxygenfunction:: sdmmc_host_set_bus_width
.. doxygenfunction:: sdmmc_host_set_card_clk
.. doxygenfunction:: sdmmc_host_do_transaction
.. doxygenfunction:: sdmmc_host_deinit
SD SPI driver APIs
------------------
SPI controllers accessible via spi_master driver (HSPI, VSPI) can be used to work with SD cards. In SPI mode, SD driver has lower throughput than in 1-line SD mode. However SPI mode makes pin selection more flexible, as SPI peripheral can be connected to any ESP32 pins using GPIO Matrix. SD SPI driver uses software controlled CS signal. Currently SD SPI driver assumes that it can use the SPI controller exclusively, so applications which need to share SPI bus between SD cards and other peripherals need to make sure that SD card and other devices are not used at the same time from different tasks.
SD SPI driver is represented using an ``sdmmc_host_t`` structure initialized using ``SDSPI_HOST_DEFAULT`` macro. For slot initialization, ``SDSPI_SLOT_CONFIG_DEFAULT`` can be used to fill in default pin mapping, which is the same as the pin mapping in SD mode.
SD SPI driver APIs are very similar to SDMMC host APIs. As with the SDMMC host driver, only ``sdspi_host_init``, ``sdspi_host_init_slot``, and ``sdspi_host_deinit`` functions are normally used by the applications. Other functions are called by the protocol level driver via function pointers in ``sdmmc_host_t`` structure.
.. note:
SD over SPI does not support speeds above SDMMC_FREQ_DEFAULT due to a limitation of SPI driver.
.. doxygenfunction:: sdspi_host_init
.. doxygendefine:: SDSPI_HOST_DEFAULT
.. doxygenfunction:: sdspi_host_init_slot
.. doxygenstruct:: sdspi_slot_config_t
:members:
.. doxygendefine:: SDSPI_SLOT_NO_CD
.. doxygendefine:: SDSPI_SLOT_NO_WP
.. doxygendefine:: SDSPI_SLOT_CONFIG_DEFAULT
.. doxygenfunction:: sdspi_host_set_card_clk
.. doxygenfunction:: sdspi_host_do_transaction
.. doxygenfunction:: sdspi_host_deinit
@@ -0,0 +1,61 @@
.. include:: ../../../../components/spi_flash/README.rst
See also
--------
- :doc:`Partition Table documentation <../../api-guides/partition-tables>`
- :doc:`Over The Air Update (OTA) API <../system/ota>` provides high-level API for updating app firmware stored in flash.
- :doc:`Non-Volatile Storage (NVS) API <nvs_flash>` provides a structured API for storing small items of data in SPI flash.
.. _spi-flash-implementation-details:
Implementation details
----------------------
In order to perform some flash operations, we need to make sure both CPUs
are not running any code from flash for the duration of the flash operation.
In a single-core setup this is easy: we disable interrupts/scheduler and do
the flash operation. In the dual-core setup this is slightly more complicated.
We need to make sure that the other CPU doesn't run any code from flash.
When SPI flash API is called on CPU A (can be PRO or APP), we start
spi_flash_op_block_func function on CPU B using esp_ipc_call API. This API
wakes up high priority task on CPU B and tells it to execute given function,
in this case spi_flash_op_block_func. This function disables cache on CPU B and
signals that cache is disabled by setting s_flash_op_can_start flag.
Then the task on CPU A disables cache as well, and proceeds to execute flash
operation.
While flash operation is running, interrupts can still run on CPUs A and B.
We assume that all interrupt code is placed into RAM. Once interrupt allocation
API is added, we should add a flag to request interrupt to be disabled for
the duration of flash operations.
Once flash operation is complete, function on CPU A sets another flag,
s_flash_op_complete, to let the task on CPU B know that it can re-enable
cache and release the CPU. Then the function on CPU A re-enables the cache on
CPU A as well and returns control to the calling code.
Additionally, all API functions are protected with a mutex (s_flash_op_mutex).
In a single core environment (:ref:`CONFIG_FREERTOS_UNICORE` enabled), we simply
disable both caches, no inter-CPU communication takes place.
API Reference - SPI Flash
-------------------------
.. include:: /_build/inc/esp_spi_flash.inc
API Reference - Partition Table
-------------------------------
.. include:: /_build/inc/esp_partition.inc
API Reference - Flash Encrypt
-----------------------------
.. include:: /_build/inc/esp_flash_encrypt.inc
+53
View File
@@ -0,0 +1,53 @@
SPIFFS Filesystem
=================
Overview
--------
SPIFFS is a file system intended for SPI NOR flash devices on embedded targets.
It supports wear leveling, file system consistency checks and more.
Notes
-----
- Presently, spiffs does not support directories. It produces a flat structure. If SPIFFS is mounted under ``/spiffs`` creating a file with path ``/spiffs/tmp/myfile.txt`` will create a file called ``/tmp/myfile.txt`` in SPIFFS, instead of ``myfile.txt`` under directory ``/spiffs/tmp``.
- It is not a realtime stack. One write operation might last much longer than another.
- Presently, it does not detect or handle bad blocks.
Tools
-----
Host-Side tools for creating SPIFS partition images exist and one such tool is `mkspiffs <https://github.com/igrr/mkspiffs>`_.
You can use it to create image from a given folder and then flash that image with ``esptool.py``
To do that you need to obtain some parameters:
- Block Size: 4096 (standard for SPI Flash)
- Page Size: 256 (standard for SPI Flash)
- Image Size: Size of the partition in bytes (can be obtained from partition table)
- Partition Offset: Starting address of the partition (can be obtained from partition table)
To pack a folder into 1 Megabyte image::
mkspiffs -c [src_folder] -b 4096 -p 256 -s 0x100000 spiffs.bin
To flash the image to ESP32 at offset 0x110000::
python esptool.py --chip esp32 --port [port] --baud [baud] write_flash -z 0x110000 spiffs.bin
See also
--------
- :doc:`Partition Table documentation <../../api-guides/partition-tables>`
Application Example
-------------------
An example for using SPIFFS is provided in :example:`storage/spiffs` directory. This example initializes and mounts SPIFFS partition, and writes and reads data from it using POSIX and C library APIs. See README.md file in the example directory for more information.
High level API Reference
------------------------
* :component_file:`spiffs/include/esp_spiffs.h`
.. include:: /_build/inc/esp_spiffs.inc
+16
View File
@@ -0,0 +1,16 @@
.. include:: ../../../../components/vfs/README.rst
Application Example
-------------------
`Instructions`_
.. _Instructions: ../../template.html
API Reference
-------------
.. include:: /_build/inc/esp_vfs.inc
.. include:: /_build/inc/esp_vfs_dev.inc
@@ -0,0 +1,34 @@
.. include:: ../../../../components/wear_levelling/README.rst
See also
--------
- :doc:`FAT Filesystem <./fatfs>`
- :doc:`Partition Table documentation <../../api-guides/partition-tables>`
Application Example
-------------------
An example which combines wear levelling driver with FATFS library is provided in ``examples/storage/wear_levelling`` directory. This example initializes the wear levelling driver, mounts FATFS partition, and writes and reads data from it using POSIX and C library APIs. See README.md file in the example directory for more information.
High level API Reference
------------------------
Header Files
^^^^^^^^^^^^
* :component_file:`fatfs/src/esp_vfs_fat.h`
Functions
^^^^^^^^^
.. doxygenfunction:: esp_vfs_fat_spiflash_mount
.. doxygenstruct:: esp_vfs_fat_mount_config_t
:members:
.. doxygenfunction:: esp_vfs_fat_spiflash_unmount
Mid level API Reference
-----------------------
.. include:: /_build/inc/wear_levelling.inc
@@ -0,0 +1,18 @@
Application Level Tracing
=========================
Overview
--------
IDF provides useful feature for program behaviour analysis: application level tracing. It is implemented in the corresponding library and can be enabled via menuconfig. This feature allows to transfer arbitrary data between host and ESP32 via JTAG interface with small overhead on program execution.
Developers can use this library to send application specific state of execution to the host and receive commands or other type of info in the opposite direction at runtime. The main use cases of this library are:
1. Collecting application specific data, see :ref:`app_trace-application-specific-tracing`
2. Lightweight logging to the host, see :ref:`app_trace-logging-to-host`
3. System behaviour analysis, see :ref:`app_trace-system-behaviour-analysis-with-segger-systemview`
API Reference
-------------
.. include:: /_build/inc/esp_app_trace.inc
@@ -0,0 +1,82 @@
Base MAC address
================
Overview
--------
Serveral MAC addresses (universally administered by IEEE) are uniquely assigned to the networking interfaces (WiFi/BT/Ethernet).
The final octet of each universally administered MAC address increases by one. Only the first one which is called base MAC address
of them is stored in EFUSE or external storage, the others are generated from it. Here, 'generate' means adding 0, 1, 2 and 3
(respectively) to the final octet of the base MAC address.
If the universally administered MAC addresses are not enough for all of the networking interfaces. Local administered MAC addresses
which are derived from universally administered MAC addresses are assigned to the reset of networking interfaces.
A `definition of local vs universal MAC address can be found on Wikipedia <https://en.wikipedia.org/wiki/MAC_address#Universal_vs._local>`_.
The number of universally administered MAC address can be configured using ``make menuconfig``.
Base MAC address
^^^^^^^^^^^^^^^^
If using the default base MAC address factory programmed by Espressif in BLK0 of EFUSE, nothing needs to be done.
If using a custom base MAC address stored in BLK3 of EFUSE, call API ``esp_efuse_mac_get_custom()`` to get the base MAC address
which is stored in BLK3 of EFUSE. If correct MAC address is returned, then call ``esp_base_mac_addr_set()`` to set the base MAC
address for system to generate the MAC addresses used by the networking interfaces(WiFi/BT/Ethernet).
There are 192 bits storage spaces for custom to store base MAC address in BLK3 of EFUSE. They are EFUSE_BLK3_RDATA0,
EFUSE_BLK3_RDATA1, EFUSE_BLK3_RDATA2, EFUSE_BLK3_RDATA3, EFUSE_BLK3_RDATA4 and EFUSE_BLK3_RDATA5, each of them is 32 bits
register. The format of the 192 bits storage spaces is:
.. highlight:: none
::
------------------------------------------------------
Field |Bits |Range |Description
------------------------------------------------------
version |8 |[191:184] |1: useful. 0: useless
------------------------------------------------------
reserve |112 |[183:72] |reserved
------------------------------------------------------
mac address |64 |[71:8] |base MAC address
------------------------------------------------------
mac crc |8 |[7:0] |crc of base MAC address
------------------------------------------------------
If using base MAC address stored in external storage, firstly get the base MAC address stored in external storage, then call
API ``esp_base_mac_addr_set()`` to set the base MAC address for system to generate the MAC addresses used by the networking
interfaces(WiFi/BT/Ethernet).
All of the steps must be done before initializing the networking interfaces(WiFi/BT/Ethernet). It is recommended to do it in
``app_main()`` which can be referenced in :example:`system/base_mac_address`.
Number of universally administered MAC address
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If the number of universal MAC addresses is two, only two interfaces (WiFi station and Bluetooth) receive a universally
administered MAC address. These are generated sequentially by adding 0 and 1 (respectively) to the base MAC address.
The remaining two interfaces (WiFi softap and Ethernet) receive local MAC addresses. These are derived from the universal
WiFi station and Bluetooth MAC addresses, respectively.
If the number of universal MAC addresses is four, all four interfaces (WiFi station, WiFi softap, Bluetooth and Ethernet)
receive a universally administered MAC address. These are generated sequentially by adding 0, 1, 2 and 3 (respectively)
to the final octet of the base MAC address.
When using the default (Espressif-assigned) base MAC address, either setting can be used. When using a custom universal MAC
address range, the correct setting will depend on the allocation of MAC addresses in this range (either 2 or 4 per device.)
API Reference
-------------
Header Files
^^^^^^^^^^^^
* :component_file:`esp32/include/esp_system.h`
Functions
---------
.. doxygenfunction:: esp_base_mac_addr_set
.. doxygenfunction:: esp_efuse_mac_get_custom
@@ -0,0 +1,52 @@
High Resolution Timer
=====================
Overview
--------
Although FreeRTOS provides software timers, these timers have a few limitations:
- Maximum resolution is equal to RTOS tick period
- Timer callbacks are dispatched from a low-priority task
Hardware timers are free from both of the limitations, but often they are less convenient to use. For example, application components may need timer events to fire at certain times in the future, but the hardware timer only contains one "compare" value used for interrupt generation. This means that some facility needs to be built on top of the hardware timer to manage the list of pending events can dispatch the callbacks for these events as corresponding hardware interrupts happen.
``esp_timer`` set of APIs provide such facility. Internally, ``esp_timer`` uses a 32-bit hardware timer (FRC1, "legacy" timer). ``esp_timer`` provides one-shot and periodic timers, microsecond time resolution, and 64-bit range.
Timer callbacks are dispatched from a high-priority ``esp_timer`` task. Because all the callbacks are dispatched from the same task, it is recommended to only do the minimal possible amount of work from the callback itself, posting an event to a lower priority task using a queue instead.
.. note: Provisions are made to dispatch some simple callbacks directly from the interrupt handler, if needed. However this option is not implemented at the moment.
Using ``esp_timer`` APIs
------------------------
Single timer is represented by :cpp:type:`esp_timer_handle_t` type. Timer has a callback function associated with it. This callback function is called from the ``esp_timer`` task each time the timer elapses.
- To create a timer, call :cpp:func:`esp_timer_create`.
- To delete the timer when it is no longer needed, call :cpp:func:`esp_timer_delete`.
The timer can be started in one-shot mode or in periodic mode.
- To start the timer in one-shot mode, call :cpp:func:`esp_timer_start_once`, passing the time interval after which the callback should be called. When the callback gets called, the timer is considered to be stopped.
- To start the timer in periodic mode, call :cpp:func:`esp_timer_start_periodic`, passing the period with which the callback should be called. The timer keeps running until :cpp:func:`esp_timer_stop` is called.
Note that the timer must not be running when :cpp:func:`esp_timer_start_once` or :cpp:func:`esp_timer_start_periodic` is called. To restart a running timer, call :cpp:func:`esp_timer_stop` first, then call one of the start functions.
Obtaining Current Time
----------------------
``esp_timer`` also provides a convenience function to obtain the time passed since start-up, with microsecond precision: :cpp:func:`esp_timer_get_time`. This function returns the number of microseconds since ``esp_timer`` was initialized, which usually happens shortly before ``app_main`` function is called.
Unlike `gettimeofday` function, values returned by :cpp:func:`esp_timer_get_time`:
- Start from zero after the chip wakes up from deep sleep
- Do not have timezone or DST adjustments applied
API Reference
-------------
.. include:: /_build/inc/esp_timer.inc
+42
View File
@@ -0,0 +1,42 @@
FreeRTOS
========
Overview
--------
This section contains documentation of FreeRTOS types, functions, and macros. It is automatically generated from FreeRTOS header files.
For more information about FreeRTOS features specific to ESP-IDF, see :doc:`ESP-IDF FreeRTOS SMP Changes<../../api-guides/freertos-smp>`.
Task API
--------
.. include:: /_build/inc/task.inc
Queue API
---------
.. include:: /_build/inc/queue.inc
Semaphore API
-------------
.. include:: /_build/inc/semphr.inc
Timer API
---------
.. include:: /_build/inc/timers.inc
Event Group API
---------------
.. include:: /_build/inc/event_groups.inc
Ringbuffer API
--------------
.. include:: /_build/inc/ringbuf.inc
+245
View File
@@ -0,0 +1,245 @@
Heap Memory Debugging
=====================
Overview
--------
ESP-IDF integrates tools for requesting `heap information`_, `detecting heap corruption <heap corruption detection>`_, and `tracing memory leaks <heap tracing>`_. These can help track down memory-related bugs.
For general information about the heap memory allocator, see the :doc:`Heap Memory Allocation </api-reference/system/mem_alloc>` page.
.. _heap-information:
Heap Information
----------------
To obtain information about the state of the heap:
- :cpp:func:`xPortGetFreeHeapSize` is a FreeRTOS function which returns the number of free bytes in the (data memory) heap. This is equivalent to calling ``heap_caps_get_free_size(MALLOC_CAP_8BIT)``.
- :cpp:func:`heap_caps_get_free_size` can also be used to return the current free memory for different memory capabilities.
- :cpp:func:`heap_caps_get_largest_free_block` can be used to return the largest free block in the heap. This is the largest single allocation which is currently possible. Tracking this value and comparing to total free heap allows you to detect heap fragmentation.
- :cpp:func:`xPortGetMinimumEverFreeHeapSize` and the related :cpp:func:`heap_caps_get_minimum_free_size` can be used to track the heap "low water mark" since boot.
- :cpp:func:`heap_caps_get_info` returns a :cpp:class:`multi_heap_info_t` structure which contains the information from the above functions, plus some additional heap-specific data (number of allocations, etc.).
- :cpp:func:`heap_caps_print_heap_info` prints a summary to stdout of the information returned by :cpp:func:`heap_caps_get_info`.
- :cpp:func:`heap_caps_dump` and :cpp:func:`heap_caps_dump_all` will output detailed information about the structure of each block in the heap. Note that this can be large amount of output.
.. _heap-corruption:
Heap Corruption Detection
-------------------------
Heap corruption detection allows you to detect various types of heap memory errors:
- Out of bounds writes & buffer overflow.
- Writes to freed memory.
- Reads from freed or uninitialized memory,
Assertions
^^^^^^^^^^
The heap implementation (``multi_heap.c``, etc.) includes a lot of assertions which will fail if the heap memory is corrupted. To detect heap corruption most effectively, ensure that assertions are enabled in ``make menuconfig`` under ``Compiler options``.
If a heap integrity assertion fails, a line will be printed like ``CORRUPT HEAP: multi_heap.c:225 detected at 0x3ffbb71c``. The memory address which is printed is the address of the heap structure which has corrupt content.
It's also possible to manually check heap integrity by calling :cpp:func:`heap_caps_check_integrity_all` or related functions. This function checks all of requested heap memory for integrity, and can be used even if assertions are disabled. If the integrity check prints an error, it will also contain the address(es) of corrupt heap structures.
Finding Heap Corruption
^^^^^^^^^^^^^^^^^^^^^^^
Memory corruption can be one of the hardest classes of bugs to find and fix, as one area of memory can be corrupted from a totally different place. Some tips:
- A crash with a ``CORRUPT HEAP:`` message will usually include a stack trace, but this stack trace is rarely useful. The crash is the symptom of memory corruption when the system realises the heap is corrupt, but usually the corruption happened elsewhere and earlier in time.
- Increasing the Heap memory debugging `Configuration`_ level to "Light impact" or "Comprehensive" can give you a more accurate message with the first corrupt memory address.
- Adding regular calls to :cpp:func:`heap_caps_check_integrity_all` or :cpp:func:`heap_caps_check_integrity_addr` in your code will help you pin down the exact time that the corruption happened. You can move these checks around to "close in on" the section of code that corrupted the heap.
- Based on the memory address which is being corrupted, you can use :ref:`JTAG debugging <jtag-debugging-introduction>` to set a watchpoint on this address and have the CPU halt when it is written to.
- If you don't have JTAG, but you do know roughly when the corruption happens, then you can set a watchpoint in software just beforehand via :cpp:func:`esp_set_watchpoint`. A fatal exception will occur when the watchpoint triggers. For example ``esp_set_watchpoint(0, (void *)addr, 4, ESP_WATCHPOINT_STORE``. Note that watchpoints are per-CPU and are set on the current running CPU only, so if you don't know which CPU is corrupting memory then you will need to call this function on both CPUs.
- For buffer overflows, `heap tracing`_ in ``HEAP_TRACE_ALL`` mode lets you see which callers are allocating which addresses from the heap. See `Heap Tracing To Find Heap Corruption`_ for more details. If you can find the function which allocates memory with an address immediately before the address which is corrupted, this will probably be the function which overflows the buffer.
- Calling :cpp:func:`heap_caps_dump` or :cpp:func:`heap_caps_dump_all` can give an indication of what heap blocks are surrounding the corrupted region and may have overflowed/underflowed/etc.
Configuration
^^^^^^^^^^^^^
Temporarily increasing the heap corruption detection level can give more detailed information about heap corruption errors.
In ``make menuconfig``, under ``Component config`` there is a menu ``Heap memory debugging``. The setting :ref:`CONFIG_HEAP_CORRUPTION_DETECTION` can be set to one of three levels:
Basic (no poisoning)
++++++++++++++++++++
This is the default level. No special heap corruption features are enabled, but provided assertions are enabled (the default configuration) then a heap corruption error will be printed if any of the heap's internal data structures appear overwritten or corrupted. This usually indicates a buffer overrun or out of bounds write.
If assertions are enabled, an assertion will also trigger if a double-free occurs (the same memory is freed twice).
Calling :cpp:func:`heap_caps_check_integrity` in Basic mode will check the integrity of all heap structures, and print errors if any appear to be corrupted.
Light Impact
++++++++++++
At this level, heap memory is additionally "poisoned" with head and tail "canary bytes" before and after each block which is allocated. If an application writes outside the bounds of allocated buffers, the canary bytes will be corrupted and the integrity check will fail.
The head canary word is 0xABBA1234 (3412BAAB in byte order), and the tail canary word is 0xBAAD5678 (7856ADBA in byte order).
"Basic" heap corruption checks can also detect most out of bounds writes, but this setting is more precise as even a single byte overrun can be detected. With Basic heap checks, the number of overrun bytes before a failure is detected will depend on the properties of the heap.
Enabling "Light Impact" checking increases memory usage, each individual allocation will use 9 to 12 additional bytes of memory (depending on alignment).
Each time ``free()`` is called in Light Impact mode, the head and tail canary bytes of the buffer being freed are checked against the expected values.
When :cpp:func:`heap_caps_check_integrity` is called, all allocated blocks of heap memory have their canary bytes checked against the expected values.
In both cases, the check is that the first 4 bytes of an allocated block (before the buffer returned to the user) should be the word 0xABBA1234. Then the last 4 bytes of the allocated block (after the buffer returned to the user) should be the word 0xBAAD5678.
Different values usually indicate buffer underrun or overrun, respectively.
Comprehensive
+++++++++++++
This level incorporates the "light impact" detection features plus additional checks for uninitialised-access and use-after-free bugs. In this mode, all freshly allocated memory is filled with the pattern 0xCE, and all freed memory is filled with the pattern 0xFE.
Enabling "Comprehensive" detection has a substantial runtime performance impact (as all memory needs to be set to the allocation patterns each time a malloc/free completes, and the memory also needs to be checked each time.) However it allows easier detection of memory corruption bugs which are much more subtle to find otherwise. It is recommended to only enable this mode when debugging, not in production.
Crashes in Comprehensive Mode
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If an application crashes reading/writing an address related to 0xCECECECE in Comprehensive mode, this indicates it has read uninitialized memory. The application should be changed to either use calloc() (which zeroes memory), or initialize the memory before using it. The value 0xCECECECE may also be seen in stack-allocated automatic variables, because in IDF most task stacks are originally allocated from the heap and in C stack memory is uninitialized by default.
If an application crashes and the exception register dump indicates that some addresses or values were 0xFEFEFEFE, this indicates it is reading heap memory after it has been freed (a "use after free bug".) The application should be changed to not access heap memory after it has been freed.
If a call to malloc() or realloc() causes a crash because it expected to find the pattern 0xFEFEFEFE in free memory and a different pattern was found, then this indicates the app has a use-after-free bug where it is writing to memory which has already been freed.
Manual Heap Checks in Comprehensive Mode
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Calls to :cpp:func:`heap_caps_check_integrity` may print errors relating to 0xFEFEFEFE, 0xABBA1234 or 0xBAAD5678. In each case the checker is expecting to find a given pattern, and will error out if this is not found:
- For free heap blocks, the checker expects to find all bytes set to 0xFE. Any other values indicate a use-after-free bug where free memory has been incorrectly overwritten.
- For allocated heap blocks, the behaviour is the same as for `Light Impact` mode. The canary bytes 0xABBA1234 and 0xBAAD5678 are checked at the head and tail of each allocated buffer, and any variation indicates a buffer overrun/underrun.
.. _heap-tracing:
Heap Tracing
------------
Heap Tracing allows tracing of code which allocates/frees memory.
.. note::
Heap tracing "standalone" mode is currently implemented, meaning that tracing does not require any external hardware but uses internal memory to hold trace data. Heap tracing via JTAG trace port is also planned.
Heap tracing can perform two functions:
- Leak checking: find memory which is allocated and never freed.
- Heap use analysis: show all functions that are allocating/freeing memory while the trace is running.
How To Diagnose Memory Leaks
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you suspect a memory leak, the first step is to figure out which part of the program is leaking memory. Use the :cpp:func:`xPortGetFreeHeapSize`, :cpp:func:`heap_caps_get_free`, and related functions to track memory use over the life of the application. Try to narrow the leak down to a single function or sequence of functions where free memory always decreases and never recovers.
Once you've identified the code which you think is leaking:
- Under ``make menuconfig``, navigate to ``Component settings`` -> ``Heap Memory Debugging`` and set :ref:`CONFIG_HEAP_TRACING`.
- Call the function :cpp:func:`heap_trace_init_standalone` early in the program, to register a buffer which can be used to record the memory trace.
- Call the function :cpp:func:`heap_trace_start` to begin recording all mallocs/frees in the system. Call this immediately before the piece of code which you suspect is leaking memory.
- Call the function :cpp:func:`heap_trace_stop` to stop the trace once the suspect piece of code has finished executing.
- Call the function :cpp:func:`heap_trace_dump` to dump the results of the heap trace.
An example::
#include "esp_heap_trace.h"
#define NUM_RECORDS 100
static heap_trace_record_t trace_record[NUM_RECORDS]; // This buffer must be in internal RAM
...
void app_main()
{
...
ESP_ERROR_CHECK( heap_trace_init_standalone(trace_record, NUM_RECORDS) );
...
}
void some_function()
{
ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) );
do_something_you_suspect_is_leaking();
ESP_ERROR_CHECK( heap_trace_stop() );
heap_trace_dump();
...
}
The output from the heap trace will look something like this::
2 allocations trace (100 entry buffer)
32 bytes (@ 0x3ffaf214) allocated CPU 0 ccount 0x2e9b7384 caller 0x400d276d:0x400d27c1
0x400d276d: leak_some_memory at /path/to/idf/examples/get-started/blink/main/./blink.c:27
0x400d27c1: blink_task at /path/to/idf/examples/get-started/blink/main/./blink.c:52
8 bytes (@ 0x3ffaf804) allocated CPU 0 ccount 0x2e9b79c0 caller 0x400d2776:0x400d27c1
0x400d2776: leak_some_memory at /path/to/idf/examples/get-started/blink/main/./blink.c:29
0x400d27c1: blink_task at /path/to/idf/examples/get-started/blink/main/./blink.c:52
40 bytes 'leaked' in trace (2 allocations)
total allocations 2 total frees 0
(Above example output is using :doc:`IDF Monitor </get-started/idf-monitor>` to automatically decode PC addresses to their source files & line number.)
The first line indicates how many allocation entries are in the buffer, compared to its total size.
In ``HEAP_TRACE_LEAKS`` mode, for each traced memory allocation which has not already been freed a line is printed with:
- ``XX bytes`` is number of bytes allocated
- ``@ 0x...`` is the heap address returned from malloc/calloc.
- ``CPU x`` is the CPU (0 or 1) running when the allocation was made.
- ``ccount 0x...`` is the CCOUNT (CPU cycle count) register value when the allocation was mode. Is different for CPU 0 vs CPU 1.
- ``caller 0x...`` gives the call stack of the call to malloc()/free(), as a list of PC addresses.
These can be decoded to source files and line numbers, as shown above.
The depth of the call stack recorded for each trace entry can be configured in ``make menuconfig``, under ``Heap Memory Debugging`` -> ``Enable heap tracing`` -> ``Heap tracing stack depth``. Up to 10 stack frames can be recorded for each allocation (the default is 2). Each additional stack frame increases the memory usage of each ``heap_trace_record_t`` record by eight bytes.
Finally, the total number of 'leaked' bytes (bytes allocated but not freed while trace was running) is printed, and the total number of allocations this represents.
A warning will be printed if the trace buffer was not large enough to hold all the allocations which happened. If you see this warning, consider either shortening the tracing period or increasing the number of records in the trace buffer.
Heap Tracing To Find Heap Corruption
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When a region in heap is corrupted, it may be from some other part of the program which allocated memory at a nearby address.
If you have some idea at what time the corruption occured, enabling heap tracing in ``HEAP_TRACE_ALL`` mode allows you to record all of the functions which allocated memory, and the addresses where they were corrupted.
Using heap tracing in this way is very similar to memory leak detection as described above. For memory which is allocated and not freed, the output
Heap tracing can also be used to help track down heap corruption. By using
Performance Impact
^^^^^^^^^^^^^^^^^^
Enabling heap tracing in menuconfig increases the code size of your program, and has a very small negative impact on performance of heap allocation/free operations even when heap tracing is not running.
When heap tracing is running, heap allocation/free operations are substantially slower than when heap tracing is stopped. Increasing the depth of stack frames recorded for each allocation (see above) will also increase this performance impact.
False-Positive Memory Leaks
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Not everything printed by :cpp:func:`heap_trace_dump` is necessarily a memory leak. Among things which may show up here, but are not memory leaks:
- Any memory which is allocated after :cpp:func:`heap_trace_start` but then freed after :cpp:func:`heap_trace_stop` will appear in the leak dump.
- Allocations may be made by other tasks in the system. Depending on the timing of these tasks, it's quite possible this memory is freed after :cpp:func:`heap_trace_stop` is called.
- The first time a task uses stdio - for example, when it calls ``printf()`` - a lock (RTOS mutex semaphore) is allocated by the libc. This allocation lasts until the task is deleted.
- The Bluetooth, WiFi, and TCP/IP libraries will allocate heap memory buffers to handle incoming or outgoing data. These memory buffers are usually short lived, but some may be shown in the heap leak trace if the data was received/transmitted by the lower levels of the network while the leak trace was running.
- TCP connections will continue to use some memory after they are closed, because of the ``TIME_WAIT`` state. After the ``TIME_WAIT`` period has completed, this memory will be freed.
One way to differentiate between "real" and "false positive" memory leaks is to call the suspect code multiple times while tracing is running, and look for patterns (multiple matching allocations) in the heap trace output.
API Reference - Heap Tracing
----------------------------
.. include:: /_build/inc/esp_heap_trace.inc
+51
View File
@@ -0,0 +1,51 @@
.. _hooks_api_reference:
FreeRTOS Hooks
==============
Overview
--------
FreeRTOS consists of Idle Hooks and Tick Hooks which allow for application
specific funtiionality to be added to the Idle Task and Tick Interrupt. The
ESP32 is dual core in nature, hence the ESP-IDF provides its own Idle and Tick
Hooks that are dual core compatible in addition to the hooks provided by Vanilla
FreeRTOS.
Vanilla FreeRTOS Hooks
----------------------
Idle and Tick Hooks in vanilla FreeRTOS are implemented by defining
implementations for the functions ``vApplicationIdleHook`` and
``vApplicationTickHook`` respectively somewhere in the application. Vanilla
FreeRTOS will run the user defined Idle Hook every iteration of the Idle Task,
whereas the user defined Tick Hook will run once per tick interrupt (given that
there are no pended ticks).
Due to vanilla FreeRTOS being designed for single core, ``vApplicationIdleHook``
and ``vApplicationTickHook`` will be run in both cores on the ESP32. In
other words, the same Idle Hook and Tick Hook are used for both cores.
To enable the vanilla FreeRTOS hooks in ESP-IDF, :ref:`CONFIG_FREERTOS_LEGACY_HOOKS`
must be enabled in ``make menuconfig``. :ref:`CONFIG_FREERTOS_LEGACY_IDLE_HOOK`
and :ref:`CONFIG_FREERTOS_LEGACY_TICK_HOOK` should also be enabled.
ESP-IDF Idle and Tick Hooks
---------------------------
Due to the dual core nature of the ESP32, it may be necessary for some
applications to have seperate Idle Hooks for each core. Furthermore, it may
be necessary for Idle and Tick Hooks to have execute multiple functionalities
that are configurable at run time. Therefore the ESP-IDF provides it's own Idle
and Tick Hooks in addition to the hooks provided by Vanilla FreeRTOS.
The ESP-IDF Hooks do not operate in the same way as Vanilla FreeRTOS Hooks
where users provide a definition for each of the hooks. Instead, the ESP-IDF
Hooks are predefined to call a list of user registered callbacks specific to
each core. Users can register and deregister callbacks which are run on the
Idle or Tick Hook of a specific core.
API Reference
-------------
.. include:: /_build/inc/esp_freertos_hooks.inc
+23
View File
@@ -0,0 +1,23 @@
System API
**********
.. toctree::
:maxdepth: 1
FreeRTOS <freertos>
FreeRTOS Hooks <hooks>
Heap Memory Allocation <mem_alloc>
Heap Memory Debugging <heap_debug>
Interrupt Allocation <intr_alloc>
Watchdogs <wdts>
Inter-Processor Call <ipc>
High Resolution Timer <esp_timer>
Logging <log>
Application Level Tracing <app_trace>
Power Management <power_management>
Sleep Modes <sleep_modes>
Base MAC address <base_mac_address>
Over The Air Updates (OTA) <ota>
Example code for this API section is provided in :example:`system` directory of ESP-IDF examples.
+106
View File
@@ -0,0 +1,106 @@
Interrupt allocation
====================
Overview
--------
The ESP32 has two cores, with 32 interrupts each. Each interrupt has a certain priority level, most (but not all) interrupts are connected
to the interrupt mux. Because there are more interrupt sources than interrupts, sometimes it makes sense to share an interrupt in
multiple drivers. The esp_intr_alloc abstraction exists to hide all these implementation details.
A driver can allocate an interrupt for a certain peripheral by calling esp_intr_alloc (or esp_intr_alloc_sintrstatus). It can use
the flags passed to this function to set the type of interrupt allocated, specifying a specific level or trigger method. The
interrupt allocation code will then find an applicable interrupt, use the interrupt mux to hook it up to the peripheral, and
install the given interrupt handler and ISR to it.
This code has two different types of interrupts it handles differently: Shared interrupts and non-shared interrupts. The simplest
of the two are non-shared interrupts: a separate interrupt is allocated per esp_intr_alloc call and this interrupt is solely used for
the peripheral attached to it, with only one ISR that will get called. Shared interrupts can have multiple peripherals triggering
it, with multiple ISRs being called when one of the peripherals attached signals an interrupt. Thus, ISRs that are intended for shared
interrupts should check the interrupt status of the peripheral they service in order to see if any action is required.
Non-shared interrupts can be either level- or edge-triggered. Shared interrupts can
only be level interrupts (because of the chance of missed interrupts when edge interrupts are
used.)
(The logic behind this: DevA and DevB share an int. DevB signals an int. Int line goes high. ISR handler
calls code for DevA -> does nothing. ISR handler calls code for DevB, but while doing that,
DevA signals an int. ISR DevB is done, clears int for DevB, exits interrupt code. Now an
interrupt for DevA is still pending, but because the int line never went low (DevA kept it high
even when the int for DevB was cleared) the interrupt is never serviced.)
Multicore issues
----------------
Peripherals that can generate interrupts can be divided in two types:
- External peripherals, within the ESP32 but outside the Xtensa cores themselves. Most ESP32 peripherals are of this type.
- Internal peripherals, part of the Xtensa CPU cores themselves.
Interrupt handling differs slightly between these two types of peripherals.
Internal peripheral interrupts
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Each Xtensa CPU core has its own set of six internal peripherals:
- Three timer comparators
- A performance monitor
- Two software interrupts.
Internal interrupt sources are defined in esp_intr_alloc.h as ``ETS_INTERNAL_*_INTR_SOURCE``.
These peripherals can only be configured from the core they are associated with. When generating an interrupt,
the interrupt they generate is hard-wired to their associated core; it's not possible to have e.g. an internal
timer comparator of one core generate an interrupt on another core. That is why these sources can only be managed
using a task running on that specific core. Internal interrupt sources are still allocatable using esp_intr_alloc
as normal, but they cannot be shared and will always have a fixed interrupt level (namely, the one associated in
hardware with the peripheral).
External Peripheral Interrupts
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The remaining interrupt sources are from external peripherals. These are defined in soc/soc.h as ``ETS_*_INTR_SOURCE``.
Non-internal interrupt slots in both CPU cores are wired to an interrupt multiplexer, which can be used to
route any external interrupt source to any of these interrupt slots.
- Allocating an external interrupt will always allocate it on the core that does the allocation.
- Freeing an external interrupt must always happen on the same core it was allocated on.
- Disabling and enabling external interrupts from another core is allowed.
- Multiple external interrupt sources can share an interrupt slot by passing ``ESP_INTR_FLAG_SHARED`` as a flag to esp_intr_alloc().
Care should be taken when calling esp_intr_alloc() from a task which is not pinned to a core. During task switching, these tasks can migrate between cores. Therefore it is impossible to tell which CPU the interrupt is allocated on, which makes it difficult to free the interrupt handle and may also cause debugging difficulties. It is advised to use xTaskCreatePinnedToCore() with a specific CoreID argument to create tasks that will allocate interrupts. In the case of internal interrupt sources, this is required.
IRAM-Safe Interrupt Handlers
----------------------------
The ``ESP_INTR_FLAG_IRAM`` flag registers an interrupt handler that always runs from IRAM (and reads all its data from DRAM), and therefore does not need to be disabled during flash erase and write operations.
This is useful for interrupts which need a guaranteed minimum execution latency, as flash write and erase operations can be slow (erases can take tens or hundreds of milliseconds to complete).
It can also be useful to keep an interrupt handler in IRAM if it is called very frequently, to avoid flash cache misses.
Refer to the :ref:`SPI flash API documentation <iram-safe-interrupt-handlers>` for more details.
Multiple Handlers Sharing A Source
----------------------------------
Several handlers can be assigned to a same source, given that all handlers are allocated using the ``ESP_INTR_FLAG_SHARED`` flag.
They'll be all allocated to the interrupt, which the source is attached to, and called sequentially when the source is active.
The handlers can be disabled and freed individually. The source is attached to the interrupt (enabled), if one or more handlers are enabled, otherwise detached.
A handler will never be called when disabled, while **its source may still be triggered** if any one of its handler enabled.
Sources attached to non-shared interrupt do not support this feature.
Though the framework support this feature, you have to use it *very carefully*. There usually exist 2 ways to stop a interrupt from being triggered: *disable the sourse* or *mask peripheral interrupt status*.
IDF only handles the enabling and disabling of the source itself, leaving status and mask bits to be handled by users. **Status bits should always be masked before the handler responsible for it is disabled,
or the status should be handled in other enabled interrupt properly**. You may leave some status bits unhandled if you just disable one of all the handlers without mask the status bits, which causes the interrupt being triggered infinitely,
and finally a system crash.
API Reference
-------------
.. include:: /_build/inc/esp_intr_alloc.inc
+43
View File
@@ -0,0 +1,43 @@
Inter-Processor Call
====================
Overview
--------
Due to the dual core nature of the ESP32, there are instances where a certain
function must be run in the context of a particular core (e.g. allocating
ISR to an interrupt source of a particular core). The IPC (Inter-Processor
Call) feature allows for the execution of functions on a particular CPU.
A given function can be executed on a particular core by calling
:cpp:func:`esp_ipc_call` or :cpp:func:`esp_ipc_call_blocking`. IPC is
implemented via two high priority FreeRTOS tasks pinned to each CPU known as
the IPC Tasks. The two IPC Tasks remain inactive (blocked) until
:cpp:func:`esp_ipc_call` or :cpp:func:`esp_ipc_call_blocking` is called. When
an IPC Task of a particular core is unblocked, it will preempt the current
running task on that core and execute a given function.
Usage
-----
:cpp:func:`esp_ipc_call` unblocks the IPC task on a particular core to execute
a given function. The task that calls :cpp:func:`esp_ipc_call` will be blocked
until the IPC Task begins execution of the given function.
:cpp:func:`esp_ipc_call_blocking` is similar but will block the calling task
until the IPC Task has completed execution of the given function.
Functions executed by IPCs must be functions of type
`void func(void *arg)`. To run more complex functions which require a larger
stack, the IPC tasks' stack size can be configured by modifying
:ref:`CONFIG_IPC_TASK_STACK_SIZE` in `menuconfig`. The IPC API is protected by a
mutex hence simultaneous IPC calls are not possible.
Care should taken to avoid deadlock when writing functions to be executed by
IPC, especially when attempting to take a mutex within the function.
API Reference
-------------
.. include:: /_build/inc/esp_ipc.inc
+18
View File
@@ -0,0 +1,18 @@
.. include:: ../../../../components/log/README.rst
Application Example
-------------------
Log library is commonly used by most of esp-idf components and examples. For demonstration of log functionality check :idf:`examples` folder of `espressif/esp-idf <https://github.com/espressif/esp-idf>`_ repository, that among others, contains the following examples:
* :example:`system/ota`
* :example:`storage/sd_card`
* :example:`protocols/https_request`
API Reference
-------------
.. include:: /_build/inc/esp_log.inc
@@ -0,0 +1,76 @@
Heap Memory Allocation
======================
Overview
--------
The ESP32 has multiple types of RAM. Internally, there's IRAM, DRAM as well as RAM that can be used as both. It's also
possible to connect external SPI RAM to the ESP32 - external RAM can be integrated into the ESP32's memory map using
the flash cache.
For most purposes, the standard libc ``malloc()`` and ``free()`` functions can be used for heap allocation without any
issues.
However, in order to fully make use of all of the memory types and their characteristics, esp-idf also has a
capabilities-based heap memory allocator. If you want to have memory with certain properties (for example, DMA-capable
memory, or executable memory), you can create an OR-mask of the required capabilities and pass that to
:cpp:func:`heap_caps_malloc`. For instance, the standard ``malloc()`` implementation internally allocates memory via
``heap_caps_malloc(size, MALLOC_CAP_8BIT)`` in order to get data memory that is byte-addressable.
Because malloc uses this allocation system as well, memory allocated using :cpp:func:`heap_caps_malloc` can be freed by calling
the standard ``free()`` function.
The "soc" component contains a list of memory regions for the chip, along with the type of each memory (aka its tag) and the associated capabilities for that memory type. On startup, a separate heap is initialised for each contiguous memory region. The capabilities-based allocator chooses the best heap for each allocation, based on the requested capabilities.
Special Uses
------------
DMA-Capable Memory
^^^^^^^^^^^^^^^^^^
Use the MALLOC_CAP_DMA flag to allocate memory which is suitable for use with hardware DMA engines (for example SPI and I2S). This capability flag excludes any external PSRAM.
32-Bit Accessible Memory
^^^^^^^^^^^^^^^^^^^^^^^^
If a certain memory structure is only addressed in 32-bit units, for example an array of ints or pointers, it can be
useful to allocate it with the MALLOC_CAP_32BIT flag. This also allows the allocator to give out IRAM memory; something
which it can't do for a normal malloc() call. This can help to use all the available memory in the ESP32.
Memory allocated with MALLOC_CAP_32BIT can *only* be accessed via 32-bit reads and writes, any other type of access will
generate a fatal LoadStoreError exception.
API Reference - Heap Allocation
-------------------------------
.. include:: /_build/inc/esp_heap_caps.inc
Heap Tracing & Debugging
------------------------
The following features are documented on the :doc:`Heap Memory Debugging </api-reference/system/heap_debug>` page:
- :ref:`Heap Information <heap-information>` (free space, etc.)
- :ref:`Heap Corruption Detection <heap-corruption>`
- :ref:`Heap Tracing <heap-tracing>` (memory leak detection, monitoring, etc.)
API Reference - Initialisation
------------------------------
.. include:: /_build/inc/esp_heap_caps_init.inc
Implementation Notes
--------------------
Knowledge about the regions of memory in the chip comes from the "soc" component, which contains memory layout information for the chip.
Each contiguous region of memory contains its own memory heap. The heaps are created using the `multi_heap <API Reference - Multi Heap API>`_ functionality. multi_heap allows any contiguous region of memory to be used as a heap.
The heap capabilities allocator uses knowledge of the memory regions to initialize each individual heap. When you call a function in the heap capabilities API, it will find the most appropriate heap for the allocation (based on desired capabilities, available space, and preferences for each region's use) and then call the multi_heap function to use the heap situation in that particular region.
API Reference - Multi Heap API
------------------------------
(Note: The multi heap API is used internally by the heap capabilities allocator. Most IDF programs will never need to call this API directly.)
.. include:: /_build/inc/multi_heap.inc
+52
View File
@@ -0,0 +1,52 @@
Over The Air Updates (OTA)
==========================
OTA Process Overview
--------------------
The OTA update mechanism allows a device to update itself based on data received while the normal firmware is running
(for example, over WiFi or Bluetooth.)
OTA requires configuring the :doc:`Partition Table <../../api-guides/partition-tables>` of the device with at least two "OTA app slot"
partitions (ie `ota_0` and `ota_1`) and an "OTA Data Partition".
The OTA operation functions write a new app firmware image to whichever OTA app slot is not currently being used for
booting. Once the image is verified, the OTA Data partition is updated to specify that this image should be used for the
next boot.
.. _ota_data_partition:
OTA Data Partition
------------------
An OTA data partition (type ``data``, subtype ``ota``) must be included in the :doc:`Partition Table <../../api-guides/partition-tables>`
of any project which uses the OTA functions.
For factory boot settings, the OTA data partition should contain no data (all bytes erased to 0xFF). In this case the
esp-idf software bootloader will boot the factory app if it is present in the the partition table. If no factory app is
included in the partition table, the first available OTA slot (usually ``ota_0``) is booted.
After the first OTA update, the OTA data partition is updated to specify which OTA app slot partition should be booted next.
The OTA data partition is two flash sectors (0x2000 bytes) in size, to prevent problems if there is a power failure
while it is being written. Sectors are independently erased and written with matching data, and if they disagree a
counter field is used to determine which sector was written more recently.
See also
--------
* :doc:`Partition Table documentation <../../api-guides/partition-tables>`
* :doc:`Lower-Level SPI Flash/Partition API <../storage/spi_flash>`
Application Example
-------------------
End-to-end example of OTA firmware update workflow: :example:`system/ota`.
API Reference
-------------
.. include:: /_build/inc/esp_ota_ops.inc
@@ -0,0 +1,123 @@
Power Management
================
Overview
--------
Power management algorithm included in ESP-IDF can adjust APB frequency, CPU frequency, and put the chip into light sleep mode to run the application
at smallest possible power consumption, given the requirements of application components.
Application components can express their requirements by creating and acquiring power management locks.
For instance, a driver for a peripheral clocked from APB can request the APB frequency to be set to 80 MHz, for the duration while the peripheral is used. Another example is that the RTOS will request the CPU to run at the highest configured frequency while there are tasks ready to run. Yet another example is a peripheral driver which needs interrupts to be enabled. Such driver can request light sleep to be disabled.
Naturally, requesting higher APB or CPU frequency or disabling light sleep causes higher current consumption. Components should try to limit usage of power management locks to the shortest amount of time possible.
Configuration
-------------
Power management can be enabled at compile time, using :ref:`CONFIG_PM_ENABLE` option.
Enabling power management features comes at the cost of increased interrupt latency. Extra latency depends on a number of factors, among which are CPU frequency, single/dual core mode, whether frequency switch needs to be performed or not. Minimal extra latency is 0.2us (when CPU frequency is 240MHz, and frequency scaling is not enabled), maximum extra latency is 40us (when frequency scaling is enabled, and a switch from 40MHz to 80MHz is performed on interrupt entry).
Dynamic frequency scaling (DFS) can be enabled in the application by calling :cpp:func:`esp_pm_configure` function. Its argument is a structure defining frequency scaling settings (for ESP32, minimum and maximum CPU frequencies). Alternatively, :ref:`CONFIG_PM_DFS_INIT_AUTO` option can be enabled in menuconfig. If enabled, maximal CPU frequency is determined by :ref:`CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ` setting, and minimal CPU frequency is set to the XTAL frequency.
.. note::
:cpp:func:`esp_pm_configure` function also has provisions for enabling automatic light sleep mode. However this feature is not fully supported yet, so `esp_pm_configure` will return an `ESP_ERR_NOT_SUPPORTED` if automatic light sleep is requested.
Power Management Locks
----------------------
As mentioned in the overview, applications can acquire/release locks to control the power management algorithm. When application takes a lock, power management algorithm operation is restricted in a way described below, for each lock. When the lock is released, such restriction is removed.
Different parts of the application can take the same lock. In this case, lock mush be released the same number of times as it was acquired, in order for power managment algorithm to resume.
In ESP32, three types of locks are supported:
``ESP_PM_CPU_FREQ_MAX``
Requests CPU frequency to be at the maximal value set via :cpp:func:`esp_pm_configure`. For ESP32, this value can be set to 80, 160, or 240MHz.
``ESP_PM_APB_FREQ_MAX``
Requests APB frequency to be at the maximal supported value. For ESP32, this is 80 MHz.
``ESP_PM_NO_LIGHT_SLEEP``
Prevents automatic light sleep from being used. Note: currently taking this lock has no effect, as automatic light sleep is never used.
Power Management Algorithm for the ESP32
----------------------------------------
When dynamic frequency scaling is enabled, CPU frequency will be switched as follows:
- If maximal CPU frequency (set using :cpp:func:`esp_pm_configure` or :ref:`CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ`) is 240 MHz:
1. When ``ESP_PM_CPU_FREQ_MAX`` or ``ESP_PM_APB_FREQ_MAX`` locks are acquired, CPU frequency will be 240 MHz, and APB frequency will be 80 MHz.
2. Otherwise, frequency will be switched to the minimal value set using :cpp:func:`esp_pm_configure` (usually, XTAL).
- If maximal CPU frequency is 160 MHz:
1. When ``ESP_PM_CPU_FREQ_MAX`` is acquired, CPU frequency is set to 160 MHz, and APB frequency to 80 MHz.
2. When ``ESP_PM_CPU_FREQ_MAX`` is not acquired, but ``ESP_PM_APB_FREQ_MAX`` is, CPU and APB frequencies are set to 80 MHz.
3. Otherwise, frequency will be switched to the minimal value set using :cpp:func:`esp_pm_configure` (usually, XTAL).
- If maximal CPU frequency is 80 MHz:
1. When ``ESP_PM_CPU_FREQ_MAX`` or ``ESP_PM_APB_FREQ_MAX`` locks are acquired, CPU and APB frequencies will be 80 MHz.
2. Otherwise, frequency will be switched to the minimal value set using :cpp:func:`esp_pm_configure` (usually, XTAL).
Dynamic Frequency Scaling and Peripheral Drivers
------------------------------------------------
When DFS is enabled, APB frequency can be changed several times within a single RTOS tick. Some peripherals can work normally even when APB frequency changes; some can not.
The following peripherals can work even when APB frequency is changing:
- UART: if REF_TICK is used as clock source (see `use_ref_tick` member of `uart_config_t`).
- LEDC: if REF_TICK is used as clock source (see :cpp:func:`ledc_timer_config` function).
- RMT: if REF_TICK is used as clock source. Currently the driver does not support REF_TICK, but it can be enabled by clearing ``RMT_REF_ALWAYS_ON_CHx`` bit for the respective channel.
Currently, the following peripheral drivers are aware of DFS and will use ``ESP_PM_APB_FREQ_MAX`` lock for the duration of the transaction:
- SPI master
- SDMMC
The following drivers will hold ``ESP_PM_APB_FREQ_MAX`` lock while the driver is enabled:
- SPI slave — between calls to :cpp:func:`spi_slave_initialize` and cpp:func:`spi_slave_free`.
- Ethernet — between calls to :cpp:func:`esp_eth_enable` and :cpp:func:`esp_eth_disable`.
- WiFi — between calls to :cpp:func:`esp_wifi_start` and :cpp:func:`esp_wifi_stop`. If modem sleep is enabled, lock will be released for thte periods of time when radio is disabled.
- Bluetooth — between calls to :cpp:func:`esp_bt_controller_enable` and :cpp:func:`esp_bt_controller_disable`.
The following peripheral drivers are not aware of DFS yet. Applications need to acquire/release locks when necessary:
- I2C
- I2S
- MCPWM
- PCNT
- Sigma-delta
- Timer group
API Reference
-------------
.. include:: /_build/inc/esp_pm.inc
.. include:: /_build/inc/pm.inc
@@ -0,0 +1,155 @@
Sleep Modes
===========
Overview
--------
ESP32 is capable of light sleep and deep sleep power saving modes.
In light sleep mode, digital peripherals, most of the RAM, and CPUs are clock-gated, and supply voltage is reduced. Upon exit from light sleep, peripherals and CPUs resume operation, their internal state is preserved.
In deep sleep mode, CPUs, most of the RAM, and all the digital peripherals which are clocked from APB_CLK are powered off. The only parts of the chip which can still be powered on are: RTC controller, RTC peripherals (including ULP coprocessor), and RTC memories (slow and fast).
Wakeup from deep and light sleep modes can be done using several sources. These sources can be combined, in this case the chip will wake up when any one of the sources is triggered. Wakeup sources can be enabled using ``esp_sleep_enable_X_wakeup`` APIs. Next section describes these APIs in detail. Wakeup sources can be configured at any moment before entering light or deep sleep mode.
Additionally, the application can force specific powerdown modes for the RTC peripherals and RTC memories using ``esp_sleep_pd_config`` API.
Once wakeup sources are configured, application can enter sleep mode using ``esp_light_sleep_start`` or ``esp_deep_sleep_start`` APIs. At this point the hardware will be configured according to the requested wakeup sources, and RTC controller will either power down or power off the CPUs and digital peripherals.
WiFi/BT and sleep modes
-----------------------
In deep sleep mode, wireless peripherals are powered down. Before entering sleep mode, applications must disable WiFi and BT using appropriate calls ( ``esp_bluedroid_disable``, ``esp_bt_controller_disable``, ``esp_wifi_stop``).
WiFi can coexist with light sleep mode, allowing the chip to go into light sleep mode when there is no network activity, and waking up the chip from light sleep mode when required. However **APIs described in this section can not be used for that purpose**. ``esp_light_sleep_start`` forces the chip to enter light sleep mode, regardless of whether WiFi is active or not. Automatic entry into light sleep mode, coordinated with WiFi driver, will be supported using a separate set of APIs.
Wakeup sources
--------------
Timer
^^^^^
RTC controller has a built in timer which can be used to wake up the chip after a predefined amount of time. Time is specified at microsecond precision, but the actual resolution depends on the clock source selected for RTC SLOW_CLK. See chapter "Reset and Clock" of the ESP32 Technical Reference Manual for details about RTC clock options.
This wakeup mode doesn't require RTC peripherals or RTC memories to be powered on during sleep.
The following function can be used to enable deep sleep wakeup using a timer.
.. doxygenfunction:: esp_sleep_enable_timer_wakeup
Touch pad
^^^^^^^^^
RTC IO module contains logic to trigger wakeup when a touch sensor interrupt occurs. You need to configure the touch pad interrupt before the chip starts deep sleep.
Revisions 0 and 1 of the ESP32 only support this wakeup mode when RTC peripherals are not forced to be powered on (i.e. ESP_PD_DOMAIN_RTC_PERIPH should be set to ESP_PD_OPTION_AUTO).
.. doxygenfunction:: esp_sleep_enable_touchpad_wakeup
External wakeup (ext0)
^^^^^^^^^^^^^^^^^^^^^^
RTC IO module contains logic to trigger wakeup when one of RTC GPIOs is set to a predefined logic level. RTC IO is part of RTC peripherals power domain, so RTC peripherals will be kept powered on during deep sleep if this wakeup source is requested.
Because RTC IO module is enabled in this mode, internal pullup or pulldown resistors can also be used. They need to be configured by the application using ``rtc_gpio_pullup_en`` and ``rtc_gpio_pulldown_en`` functions, before calling ``esp_sleep_start``.
In revisions 0 and 1 of the ESP32, this wakeup source is incompatible with ULP and touch wakeup sources.
.. warning:: After wake up from sleep, IO pad used for wakeup will be configured as RTC IO. Before using this pad as digital GPIO, reconfigure it using ``rtc_gpio_deinit(gpio_num)`` function.
.. doxygenfunction:: esp_sleep_enable_ext0_wakeup
External wakeup (ext1)
^^^^^^^^^^^^^^^^^^^^^^
RTC controller contains logic to trigger wakeup using multiple RTC GPIOs. One of the two logic functions can be used to trigger wakeup:
- wake up if any of the selected pins is high (``ESP_EXT1_WAKEUP_ANY_HIGH``)
- wake up if all the selected pins are low (``ESP_EXT1_WAKEUP_ALL_LOW``)
This wakeup source is implemented by the RTC controller. As such, RTC peripherals and RTC memories can be powered down in this mode. However, if RTC peripherals are powered down, internal pullup and pulldown resistors will be disabled. To use internal pullup or pulldown resistors, request RTC peripherals power domain to be kept on during sleep, and configure pullup/pulldown resistors using ``rtc_gpio_`` functions, before entering sleep::
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
gpio_pullup_dis(gpio_num);
gpio_pulldown_en(gpio_num);
.. warning:: After wake up from sleep, IO pad(s) used for wakeup will be configured as RTC IO. Before using these pads as digital GPIOs, reconfigure them using ``rtc_gpio_deinit(gpio_num)`` function.
The following function can be used to enable this wakeup mode:
.. doxygenfunction:: esp_sleep_enable_ext1_wakeup
.. doxygenenum:: esp_sleep_ext1_wakeup_mode_t
ULP coprocessor wakeup
^^^^^^^^^^^^^^^^^^^^^^
ULP coprocessor can run while the chip is in sleep mode, and may be used to poll sensors, monitor ADC or touch sensor values, and wake up the chip when a specific event is detected. ULP coprocessor is part of RTC peripherals power domain, and it runs the program stored in RTC slow memeory. RTC slow memory will be powered on during sleep if this wakeup mode is requested. RTC peripherals will be automatically powered on before ULP coprocessor starts running the program; once the program stops running, RTC peripherals are automatically powered down again.
Revisions 0 and 1 of the ESP32 only support this wakeup mode when RTC peripherals are not forced to be powered on (i.e. ESP_PD_DOMAIN_RTC_PERIPH should be set to ESP_PD_OPTION_AUTO).
The following function can be used to enable this wakeup mode:
.. doxygenfunction:: esp_sleep_enable_ulp_wakeup
Power-down of RTC peripherals and memories
------------------------------------------
By default, ``esp_deep_sleep_start`` and ``esp_light_sleep_start`` functions will power down all RTC power domains which are not needed by the enabled wakeup sources. To override this behaviour, ``esp_sleep_pd_config`` function is provided.
Note: in revision 0 of the ESP32, RTC fast memory will always be kept enabled in deep sleep, so that the deep sleep stub can run after reset. This can be overriden, if the application doesn't need clean reset behaviour after deep sleep.
If some variables in the program are placed into RTC slow memory (for example, using ``RTC_DATA_ATTR`` attribute), RTC slow memory will be kept powered on by default. This can be overriden using ``esp_sleep_pd_config`` function, if desired.
.. doxygenfunction:: esp_sleep_pd_config
.. doxygenenum:: esp_sleep_pd_domain_t
.. doxygenenum:: esp_sleep_pd_option_t
Entering light sleep
--------------------
The following function can be used to enter light sleep once wakeup sources are configured. It is also possible to go into light sleep with no wakeup sources configured, in this case the chip will be in light sleep mode indefinetly, until external reset is applied.
.. doxygenfunction:: esp_light_sleep_start
Entering deep sleep
-------------------
The following function can be used to enter deep sleep once wakeup sources are configured. It is also possible to go into deep sleep with no wakeup sources configured, in this case the chip will be in deep sleep mode indefinetly, until external reset is applied.
.. doxygenfunction:: esp_deep_sleep_start
Configuring IOs
---------------
Some ESP32 IOs have internal pullups or pulldowns, which are enabled by default. If an external circuit drives this pin in deep sleep mode, current consumption may increase due to current flowing through these pullups and pulldowns.
To isolate a pin, preventing extra current draw, call :cpp:func:`rtc_gpio_isolate` function.
For example, on ESP32-WROVER module, GPIO12 is pulled up externally. GPIO12 also has an internal pulldown in the ESP32 chip. This means that in deep sleep, some current will flow through these external and internal resistors, increasing deep sleep current above the minimal possible value.
Add the following code before :cpp:func:`esp_deep_sleep_start` to remove this extra current:
```c++
rtc_gpio_isolate(GPIO_NUM_12);
```
Checking sleep wakeup cause
---------------------------
The following function can be used to check which wakeup source has triggered wakeup from sleep mode. For touch pad and ext1 wakeup sources, it is possible to identify pin or touch pad which has caused wakeup.
.. doxygenfunction:: esp_sleep_get_wakeup_cause
.. doxygenenum:: esp_sleep_wakeup_cause_t
.. doxygenfunction:: esp_sleep_get_touchpad_wakeup_status
.. doxygenfunction:: esp_sleep_get_ext1_wakeup_status
Application Example
-------------------
Implementation of basic functionality of deep sleep is shown in :example:`protocols/sntp` example, where ESP module is periodically waken up to retrive time from NTP server.
More extensive example in :example:`system/deep_sleep` illustrates usage of various deep sleep wakeup triggers and ULP coprocessor programming.
+103
View File
@@ -0,0 +1,103 @@
Watchdogs
=========
Overview
--------
The ESP-IDF has support for two types of watchdogs: The Interrupt Watchdog Timer
and the Task Watchdog Timer (TWDT). The Interrupt Watchdog Timer and the TWDT
can both be enabled using ``make menuconfig``, however the TWDT can also be
enabled during runtime. The Interrupt Watchdog is responsible for detecting
instances where FreeRTOS task switching is blocked for a prolonged period of
time. The TWDT is responsible for detecting instances of tasks running without
yielding for a prolonged period.
Interrupt watchdog
^^^^^^^^^^^^^^^^^^
The interrupt watchdog makes sure the FreeRTOS task switching interrupt isn't blocked for a long time. This
is bad because no other tasks, including potentially important ones like the WiFi task and the idle task,
can't get any CPU runtime. A blocked task switching interrupt can happen because a program runs into an
infinite loop with interrupts disabled or hangs in an interrupt.
The default action of the interrupt watchdog is to invoke the panic handler. causing a register dump and an opportunity
for the programmer to find out, using either OpenOCD or gdbstub, what bit of code is stuck with interrupts
disabled. Depending on the configuration of the panic handler, it can also blindly reset the CPU, which may be
preferred in a production environment.
The interrupt watchdog is built around the hardware watchdog in timer group 1. If this watchdog for some reason
cannot execute the NMI handler that invokes the panic handler (e.g. because IRAM is overwritten by garbage),
it will hard-reset the SOC.
Task Watchdog Timer
^^^^^^^^^^^^^^^^^^^
The Task Watchdog Timer (TWDT) is responsible for detecting instances of tasks
running for a prolonged period of time without yielding. This is a symptom of
CPU starvation and is usually caused by a higher priority task looping without
yielding to a lower-priority task thus starving the lower priority task from
CPU time. This can be an indicator of poorly written code that spinloops on a
peripheral, or a task that is stuck in an infinite loop.
By default the TWDT will watch the Idle Tasks of each CPU, however any task can
elect to be watched by the TWDT. Each watched task must 'reset' the TWDT
periodically to indicate that they have been allocated CPU time. If a task does
not reset within the TWDT timeout period, a warning will be printed with
information about which tasks failed to reset the TWDT in time and which
tasks are currently running on the ESP32 CPUs and.
The TWDT is built around the Hardware Watchdog Timer in Timer Group 0. The TWDT
can be initialized by calling :cpp:func:`esp_task_wdt_init` which will configure
the hardware timer. A task can then subscribe to the TWDT using
:cpp:func:`esp_task_wdt_add` in order to be watched. Each subscribed task must
periodically call :cpp:func:`esp_task_wdt_reset` to reset the TWDT. Failure by
any subscribed tasks to periodically call :cpp:func:`esp_task_wdt_reset`
indicates that one or more tasks have been starved of CPU time or are stuck in a
loop somewhere.
A watched task can be unsubscribed from the TWDT using
:cpp:func:`esp_task_wdt_delete()`. A task that has been unsubscribed should no
longer call :cpp:func:`esp_task_wdt_reset`. Once all tasks have unsubscribed
form the TWDT, the TWDT can be deinitialized by calling
:cpp:func:`esp_task_wdt_deinit()`.
By default :ref:`CONFIG_TASK_WDT` in ``make menuconfig`` will be enabled causing
the TWDT to be initialized automatically during startup. Likewise
:ref:`CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0` and
:ref:`CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1` are also enabled by default causing
the two Idle Tasks to be subscribed to the TWDT during startup.
JTAG and watchdogs
^^^^^^^^^^^^^^^^^^
While debugging using OpenOCD, the CPUs will be halted every time a breakpoint
is reached. However if the watchdog timers continue to run when a breakpoint is
encountered, they will eventually trigger a reset making it very difficult to
debug code. Therefore OpenOCD will disable the hardware timers of both the
interrupt and task watchdogs at every breakpoint. Moreover, OpenOCD will not
reenable them upon leaving the breakpoint. This means that interrupt watchdog
and task watchdog functionality will essentially be disabled. No warnings or
panics from either watchdogs will be generated when the ESP32 is connected to
OpenOCD via JTAG.
Interrupt Watchdog API Reference
--------------------------------
Header File
^^^^^^^^^^^
* :component_file:`esp32/include/esp_int_wdt.h`
Functions
---------
.. doxygenfunction:: esp_int_wdt_init
Task Watchdog API Reference
----------------------------
A full example using the Task Watchdog is available in esp-idf: :example:`system/task_watchdog`
.. include:: /_build/inc/esp_task_wdt.inc
+105
View File
@@ -0,0 +1,105 @@
API Documentation Template
==========================
.. note::
*INSTRUCTIONS*
1. Use this file (:idf_file:`docs/api-reference/template.rst`) as a template to document API.
2. Change the file name to the name of the header file that represents documented API.
3. Include respective files with descriptions from the API folder using ``..include::``
* README.rst
* example.rst
* ...
4. Optionally provide description right in this file.
5. Once done, remove all instructions like this one and any superfluous headers.
Overview
--------
.. note::
*INSTRUCTIONS*
1. Provide overview where and how this API may be used.
2. Where applicable include code snippets to illustrate functionality of particular functions.
3. To distinguish between sections, use the following `heading levels <http://www.sphinx-doc.org/en/stable/rest.html#sections>`_:
* ``#`` with overline, for parts
* ``*`` with overline, for chapters
* ``=``, for sections
* ``-``, for subsections
* ``^``, for subsubsections
* ``"``, for paragraphs
Application Example
-------------------
.. note::
*INSTRUCTIONS*
1. Prepare one or more practical examples to demonstrate functionality of this API.
2. Each example should follow pattern of projects located in ``esp-idf/examples/`` folder.
3. Place example in this folder complete with ``README.md`` file.
4. Provide overview of demonstrated functionality in ``README.md``.
5. With good overview reader should be able to understand what example does without opening the source code.
6. Depending on complexity of example, break down description of code into parts and provide overview of functionality of each part.
7. Include flow diagram and screenshots of application output if applicable.
8. Finally add in this section synopsis of each example together with link to respective folder in ``esp-idf/examples/``.
API Reference
-------------
.. highlight:: none
.. note::
*INSTRUCTIONS*
1. This repository provides for automatic update of API reference documentation using :doc:`code markup retrieved by Doxygen from header files <../contribute/documenting-code>`.
2. Update is done on each documentation build by invoking script :idf_file:`docs/gen-dxd.py` for all header files listed in the ``INPUT`` statement of :idf_file:`docs/Doxyfile`.
3. Each line of the ``INPUT`` statement (other than a comment that begins with ``##``) contains a path to header file ``*.h`` that will be used to generate corresponding ``*.inc`` files::
##
## Wi-Fi - API Reference
##
../components/esp32/include/esp_wifi.h \
../components/esp32/include/esp_smartconfig.h \
4. The ``*.inc`` files contain formatted reference of API members generated automatically on each documentation build. All ``*.inc`` files are placed in Sphinx ``_build`` directory. To see directives generated for e.g. ``esp_wifi.h``, run ``python gen-dxd.py esp32/include/esp_wifi.h``.
5. To show contents of ``*.inc`` file in documentation, include it as follows::
.. include:: /_build/inc/esp_wifi.inc
For example see :idf_file:`docs/api-reference/wifi/esp_wifi.rst`
6. Optionally, rather that using ``*.inc`` files, you may want to describe API in you own way. See :idf_file:`docs/api-reference/system/deep_sleep.rst` for example.
Below is the list of common ``.. doxygen...::`` directives:
* Functions - ``.. doxygenfunction:: name_of_function``
* Unions -``.. doxygenunion:: name_of_union``
* Structures -``.. doxygenstruct:: name_of_structure`` together with ``:members:``
* Macros - ``.. doxygendefine:: name_of_define``
* Type Definitions - ``.. doxygentypedef:: name_of_type``
* Enumerations - ``.. doxygenenum:: name_of_enumeration``
See `Breathe documentation <https://breathe.readthedocs.io/en/latest/directives.html>`_ for additional information.
To provide a link to header file, use the :ref:`link custom role <link-custom-roles>` as follows::
* :component_file:`path_to/header_file.h`
7. In any case, to generate API reference, the file :idf_file:`docs/Doxyfile` should be updated with paths to ``*.h`` headers that are being documented.
8. When changes are committed and documentation is build, check how this section has been rendered. :doc:`Correct annotations <../contribute/documenting-code>` in respective header files, if required.
+103
View File
@@ -0,0 +1,103 @@
ESP-NOW
=======
Overview
--------
ESP-NOW is a kind of connectionless WiFi communication protocol which is defined by Espressif. In ESP-NOW, application data is
encapsulated in vendor-specific action frame and then transmitted from one WiFi device to another without connection.
CTR with CBC-MAC Protocol(CCMP) is used to protect the action frame for security. ESP-NOW is widely used in smart light, remote
controlling, sensor, etc.
Frame Format
------------
ESP-NOW uses vendor-specific action frame to transmit ESP-NOW data. The format of vendor-specific action frame is as follows:
.. highlight:: none
::
----------------------------------------------------------------------------------------
| MAC Header | Category Code | Organization Identifier | Vendor Specific Content | FCS |
----------------------------------------------------------------------------------------
1 byte 3 bytes 7~255 bytes
- Category Code: The Category field is set to the value(127) indicating the vendor-specific category.
- Organization Identifier: The Organization Identifier contains a unique identifier(0x18fe34) which is the first three bytes
of MAC address applied by Espressif.
- Vendor Specific Content: The Vendor Specific Content contains vendor-specific field as follows:
.. highlight:: none
::
-------------------------------------------------------------------------------
| Element ID | Length | Organization Identifier | Type | Version | Body |
-------------------------------------------------------------------------------
1 byte 1 byte 3 bytes 1 byte 1 byte 0~250 bytes
- Element ID: The Element ID field is set to the value(221) indicating the vendor-specific element.
- Length: The length is the total length of Organization Identifier, Type, Version and Body.
- Organization Identifier: The Organization Identifier contains a unique identifier(0x18fe34) which is the first three bytes
of MAC address applied by Espressif.
- Type: The Type field is set to the value(4) indicating ESP-NOW.
- Version: The Version field is set to the version of ESP-NOW.
- Body: The Body contains the ESP-NOW data.
As ESP-NOW is connectionless, the MAC header is a little different from that of standard frames. The FromDS and ToDS bits of
FrameControl field are both 0. The first address field is set to the destination address. The second address field is set to
the source address. The third address field is set to broadcast address(0xff:0xff:0xff:0xff:0xff:0xff).
Security
--------
ESP-NOW use CCMP method which can be referenced in IEEE Std. 802.11-2012 to protect the vendor-specific action frame. The WiFi
device maintains a Primary Master Key(PMK) and several Local Master Keys(LMK). The lengths of them are 16 bytes. PMK is used
to encrypt LMK with AES-128 algorithm. Call ``esp_now_set_pmk()`` to set PMK. If PMK is not set, a default PMK will be used.
If LMK of the paired device is set, it will be used to encrypt the vendor-specific action frame with CCMP method. The maximum
number of different LMKs is six. Do not support encrypting multicast vendor-specific action frame.
Initialization and De-initialization
------------------------------------
Call ``esp_now_init()`` to initialize ESP-NOW and ``esp_now_deinit()`` to de-initialize ESP-NOW. ESP-NOW data must be transmitted
after WiFi is started, so it is recommended to start WiFi before initializing ESP-NOW and stop WiFi after de-initializing ESP-NOW.
When ``esp_now_deinit()`` is called, all of the information of paired devices will be deleted.
Add Paired Device
-----------------
Before sending data to other device, call ``esp_now_add_peer()`` to add it to the paired device list first. The maximum number of
paired devices is twenty. If security is enabled, the LMK must be set. ESP-NOW data can be sent from station or softap interface.
Make sure that the interface is enabled before sending ESP-NOW data. A device with broadcast MAC address must be added before
sending broadcast data. The range of the channel of paired device is from 0 to 14. If the channel is set to 0, data will be sent
on the current channel. Otherwise, the channel must be set as the channel that the local device is on.
Send ESP-NOW Data
-----------------
Call ``esp_now_send()`` to send ESP-NOW data and ``esp_now_register_send_cb`` to register sending callback function. It will return
`ESP_NOW_SEND_SUCCESS` in sending callback function if the data is received successfully on MAC layer. Otherwise, it will return
`ESP_NOW_SEND_FAIL`. There are several reasons failing to send ESP-NOW data, for example, the destination device doesn't exist, the
channels of the devices are not the same, the action frame is lost when transmiting on the air, etc. It is not guaranteed that
application layer can receive the data. If necessary, send back ack data when receiving ESP-NOW data. If receiving ack data timeout
happens, retransmit the ESP-NOW data. A sequence number can also be assigned to ESP-NOW data to drop the duplicated data.
If there is a lot of ESP-NOW data to send, call ``esp_now_send()`` to send less than or equal to 250 bytes of data once a time.
Note that too short interval between sending two ESP-NOW datas may lead to disorder of sending callback function. So, it is
recommended that sending the next ESP-NOW data after the sending callback function of previous sending has returned. The sending
callback function runs from a high-priority WiFi task. So, do not do lengthy operations in the callback function. Instead, post
necessary data to a queue and handle it from a lower priority task.
Receiving ESP-NOW Data
----------------------
Call ``esp_now_register_recv_cb`` to register receiving callback function. When receiving ESP-NOW data, receiving callback function
is called. The receiving callback function also runs from WiFi task. So, do not do lengthy operations in the callback function.
Instead, post necessary data to a queue and handle it from a lower priority task.
API Reference
-------------
.. include:: /_build/inc/esp_now.inc
@@ -0,0 +1,7 @@
Smart Config
============
API Reference
-------------
.. include:: /_build/inc/esp_smartconfig.inc
+33
View File
@@ -0,0 +1,33 @@
Wi-Fi
=====
Introduction
------------
The WiFi libraries provide support for configuring and monitoring the ESP32 WiFi networking functionality. This includes configuration for:
- Station mode (aka STA mode or WiFi client mode). ESP32 connects to an access point.
- AP mode (aka Soft-AP mode or Access Point mode). Stations connect to the ESP32.
- Combined AP-STA mode (ESP32 is concurrently an access point and a station connected to another access point).
- Various security modes for the above (WPA, WPA2, WEP, etc.)
- Scanning for access points (active & passive scanning).
- Promiscuous mode monitoring of IEEE802.11 WiFi packets.
Application Examples
--------------------
See :example:`wifi` directory of ESP-IDF examples that contains the following applications:
* Simple application showing how to connect ESP32 module to an Access Point - `esp-idf-template <https://github.com/espressif/esp-idf-template>`_.
* Using power save mode of Wi-Fi - :example:`wifi/power_save`.
API Reference
-------------
.. include:: /_build/inc/esp_wifi.inc
.. include:: /_build/inc/esp_wifi_types.inc
+12
View File
@@ -0,0 +1,12 @@
Wi-Fi API
*********
.. toctree::
:maxdepth: 1
Wi-Fi <esp_wifi>
Smart Config <esp_smartconfig>
ESPNOW <esp_now>
Example code for this API section is provided in :example:`wifi` directory of ESP-IDF examples.

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