mirror of
https://github.com/espressif/esp-idf.git
synced 2026-05-04 03:52:01 +02:00
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:
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user