]> granicus.if.org Git - esp-idf/commitdiff
driver: add rs485 half duplex interface support to uart driver (update after review)
authorAlex Lisitsyn <aleks@espressif.com>
Wed, 27 Jun 2018 14:48:14 +0000 (16:48 +0200)
committerAlex Lisitsyn <aleks@espressif.com>
Mon, 2 Jul 2018 13:24:58 +0000 (15:24 +0200)
An existing UART driver does not support RS485 half duplex mode.
This task adds this functionality to ESP_IDF UART driver.
driver/uart.c/h: updated to add support of RS485 half duplex mode
examples/peripherals/uart_echo_rs485/main/rs485_example.c: added test example
components/driver/test/test_uart.c: added test of RS485 half duplex mode
docs/en/api-reference/peripherals/uart.rst: updated documentation
test_uart.c: suppress GCC warnings about discarded const qualifiers
uart.rst: remove sphinx warning - "Duplicate explicit target name"
simple change in uart.h file
update (test_uart.c) after rebase from master
update uart.rst, uart.c, rs485_example.c
Update example description in file Readme.md
update uart.c/h, uart.rst, test_uart.c according to review results
update uart.h (uart_set_rx_timeout() description
test_uart.c remove ignore tag
uart.c/h: fix param errors
test_uart.c: Remove GCC warning supress
uart.rst: fix the notes
rs485_example.c: fix output

The tests are completed using RS485 adapters hardware connected to two ESP32 WROVER KITs.

TW#13812
Closes https://github.com/espressif/esp-idf/pull/667
Closes https://github.com/espressif/esp-idf/pull/1006

components/driver/include/driver/uart.h
components/driver/test/test_uart.c
components/driver/uart.c
docs/en/api-reference/peripherals/uart.rst
examples/peripherals/uart_echo_rs485/main/rs485_example.c

index d70da5e1a7c9fc91814b6b5b63412d44167f4605..a756377a76f4eb3ba4d9c60b3663c9fdf45da90d 100644 (file)
@@ -50,11 +50,11 @@ extern "C" {
  * @brief UART mode selection
  */
 typedef enum {
-    UART_MODE_UART = 0x0,               /*!< mode: regular UART mode*/
-    UART_MODE_RS485_A = 0x01,           /*!< mode: RS485 collision detection UART mode*/
-    UART_MODE_RS485_B = 0x02,           /*!< mode: application control RS485 UART mode*/
-    UART_MODE_RS485_HALF_DUPLEX = 0x03, /*!< mode: half duplex RS485 UART mode control by RTS pin */
-    UART_MODE_IRDA = 0x4,               /*!< mode: IRDA  UART mode*/
+    UART_MODE_UART = 0x00,                      /*!< mode: regular UART mode*/
+    UART_MODE_RS485_HALF_DUPLEX = 0x01,         /*!< mode: half duplex RS485 UART mode control by RTS pin */
+    UART_MODE_IRDA = 0x02,                      /*!< mode: IRDA  UART mode*/
+    UART_MODE_RS485_COLLISION_DETECT = 0x03,    /*!< mode: RS485 collision detection UART mode (used for test purposes)*/
+    UART_MODE_RS485_APP_CTRL = 0x04,            /*!< mode: application control RS485 UART mode (used for test purposes)*/
 } uart_mode_t;
 
 /**
@@ -84,7 +84,7 @@ typedef enum {
 typedef enum {
     UART_NUM_0 = 0x0,  /*!< UART base address 0x3ff40000*/
     UART_NUM_1 = 0x1,  /*!< UART base address 0x3ff50000*/
-    UART_NUM_2 = 0x2,  /*!< UART base address 0x3ff6E000*/
+    UART_NUM_2 = 0x2,  /*!< UART base address 0x3ff6e000*/
     UART_NUM_MAX,
 } uart_port_t;
 
@@ -756,8 +756,8 @@ esp_err_t uart_pattern_queue_reset(uart_port_t uart_num, int queue_length);
  * @param mode UART    UART mode to set 
  *
  * @return
- *     - ESP_OK   Success
- *     - ESP_FAIL Parameter error
+ *     - ESP_OK Success
+ *     - ESP_ERR_INVALID_ARG Parameter error
  */
 esp_err_t uart_set_mode(uart_port_t uart_num, uart_mode_t mode);
 
@@ -771,8 +771,9 @@ esp_err_t uart_set_mode(uart_port_t uart_num, uart_mode_t mode);
  *        the TOUT feature is disabled.
  *
  * @return
- *     - ESP_OK   Success
- *     - ESP_FAIL Parameter error
+ *     - ESP_OK Success
+ *     - ESP_ERR_INVALID_ARG Parameter error
+ *     - ESP_ERR_INVALID_STATE Driver is not installed
  */
 esp_err_t uart_set_rx_timeout(uart_port_t uart_num, const uint8_t tout_thresh);
 
@@ -786,8 +787,8 @@ esp_err_t uart_set_rx_timeout(uart_port_t uart_num, const uint8_t tout_thresh);
  * @param collision_flag Pointer to variable of type bool to return collision flag.
  *
  * @return
- *     - ESP_OK   No collision 
- *     - ESP_FAIL Parameter error
+ *     - ESP_OK Success 
+ *     - ESP_ERR_INVALID_ARG Parameter error
  */
 esp_err_t uart_get_collision_flag(uart_port_t uart_num, bool* collision_flag);
 
index 0684809fbcae0a8dcbdb326213c523cae94e29b6..b0cf1d846064555001dba5a722da81698dd21079 100644 (file)
@@ -313,9 +313,5 @@ static void rs485_master()
  * correctness of RS485 interface channel communication. It requires
  * RS485 bus driver hardware to be connected to boards.
 */
-// The lines below are required to suppress GCC warnings about discarded const qualifiers
-// of function pointers in unity macro expansion. These warnings may be treated as errors during compilation.
-#pragma GCC diagnostic push  // required for GCC
-#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers"
 TEST_CASE_MULTIPLE_DEVICES("RS485 half duplex uart multiple devices test.", "[driver]", rs485_master, rs485_slave);
-#pragma GCC diagnostic pop   // require GCC
+
index d1f5715b48059a39b51ff55c95dd4e048e1f448a..7fe6045ddc0c4fae8a5c347903015b329a325ca1 100644 (file)
@@ -1441,7 +1441,7 @@ esp_err_t uart_set_mode(uart_port_t uart_num, uart_mode_t mode)
 {
     UART_CHECK((p_uart_obj[uart_num]), "uart driver error", ESP_ERR_INVALID_STATE);
     UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG);
-    if ((mode == UART_MODE_RS485_A) || (mode == UART_MODE_RS485_B
+    if ((mode == UART_MODE_RS485_COLLISION_DETECT) || (mode == UART_MODE_RS485_APP_CTRL
             || (mode == UART_MODE_RS485_HALF_DUPLEX)) {
         UART_CHECK((UART[uart_num]->conf1.rx_flow_en != 1),
                 "disable hw flowctrl before using RS485 mode", ESP_ERR_INVALID_ARG);
@@ -1455,7 +1455,7 @@ esp_err_t uart_set_mode(uart_port_t uart_num, uart_mode_t mode)
     switch (mode) {
     case UART_MODE_UART:
         break;
-    case UART_MODE_RS485_A:
+    case UART_MODE_RS485_COLLISION_DETECT:
         // This mode allows read while transmitting that allows collision detection
         p_uart_obj[uart_num]->coll_det_flg = false;
         // Transmitter\92s output signal loop back to the receiver\92s input signal
@@ -1470,7 +1470,7 @@ esp_err_t uart_set_mode(uart_port_t uart_num, uart_mode_t mode)
                                         | UART_RS485_FRM_ERR_INT_ENA
                                         | UART_RS485_PARITY_ERR_INT_ENA);
         break;
-    case UART_MODE_RS485_B:
+    case UART_MODE_RS485_APP_CTRL:
         // Application software control, remove echo
         UART[uart_num]->rs485_conf.rx_busy_tx_en = 1;
         UART[uart_num]->rs485_conf.en = 1;
@@ -1488,7 +1488,7 @@ esp_err_t uart_set_mode(uart_port_t uart_num, uart_mode_t mode)
         UART[uart_num]->conf0.irda_en = 1;
         break;
     default:
-        UART_CHECK(1, "unsupported uart mode", ESP_FAIL);
+        UART_CHECK(1, "unsupported uart mode", ESP_ERR_INVALID_ARG);
         break;
     }
     p_uart_obj[uart_num]->uart_mode = mode;
@@ -1498,8 +1498,8 @@ esp_err_t uart_set_mode(uart_port_t uart_num, uart_mode_t mode)
 
 esp_err_t uart_set_rx_timeout(uart_port_t uart_num, const uint8_t tout_thresh) 
 {
-    UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL);
-    UART_CHECK((tout_thresh < 127), "tout_thresh max value is 126", ESP_FAIL);
+    UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG);
+    UART_CHECK((tout_thresh < 127), "tout_thresh max value is 126", ESP_ERR_INVALID_ARG);
     UART_ENTER_CRITICAL(&uart_spinlock[uart_num]);
     // The tout_thresh = 1, defines TOUT interrupt timeout equal to  
     // transmission time of one symbol (~11 bit) on current baudrate  
@@ -1515,10 +1515,11 @@ esp_err_t uart_set_rx_timeout(uart_port_t uart_num, const uint8_t tout_thresh)
 
 esp_err_t uart_get_collision_flag(uart_port_t uart_num, bool* collision_flag)
 {
-    UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL);
-    UART_CHECK((collision_flag != NULL), "wrong parameter pointer", ESP_FAIL);
+    UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG);
+    UART_CHECK((collision_flag != NULL), "wrong parameter pointer", ESP_ERR_INVALID_ARG);
     UART_CHECK((UART_IS_MODE_SET(uart_num, UART_MODE_RS485_HALF_DUPLEX) 
-                    || UART_IS_MODE_SET(uart_num, UART_MODE_RS485_A)), "wrong mode", ESP_FAIL);
+                    || UART_IS_MODE_SET(uart_num, UART_MODE_RS485_COLLISION_DETECT)), 
+                    "wrong mode", ESP_ERR_INVALID_ARG);
     *collision_flag = p_uart_obj[uart_num]->coll_det_flg;
     return ESP_OK;
 }
index 492af2d913ef32a38acfe4496f74a9a1a828a3ec..61bfa3082d0e3118b06291cafea564ca01701313 100644 (file)
@@ -43,7 +43,7 @@ The alternate way is to configure specific parameters individually by calling de
 
 Configuration example: ::
 
-    const int uart_num = UART_NUM_1;
+    const int uart_num = UART_NUM_2;
     uart_config_t uart_config = {
         .baud_rate = 115200,
         .data_bits = UART_DATA_8_BITS,
@@ -68,8 +68,8 @@ In next step, after configuring communication parameters, we are setting physica
 
 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. ::
 
-    // Set UART pins(TX: IO16, RX: IO17, RTS: IO18, CTS: IO19)
-    ESP_ERROR_CHECK(uart_set_pin(uart_num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, 18, 19));
+    // 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:
 
@@ -89,7 +89,7 @@ Example: ::
     const int uart_buffer_size = (1024 * 2);
     QueueHandle_t uart_queue;
     // Install UART driver using an event queue here
-    ESP_ERROR_CHECK(uart_driver_install(uart_num, uart_buffer_size, \
+    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.
@@ -111,7 +111,7 @@ The basic API function to write the data to Tx FIFO buffer is :cpp:func:`uart_tx
 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_1;
+    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. ::
@@ -132,7 +132,7 @@ 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::
 
     // Read data from UART.
-    const int uart_num = UART_NUM_1;
+    const int uart_num = UART_NUM_2;
     uint8_t data[128];
     int length = 0;
     ESP_ERROR_CHECK(uart_get_buffered_data_len(uart_num, (size_t*)&length));
@@ -188,7 +188,7 @@ If communication is established with :cpp:func:`uart_driver_install` for some sp
 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:: 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.
 
 - 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.
@@ -202,8 +202,7 @@ The ESP32 UART hardware is not able to control automatically the RTS pin connect
 Overview of RS485 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. 
-
+.. 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.
 
 The circuit A: Collision detection circuit 
 """"""""""""""""""""""""""""""""""""""""""
index bd815c5d553fb0f1bba619d5432c3b077e6c3611..05b5fb9cf394a14e753c012311e8ff8785809f13 100644 (file)
  * 
  */
 
-// Note io16 , io17 does not work on wrover module 
+// Note: UART2 default pins IO16, IO17 do not work on ESP32-WROVER module 
 // because these pins connected to PSRAM
-#define ECHO_TEST_TXD   (23) //(17) 
-#define ECHO_TEST_RXD   (22) //(16)
+#define ECHO_TEST_TXD   (23)
+#define ECHO_TEST_RXD   (22)
 
 // RTS for RS485 Half-Duplex Mode manages DE/~RE
 #define ECHO_TEST_RTS   (18)
@@ -50,6 +50,8 @@
 #define ECHO_TASK_PRIO          (10)
 #define ECHO_UART_PORT          (UART_NUM_2)
 
+static const char *TAG = "RS485_ECHO_APP";
+
 // An example of echo test with hardware flow control on UART
 static void echo_task()
 {
@@ -63,13 +65,15 @@ static void echo_task()
         .rx_flow_ctrl_thresh = 122,
     };
     
-    printf("Start RS485 application test.\r\n");
-    printf("Configure UART.\r\n");
+    // Set UART log level
+    esp_log_level_set(TAG, ESP_LOG_INFO);
+    
+    ESP_LOGI(TAG, "Start RS485 application test and configure UART.");
 
     // Configure UART parameters
     uart_param_config(uart_num, &uart_config);
     
-    printf("UART set pins.\r\n");
+    ESP_LOGI(TAG, "UART set pins, mode and install driver.");
     // Set UART1 pins(TX: IO23, RX: I022, RTS: IO18, CTS: IO19)
     uart_set_pin(uart_num, ECHO_TEST_TXD, ECHO_TEST_RXD, ECHO_TEST_RTS, ECHO_TEST_CTS);
 
@@ -83,7 +87,7 @@ static void echo_task()
     // Allocate buffers for UART
     uint8_t* data = (uint8_t*) malloc(BUF_SIZE);
 
-    printf("Start recieve loop.\r\n");
+    ESP_LOGI(TAG, "UART start recieve loop.\r\n");
     uart_write_bytes(uart_num, "Start RS485 UART test.\r\n", 24);
 
     while(1) {
@@ -96,7 +100,8 @@ static void echo_task()
             char prefix[] = "RS485 Received: [";
             uart_write_bytes(uart_num, prefix, (sizeof(prefix) - 1));
             
-            printf("Received [ ");
+            ESP_LOGI(TAG, "Received %u bytes:", len);
+            printf("[ ");
             for (int i = 0; i < len; i++) {
                 printf("0x%.2X ", (uint8_t)data[i]);
                 uart_write_bytes(uart_num, (const char*)&data[i], 1);