Review the file api-reference/peripherals/uart.rst

This commit is contained in:
Kirill Chalov 2019-11-04 19:23:12 +08:00
parent 2fa24d6e39
commit 9da268bd32

View File

@ -4,25 +4,26 @@ UART
Overview
--------
A Universal Asynchronous Receiver/Transmitter (UART) is a component known to handle the timing requirements for a variety of widely-adapted interfaces (RS232, RS485, RS422, ...). A UART provides a widely adopted and cheap method to realize full-duplex or half-duplex data exchange among different devices.
A Universal Asynchronous Receiver/Transmitter (UART) is a hardware feature that handles communication (i.e., timing requirements and data framing) using widely-adapted asynchronous serial communication interfaces, such as RS232, RS422, RS485. A UART provides a widely adopted and cheap method to realize full-duplex or half-duplex data exchange among different devices.
The ESP32 chip has three UART controllers (UART0, UART1, and UART2) that feature an identical set of registers for ease of programming and flexibility. Each UART controller is independently configurable with parameters such as baud rate, data bit length, bit ordering, number of stop bits, parity bit etc. All the controllers are compatible with UART-enabled devices from various manufacturers and can also support Infrared Data Association protocols (IrDA).
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:
The following overview describes how to establish communication between an ESP32 and other UART devices using the functions and data types of the UART driver. The overview reflects a typical programming workflow and is broken down into the sections provided below:
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
1. :ref:`uart-api-setting-communication-parameters` - Setting baud rate, data bits, stop bits, etc.
2. :ref:`uart-api-setting-communication-pins` - Assigning pins for connection to a device.
3. :ref:`uart-api-driver-installation` - Allocating ESP32's resources for the UART driver.
4. :ref:`uart-api-running-uart-communication` - Sending / receiving data
5. :ref:`uart-api-using-interrupts` - Triggering interrupts on specific communication events
6. :ref:`uart-api-deleting-driver` - Freeing allocated resources if a UART communication is no longer required
The minimum to make the UART working is to complete the first four steps, the last two steps are optional.
Steps 1 to 3 comprise the configuration stage. Step 4 is where the UART starts operating. Steps 5 and 6 are optional.
The driver is identified by :cpp:type:`uart_port_t`, that corresponds to one of the three UART controllers. Such identification is present in all the following function calls.
The UART driver's functions identify each of the three UART controllers using :cpp:type:`uart_port_t`. This identification is needed for all the following function calls.
.. _uart-api-setting-communication-parameters:
@ -30,18 +31,15 @@ The driver is identified by :cpp:type:`uart_port_t`, that corresponds to one of
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.
UART communication parameters can be configured all in a single step or individually in multiple steps.
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`
* Communication mode - :cpp:func:`uart_set_mode` selected out of :cpp:type:`uart_mode_t`
Single Step
"""""""""""
Configuration example: ::
Call the function :cpp:func:`uart_param_config` and pass to it a :cpp:type:`uart_config_t` structure. The :cpp:type:`uart_config_t` structure should contain all the required parameters. See the example below.
.. code-block:: c
const int uart_num = UART_NUM_2;
uart_config_t uart_config = {
@ -56,7 +54,31 @@ Configuration example: ::
ESP_ERROR_CHECK(uart_param_config(uart_num, &uart_config));
All the above functions have a ``_get_`` equivalent to retrieve the current setting, e.g. :cpp:func:`uart_get_baudrate`.
Multiple Steps
""""""""""""""
Configure specific parameters individually by calling a dedicated function from the table given below. These functions are also useful if re-configuring a single parameter.
.. list-table:: Functions for Configuring specific parameters individually
:widths: 30 70
:header-rows: 1
* - Parameter to Configure
- Function
* - 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 :cpp:type:`uart_hw_flowcontrol_t`
* - Communication mode
- :cpp:func:`uart_set_mode` selected out of :cpp:type:`uart_mode_t`
Each of the above functions has a ``_get_`` counterpart to check the currently set value. For example, to check the current baud rate value, call :cpp:func:`uart_get_baudrate`.
.. _uart-api-setting-communication-pins:
@ -64,26 +86,31 @@ All the above functions have a ``_get_`` equivalent to retrieve the current sett
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.
After setting communication parameters, configure the physical GPIO pins to which the other UART device will be connected. For this, call the function :cpp:func:`uart_set_pin` and specify the GPIO pin numbers to which the driver should route the Tx, Rx, RTS, and CTS signals. If you want to keep a currently allocated pin number for a specific signal, pass the macro :c:macro:`UART_PIN_NO_CHANGE`.
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. ::
The same macro should be specified for pins that will not be used.
.. code-block:: c
// Set UART pins(TX: IO16 (UART2 default), RX: IO17 (UART2 default), RTS: IO18, CTS: IO19)
ESP_ERROR_CHECK(uart_set_pin(UART_NUM_2, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, 18, 19));
.. _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:
Once the communication pins are set, install the driver by calling :cpp:func:`uart_driver_install` and specify the following parameters:
* size of the send buffer
* size of the receive buffer
* the event queue handle and size
* flags to allocate an interrupt
- Size of Tx ring buffer
- Size of Rx ring buffer
- Event queue handle and size
- Flags to allocate an interrupt
Example: ::
The function will allocate the required ESP32 resources for the UART driver.
.. code-block:: c
// Setup UART buffered IO with event queue
const int uart_buffer_size = (1024 * 2);
@ -92,7 +119,7 @@ Example: ::
ESP_ERROR_CHECK(uart_driver_install(UART_NUM_2, uart_buffer_size, \
uart_buffer_size, 10, &uart_queue, 0));
If all above steps have been complete, we are ready to connect the other UART device and check the communication.
Once this step is complete, you can connect the external UART device and check the communication.
.. _uart-api-running-uart-communication:
@ -100,36 +127,58 @@ If all above steps have been complete, we are ready to connect the other UART de
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.
Serial communication is controlled by each UART controller's finite state machine (FSM).
The process of sending data involves the following steps:
1. Write data into Tx FIFO buffer
2. FSM serializes the data
3. FSM sends the data out
The process of receiving data is similar, but the steps are reversed:
1. FSM processes an incoming serial stream and parallelizes it
2. FSM writes the data into Rx FIFO buffer
3. Read the data from Rx FIFO buffer
Therefore, an application will be limited to writing and reading data from a respective buffer using :cpp:func:`uart_write_bytes` and :cpp:func:`uart_read_bytes` respectively, and the FSM will do the rest.
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.
After preparing the data for transmission, call the function :cpp:func:`uart_write_bytes` and pass the data buffer's address and data length to it. The function will copy the data to the Tx ring buffer (either immediately or after enough space is available), and then exit. When there is free space in the Tx FIFO buffer, an interrupt service routine (ISR) moves the data from the Tx ring buffer to the Tx FIFO buffer in the background. The code below demonstrates the use of this function.
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. ::
// Wait for packet to be sent
const int uart_num = UART_NUM_2;
ESP_ERROR_CHECK(uart_wait_tx_done(uart_num, 100)); // wait timeout is 100 RTOS ticks (TickType_t)
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. The code below demonstrates using of this function. ::
.. code-block:: c
// Write data to UART.
char* test_str = "This is a test string.\n";
uart_write_bytes(uart_num, (const char*)test_str, strlen(test_str));
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 ::
The function :cpp:func:`uart_write_bytes_with_break` is similar to :cpp:func:`uart_write_bytes` but adds a serial break signal at the end of the transmission. A 'serial break signal' means holding the Tx line low for a period longer than one data frame.
.. code-block:: c
// Write data to UART, end with a break signal.
uart_write_bytes_with_break(uart_num, "test break\n",strlen("test break\n"), 100);
Another function for writing data to the Tx FIFO buffer is :cpp:func:`uart_tx_chars`. Unlike :cpp:func:`uart_write_bytes`, this function will not block until space is available. Instead, it will write all data which can immediately fit into the hardware Tx FIFO, and then return the number of bytes that were written.
There is a 'companion' function :cpp:func:`uart_wait_tx_done` that monitors the status of the Tx FIFO buffer and returns once it is empty.
.. code-block:: c
// Wait for packet to be sent
const int uart_num = UART_NUM_2;
ESP_ERROR_CHECK(uart_wait_tx_done(uart_num, 100)); // wait timeout is 100 RTOS ticks (TickType_t)
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`. Below is the example of using this function::
Once the data is received by the UART and saved in the Rx FIFO buffer, it needs to be retrieved using the function :cpp:func:`uart_read_bytes`. Before reading data, you can check the number of bytes available in the Rx FIFO buffer by calling :cpp:func:`uart_get_buffered_data_len`. An example of using these functions is given below.
.. code-block:: c
// Read data from UART.
const int uart_num = UART_NUM_2;
@ -138,19 +187,21 @@ To retrieve the data received by UART and saved in Rx FIFO, use function :cpp:fu
ESP_ERROR_CHECK(uart_get_buffered_data_len(uart_num, (size_t*)&length));
length = uart_read_bytes(uart_num, data, length, 100);
If the data in Rx FIFO is not required and should be discarded, call :cpp:func:`uart_flush`.
If the data in the Rx FIFO buffer is no longer needed, you can clear the buffer by calling :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.
If the hardware flow control is disabled, you can manually set the RTS and DTR signal levels by using the functions :cpp:func:`uart_set_rts` and :cpp:func:`uart_set_dtr` respectively.
Communication Mode Selection
""""""""""""""""""""""""""""
The UART controller supports set of communication modes. The selection of mode can be performed using function :cpp:func:`uart_set_mode`. Once the specific mode is selected the UART driver will handle behavior of external peripheral according to mode. As an example it can control RS485 driver chip over RTS line to allow half-duplex RS485 communication. ::
The UART controller supports a number of communication modes. A mode can be selected using the function :cpp:func:`uart_set_mode`. Once a specific mode is selected, the UART driver will handle the behavior of a connected UART device accordingly. As an example, it can control the RS485 driver chip using the RTS line to allow half-duplex RS485 communication.
.. code-block:: bash
// Setup UART in rs485 half duplex mode
ESP_ERROR_CHECK(uart_set_mode(uart_num, UART_MODE_RS485_HALF_DUPLEX));
@ -161,57 +212,81 @@ The UART controller supports set of communication modes. The selection of mode c
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`.
There are many interrupts that can be generated following specific UART states or detected errors. The full list of available interrupts is provided in `ESP32 Technical Reference Manual <https://espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf#page=342>`_ (PDF). You can enable or disable specific interrupts by calling :cpp:func:`uart_enable_intr_mask` or :cpp:func:`uart_disable_intr_mask` respectively. The mask of all interrupts is available as :c:macro:`UART_INTR_MASK`.
The API provides a convenient way to handle specific interrupts discussed above by wrapping them into dedicated functions:
By default, the :cpp:func:`uart_driver_install` function installs the driver's internal interrupt handler to manage the Tx and Rx ring buffers and provides high-level API functions like events (see below). It is also possible to register a lower level interrupt handler instead using :cpp:func:`uart_isr_register`, and to free it again using :cpp:func:`uart_isr_free`. Some UART driver functions which use the Tx and Rx ring buffers, events, etc. will not automatically work in this case - it is necessary to handle the interrupts directly in the ISR. Inside the custom handler implementation, clear the interrupt status bits using :cpp:func:`uart_clear_intr_status`.
* **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`.
The API provides a convenient way to handle specific interrupts discussed in this document by wrapping them into dedicated functions:
* **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`.
- **Event detection**: There are several events defined in :cpp:type:`uart_event_type_t` that may be reported to a user application using the FreeRTOS queue functionality. You can enable this functionality when calling :cpp:func:`uart_driver_install` described in :ref:`uart-api-driver-installation`. An example of using Event detection can be found in :example:`peripherals/uart_events`.
* **Pattern detection** - an interrupt triggered on detecting a 'pattern' of the same character being received/sent repeatedly for a number of times. This functionality is demonstrated in :example:`peripherals/uart/uart_events` example. It may be used e.g. to detect a command string followed by specific number of identical characters (the 'pattern') added at the end of the command string. 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`.
- **FIFO space threshold or transmission timeout reached**: The Tx and Rx FIFO buffers can trigger an interrupt when they are filled with a specific number of characters, or on a timeout of sending or receiving data. To use these interrupts, do the following:
- Configure respective threshold values of the buffer length and timeout by entering them in the structure :cpp:type:`uart_intr_config_t` and calling :cpp:func:`uart_intr_config`
- Enable the interrupts using the functions :cpp:func:`uart_enable_tx_intr` and :cpp:func:`uart_enable_rx_intr`
- Disable these interrupts using the corresponding functions :cpp:func:`uart_disable_tx_intr` or :cpp:func:`uart_disable_rx_intr`
- **Pattern detection**: An interrupt triggered on detecting a 'pattern' of the same character being received/sent repeatedly for a number of times. This functionality is demonstrated in the example :example:`peripherals/uart/uart_events`. It can be used, e.g., to detect a command string followed by a specific number of identical characters (the 'pattern') added at the end of the command string. The following functions are available:
- Configure and enable this interrupt using :cpp:func:`uart_enable_pattern_det_intr`
- Disable the interrupt using :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.
The API also defines several macros. For example, :c:macro:`UART_FIFO_LEN` defines the length of hardware FIFO buffers; :c:macro:`UART_BITRATE_MAX` gives the maximum baud rate supported by the UART controllers, etc.
.. _uart-api-deleting-driver:
Deleting Driver
^^^^^^^^^^^^^^^
Deleting a 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`.
If the communication established with :cpp:func:`uart_driver_install` is no longer required, the driver can be removed to free allocated resources by calling :cpp:func:`uart_driver_delete`.
Overview of RS485 specific communication options
-------------------------------------------------
------------------------------------------------
.. note:: Here and below the notation UART_REGISTER.UART_OPTION_BIT will be used to describe register options of UART. See the ESP32 Technical Reference Manual for more information.
.. note::
- UART_RS485_CONF_REG.UART_RS485_EN = 1, enable RS485 communication mode support.
- UART_RS485_CONF_REG.UART_RS485TX_RX_EN, transmitter's output signal loop back to the receiver's input signal when this bit is set.
- UART_RS485_CONF_REG.UART_RS485RXBY_TX_EN, when bit is set the transmitter should send data when its receiver is busy (remove collisions automatically by hardware).
The following section will use ``[UART_REGISTER_NAME].[UART_FIELD_BIT]`` to refer to UART register fields/bits. To find more information on a specific option bit, open `Register Summary in the ESP32 Technical Reference Manual <https://espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf#page=344>`_ (PDF), use the register name to navigate to the register description and then find the field/bit.
The on chip RS485 UART hardware is able to detect signal collisions during transmission of datagram and generate an interrupt UART_RS485_CLASH_INT when it is enabled. The term collision means that during transmission of datagram the received data is different with what has been transmitted out or framing errors exist. Data collisions are usually associated with the presence of other active devices on the bus or due to bus errors. The collision detection feature allows suppressing the collisions when its interrupt is activated and triggered. The UART_RS485_FRM_ERR_INT and UART_RS485_PARITY_ERR_INT interrupts can be used with collision detection feature to control frame errors and parity errors accordingly in RS485 mode. This functionality is supported in the UART driver and can be used with selected UART_MODE_RS485_A mode (see :cpp:func:`uart_set_mode` function). The collision detection option can work with circuit A and circuit C (see below) which allow collision detection. In case of using circuit number A or B, control of RTS pin connected to DE pin of bus driver should be provided manually by application. The function :cpp:func:`uart_get_collision_flag` allows to get collision detection flag from driver.
- ``UART_RS485_CONF_REG.UART_RS485_EN``: setting this bit enables RS485 communication mode support.
- ``UART_RS485_CONF_REG.UART_RS485TX_RX_EN``: if this bit is set, the transmitter's output signal loops back to the receiver's input signal.
- ``UART_RS485_CONF_REG.UART_RS485RXBY_TX_EN``: if this bit is set, the transmitter will still be sending data if the receiver is busy (remove collisions automatically by hardware).
The ESP32 UART hardware is not able to control automatically the RTS pin connected to ~RE/DE input of RS485 bus driver to provide half duplex communication. This can be done by UART driver software when UART_MODE_RS485_HALF_DUPLEX mode is selected using :cpp:func:`uart_set_mode` function. The UART driver software automatically asserts the RTS pin (logic 1) once the host writes data to the transmit FIFO, and deasserts RTS pin (logic 0) once the last bit of the data has been transmitted. To use this mode the software would have to disable the hardware flow control function. This mode works with any of used circuit showed below.
The ESP32's RS485 UART hardware can detect signal collisions during transmission of a datagram and generate the interrupt ``UART_RS485_CLASH_INT`` if this interrupt is enabled. The term collision means that a transmitted datagram is not equal to the one received on the other end. Data collisions are usually associated with the presence of other active devices on the bus or might occur due to bus errors.
The collision detection feature allows handling collisions when their interrupts are activated and triggered. The interrupts ``UART_RS485_FRM_ERR_INT`` and ``UART_RS485_PARITY_ERR_INT`` can be used with the collision detection feature to control frame errors and parity bit errors accordingly in RS485 mode. This functionality is supported in the UART driver and can be used by selecting the :cpp:enumerator:`UART_MODE_RS485_APP_CTRL` mode (see the function :cpp:func:`uart_set_mode`).
The collision detection feature can work with circuit A and circuit C (see Section `Interface Connection Options`_). In the case of using circuit A or B, the RTS pin connected to the DE pin of the bus driver should be controlled by the user application. Use the function :cpp:func:`uart_get_collision_flag` to check if the collision detection flag has been raised.
The ESP32's UART controllers themselves do not support half-duplex communication as they cannot provide automatic control of the RTS pin connected to the ~RE/DE input of RS485 bus driver. However, half-duplex communication can be achieved via software control of the RTS pin by the UART driver. This can be enabled by selecting the :cpp:enumerator:`UART_MODE_RS485_HALF_DUPLEX` mode when calling :cpp:func:`uart_set_mode`.
Once the host starts writing data to the Tx FIFO buffer, the UART driver automatically asserts the RTS pin (logic 1); once the last bit of the data has been transmitted, the driver de-asserts the RTS pin (logic 0). To use this mode, the software would have to disable the hardware flow control function. This mode works with all the used circuits shown below.
Overview of RS485 interface connection options
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Interface Connection Options
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. note:: The example schematics below are prepared for just demonstration of basic aspects of RS485 interface connection for ESP32 and may not contain all required elements. The Analog Devices ADM483 & ADM2483 are examples of common RS485 transceivers and other similar transceivers can also be used.
This section provides example schematics to demonstrate the basic aspects of ESP32's RS485 interface connection.
The circuit A: Collision detection circuit
""""""""""""""""""""""""""""""""""""""""""
.. note::
::
- The schematics below do **not** necessarily contain **all required elements**.
VCC ---------------+
|
- The **analog devices** ADM483 & ADM2483 are examples of common RS485 transceivers and **can be replaced** with other similar transceivers.
Circuit A: Collision Detection Circuit
""""""""""""""""""""""""""""""""""""""
.. code-block:: none
VCC ---------------+
|
+-------x-------+
RXD <------| R |
| B|----------<> B
@ -224,16 +299,16 @@ The circuit A: Collision detection circuit
| |
GND GND
This circuit is preferred because it allows collision detection and is simple enough. The receiver in the line driver is constantly enabled that allows UART to monitor the RS485 bus. Echo suppression is done by the ESP32 chip hardware when the UART_RS485_CONF_REG.UART_RS485TX_RX_EN bit is enabled.
This circuit is preferable because it allows for collision detection and is quite simple at the same time. The receiver in the line driver is constantly enabled, which allows the UART to monitor the RS485 bus. Echo suppression is performed by the ESP32 hardware when the bit ``UART_RS485_CONF_REG.UART_RS485TX_RX_EN`` is enabled.
The circuit B: manual switching of transmitter/receiver without collision detection
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
Circuit B: Manual Switching Transmitter/Receiver Without Collision Detection
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
::
.. code-block:: none
VCC ---------------+
|
VCC ---------------+
|
+-------x-------+
RXD <------| R |
| B|-----------<> B
@ -246,49 +321,60 @@ The circuit B: manual switching of transmitter/receiver without collision detect
|
GND
This circuit does not allow collision detection. It suppresses the null bytes receive by hardware when UART_RS485_CONF_REG.UART_RS485TX_RX_EN is set. The bit UART_RS485_CONF_REG.UART_RS485RXBY_TX_EN is not applicable in this case.
This circuit does not allow for collision detection. It suppresses the null bytes that the hardware receives when the bit ``UART_RS485_CONF_REG.UART_RS485TX_RX_EN`` is set. The bit ``UART_RS485_CONF_REG.UART_RS485RXBY_TX_EN`` is not applicable in this case.
The circuit C: auto switching of transmitter/receiver
"""""""""""""""""""""""""""""""""""""""""""""""""""""
Circuit C: Auto Switching Transmitter/Receiver
""""""""""""""""""""""""""""""""""""""""""""""
::
.. code-block:: none
VCC1<-------------------+-----------+ +-------------------+----> VCC2
10K ____ | | | |
+---|____|--+ +---x-----------x---+ 10K ____ |
VCC1 <-------------------+-----------+ +-------------------+----> VCC2
10K ____ | | | |
+---|____|--+ +---x-----------x---+ 10K ____ |
| | | +---|____|--+ GND2
RX <----------+-------------------| RXD | |
10K ____ | A|---+---------------<> A (+)
10K ____ | A|---+---------------<> A (+)
+-------|____|------| PV ADM2483 | | ____ 120
| ____ | | +---|____|---+ RS485 bus side
VCC1<--+--|____|--+------->| DE | |
10K | | B|---+------------+--<> B (-)
VCC1 <--+--|____|--+------->| DE | |
10K | | B|---+------------+--<> B (-)
---+ +-->| /RE | | ____
10K | | | | +---|____|---+
____ | /-C +---| TXD | 10K |
10K | | | | +---|____|---+
____ | /-C +---| TXD | 10K |
TX >---|____|--B___|/ NPN | | | |
|\ | +---x-----------x---+ |
| \-E | | | |
| | | | |
GND1 GND1 GND1 GND2 GND2
This galvanic isolated circuit does not require RTS pin control by software application or driver because it controls transceiver direction automatically. However it requires removing null bytes during transmission by setting UART_RS485_CONF_REG.UART_RS485RXBY_TX_EN = 1, UART_RS485_CONF_REG.UART_RS485TX_RX_EN = 0. This variant can work in any RS485 UART mode or even in UART_MODE_UART.
This galvanically isolated circuit does not require RTS pin control by a software application or driver because it controls the transceiver direction automatically. However, it requires suppressing null bytes during transmission by setting ``UART_RS485_CONF_REG.UART_RS485RXBY_TX_EN`` to 1 and ``UART_RS485_CONF_REG.UART_RS485TX_RX_EN`` to 0. This setup can work in any RS485 UART mode or even in :cpp:enumerator:`UART_MODE_UART`.
Application Examples
--------------------
Configure UART settings and install UART driver to read/write using UART1 interface: :example:`peripherals/uart/uart_echo`.
The table below describes the code examples available in the directory :example:`peripherals/uart/`.
Demonstration of how to report various communication events and how to use pattern detection interrupts: :example:`peripherals/uart/uart_events`.
.. list-table::
:widths: 35 65
:header-rows: 1
Transmitting and receiving with the same UART in two separate FreeRTOS tasks: :example:`peripherals/uart/uart_async_rxtxtasks`.
* - Code Example
- Description
* - :example:`peripherals/uart/uart_echo`
- Configuring UART settings, installing the UART driver, and reading/writing over the UART1 interface.
* - :example:`peripherals/uart/uart_events`
- Reporting various communication events, using pattern detection interrupts.
* - :example:`peripherals/uart/uart_async_rxtxtasks`
- Transmitting and receiving data in two separate FreeRTOS tasks over the same UART.
* - :example:`peripherals/uart/uart_select`
- Using synchronous I/O multiplexing for UART file descriptors.
* - :example:`peripherals/uart/uart_echo_rs485`
- Setting up UART driver to communicate over RS485 interface in half-duplex mode. This example is similar to :example:`peripherals/uart/uart_echo` but allows communication through an RS485 interface chip connected to ESP32 pins.
* - :example:`peripherals/uart/nmea0183_parser`
- Obtaining GPS information by parsing NMEA0183 statements received from GPS via the UART peripheral.
Using synchronous I/O multiplexing for UART file descriptors: :example:`peripherals/uart/uart_select`.
Setup of UART driver to communicate over RS485 interface in half-duplex mode: :example:`peripherals/uart/uart_echo_rs485`. This example is similar to uart_echo but provide communication through RS485 interface chip connected to ESP32 pins.
Demonstration of how to get GPS information by parsing NMEA0183 statements received from GPS via UART peripheral: :example:`peripherals/uart/nmea0183_parser`.
API Reference
-------------
@ -299,11 +385,15 @@ API Reference
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.:
The UART peripherals have dedicated IO_MUX pins to which they are connected directly. However, signals can also be routed to other pins using the less direct GPIO matrix. To use direct routes, you need to know which pin is a dedicated IO_MUX pin for a UART channel. GPIO Lookup Macros simplify the process of finding and assigning IO_MUX pins. You choose a macro based on either the IO_MUX pin number, or a required UART channel name, and the macro will return the matching counterpart for you. See some examples below.
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).
.. note::
These macros are useful if you need very high UART baud rates (over 40 MHz), which means you will have to use IO_MUX pins only. In other cases, these macros can be ignored, and you can use the GPIO Matrix as it allows you to configure any GPIO pin for any UART function.
1. :c:macro:`UART_NUM_2_TXD_DIRECT_GPIO_NUM` returns the IO_MUX pin number of UART channel 2 TXD pin (pin 17)
2. :c:macro:`UART_GPIO19_DIRECT_CHANNEL` returns the UART number of GPIO 19 when connected to the UART peripheral via IO_MUX (this is UART_NUM_0)
3. :c:macro:`UART_CTS_GPIO19_DIRECT_CHANNEL` returns the UART number of GPIO 19 when used as the UART CTS pin via IO_MUX (this is UART_NUM_0). Similar to the above macro but specifies the pin function which is also part of the IO_MUX assignment.
.. include:: /_build/inc/uart_channel.inc