środa, 13 września 2017

[1] ESP32 - Komunikacja po UART

W tym poście chciałbym opisać sposób komunikacja przez ESP32 za pomocą UARTu.

[Źródło: www.banggood.com]

Do dyspozycji są następujące układy wraz z pinami:

  • UART0 - RX - GPIO3; TX - GPIO1; CTS - n/a; RTS - n/a;
  • UART1 - RX - GPIO9; TX - GPIO10; CTS - GPIO6; RTS - GPIO11;
  • UART2 - RX - GPIO16; TX - GPIO17; CTS - GPIO8; RTS - GPIO7;

Funkcje:


Funkcje wykorzystywane w przykładach z bibliotek esp32 to:

uart_param_config() - funkcja używana do ustawiania parametrów UARTu. Pełne rozwinięcie funkcji:

esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_config)

Wartości zwracane to ESP_OK w przypadku poprawnego ustawienia, ESP_FAIL w przypadku błędu.
Jako parametr podawany jest numer portu od 0 do 2 lub jako makra UART_NUM_0, UART_NUM_1 lub UART_NUM_2. Drugi parametr odnosi się do struktury uart_config_t zawierające poszczególne pola do ustawienia. Wśród nich znajdują się:

  • int baudrate
  • uart_word_length_t data_bits
  • uart_parity_t parity
  • uart_stop_bits_t stop_bits
  • uart_hw_flowcontrol_t flow_ctrl - wybranie trybu kontroli cts lub rts;
  • uint8_t rx_flow_ctrl_tresh

uart_set_pin() - ustawienie pinów:

esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num);

Podobnie jak poprzednie funkcje zwracają one status wykonania operacji. Jako parametry podaje się numer uruchomianego uarta a następnie piny. Najpierw TX potem odpowiednio RX, RTS oraz CTS.

uart_driver_install() - odpowiada za uruchomienie sterownika dla UARTu.

esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, QueueHandle_t* uart_queue, int intr_alloc_flags);

Parametry:
  • uart_num - numer wykorzystywanego uartu,
  • rx_buffer_size - rozmiar buffora. Musi być większy niż UART_FIFO_LEN,
  • tx_buffer_size - rozmiar buffora dla TX. W przypadku podania tutaj wartości zero, dane z TX będą przesyłane w trybie blokowania.
  • queue_size - wielkość kolejki
  • uart_queue - jeśli jest jakaś wartość podana wtedy wykorzystywana jest dostęp do zdarzeń od UARTa. Ustawienie na NULL powoduje nie wykorzystywanie tej operacji.
  • intr_alloc_flags - flagi stosowane do przypisania przerwań.


uart_read_bytes() - funkcja wykorzystywana do odczytu danych przychodzących:

int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickType_t ticks_to_wait);

Jako parametr podawany jest numer kanału, następnie wskaźnik na bufor do którego odebrane dane zostaną zapisane. Dalej wielkość tego bufora. Na końcu czas oczekiwania na odebranie danych. 

uart_write_bytes() - wysyła dane na wybrany port o podanej długości.

int uart_write_bytes(uart_port_t uart_num, const char* src, size_t size);

Tutaj podawany jest numer kanału, następnie adres do przechowywanych danych. Na samym końcu rozmiar danych do wysłania. Funkcja zwraca -1 jeśli nastąpi błąd. W przypadku poprawnego wykonania zwracana jest wartość większa od 0 informująca o liczbie danych jakie zostały przesłane.

Program:


Program został przeze mnie przygotowany w eclipsie. Poniżej znajdują się plik .h oraz c.

Plik h:

  1. #ifndef MAIN_UART_SETTINGS_H_
  2. #define MAIN_UART_SETTINGS_H_
  3. /*
  4.  * UART     RXIO    TXIO    CTS     RTS
  5.  * UART0    GPIO3   GPIO1   n/a     n/a
  6.  * UART1    GPIO9   GPIO10  GPIO6   GPIO11
  7.  * UART2    GPIO16  GPIO17  GPIO8   GPIO7
  8.  */
  9. /* Include files */
  10. #include "driver/gpio.h"
  11. #include "driver/uart.h"
  12. #include "soc/uart_struct.h"
  13. #include "uart_settings.h"
  14. /* Pin definitions */
  15. #define UART0_PIN_RX    3
  16. #define UART0_PIN_TX    1
  17. //------------------------------
  18. #define UART1_PIN_RX    9
  19. #define UART1_PIN_TX    10
  20. #define UART1_PIN_CTS   6
  21. #define UART1_PIN_RTS   11
  22. //------------------------------
  23. #define UART2_PIN_RX    16
  24. #define UART2_PIN_TX    17
  25. #define UART2_PIN_CTS   8
  26. #define UART2_PIN_RTS   7
  27. //------------------------------
  28. #define BUFFER_SIZE (1024)
  29. //------------------------------
  30. /* @brief: Initialization of UART1 */
  31. void Uart1_Init(const int uartNumber, uint32_t baudrate);
  32. /* @brief: Initialization of UART2 */
  33. void Uart2_Init(const int uartNumber, uint32_t baudrate);
  34. /* @brief: Send Data procedure */
  35. void Uart_Send_Data(const int uartNumber, uint8_t *buffer);
  36. /* @brief: send table of data over selected uart port */
  37. void Uart_SendArray(const int uartNumber, uint8_t *dataTable, uint16_t cnt);
  38. /* @brief: send single byte */
  39. void Uart_SendByte(const int uartNumber,  uint8_t data);
  40. /* @brief: send number in ASCII format */
  41. void Uart_SendNumber(const int uartNumber, uint32_t x);
  42. #endif /* MAIN_UART_SETTINGS_H_ */

Plik c:

  1. #include "uart_settings.h"
  2. //----------------------------------------------------------------------------
  3. /*
  4.  * @brief:  Initialization of UART0
  5.  * @param:  uartNumber: Number of uart that will be initialzed
  6.  * @param:  baudrate: speed of transmission
  7.  * @ret:    none
  8.  */
  9. void Uart0_Init(const int uartNumber, uint32_t baudrate)
  10. {
  11.     uart_config_t uart0_config;
  12.     uart0_config.baud_rate = baudrate;                      /*!< UART baudrate                      */
  13.     uart0_config.data_bits = UART_DATA_8_BITS;              /*!< UART byte size                     */
  14.     uart0_config.parity = UART_PARITY_DISABLE;              /*!< UART parity mode                   */
  15.     uart0_config.stop_bits = UART_STOP_BITS_1;              /*!< UART stop bits                     */
  16.     uart0_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;      /*!< UART HW flow control mode(cts/rts) */
  17.     uart0_config.rx_flow_ctrl_thresh = 122;                 /*!< UART HW RTS threshold              */
  18.     uart_param_config(uartNumber, &uart0_config);
  19.     uart_set_pin(uartNumber, UART1_PIN_TX, UART1_PIN_RX, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
  20.     uart_driver_install(uartNumber, BUFFER_SIZE * 2, 0, 20, NULL, 0);
  21. }
  22. //----------------------------------------------------------------------------
  23. /*
  24.  * @brief:  Initialization of UART1
  25.  * @param:  uartNumber: Number of uart that will be initialzed
  26.  * @param:  baudrate: speed of transmission
  27.  * @ret:    none
  28.  */
  29. void Uart1_Init(const int uartNumber, uint32_t baudrate)
  30. {
  31.     uart_config_t uart1_config;
  32.     uart1_config.baud_rate = baudrate;                      /*!< UART baudrate                      */
  33.     uart1_config.data_bits = UART_DATA_8_BITS;              /*!< UART byte size                     */
  34.     uart1_config.parity = UART_PARITY_DISABLE;              /*!< UART parity mode                   */
  35.     uart1_config.stop_bits = UART_STOP_BITS_1;              /*!< UART stop bits                     */
  36.     uart1_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;      /*!< UART HW flow control mode(cts/rts) */
  37.     uart1_config.rx_flow_ctrl_thresh = 122;                 /*!< UART HW RTS threshold              */
  38.     uart_param_config(uartNumber, &uart1_config);
  39.     uart_set_pin(uartNumber, UART1_PIN_TX, UART1_PIN_RX, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
  40.     uart_driver_install(uartNumber, BUFFER_SIZE * 2, 0, 20, NULL, 0);
  41. }
  42. //----------------------------------------------------------------------------
  43. /*
  44.  * @brief:  Initialization of UART2
  45.  * @param:  uartNumber: Number of uart that will be initialzed
  46.  * @param:  baudrate: speed of transmission
  47.  * @ret:    none
  48.  */
  49. void Uart2_Init(const int uartNumber, uint32_t baudrate)
  50. {
  51.     uart_config_t uart2_config;
  52.     uart2_config.baud_rate = baudrate;
  53.     uart2_config.data_bits = UART_DATA_8_BITS;
  54.     uart2_config.parity = UART_PARITY_DISABLE;
  55.     uart2_config.stop_bits = UART_STOP_BITS_1;
  56.     uart2_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
  57.     uart2_config.rx_flow_ctrl_thresh = 122;
  58.     uart_param_config(uartNumber, &uart2_config);
  59.     uart_set_pin(uartNumber, UART2_PIN_TX, UART2_PIN_RX, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
  60.     uart_driver_install(uartNumber, BUFFER_SIZE * 2, 0, 80, NULL, 0);
  61. }
  62. //----------------------------------------------------------------------------
  63. /*
  64.  * @brief:  Initialization of UART1
  65.  * @param:  uartNumber: Number of uart to send data
  66.  * @param:  *buffer: pointer to buffer with data to send
  67.  * @ret:    none
  68.  */
  69. void Uart_Send_Data(const int uartNumber, uint8_t *buffer)
  70. {
  71.     uint16_t length = 0;
  72.     while(buffer[length] != '\0')   {
  73.         length++;
  74.     }
  75.     uart_write_bytes(uartNumber, (const char*)buffer, length);
  76. }
  77. //----------------------------------------------------------------------------
  78. /*
  79.  * @brief:  send array of data through selected uart port
  80.  * @param:  uartNumber: number of uart port over what data will be send
  81.  * @param:  *dataTable: pointer to data table
  82.  * @param:  cnt: number of data to send
  83.  * @ret:    none
  84.  *
  85.  */
  86.  void Uart_SendArray(const int uartNumber, uint8_t *dataTable, uint16_t cnt)
  87.  {
  88.      if(cnt == 0){
  89.          return;
  90.      }
  91.      uart_write_bytes(uartNumber, (const char*)dataTable, cnt);
  92.  }
  93.  //----------------------------------------------------------------------------
  94.  /*
  95.   * @brief:     send single byte over selected uart
  96.   * @param:     uartNumber: number of uart port over what data will be send
  97.   * @param:     data: one byte of data to be send
  98.   * @ret:   none
  99.   *
  100.   */
  101.  void Uart_SendByte(const int uartNumber,  uint8_t data)
  102.  {
  103.      char toSend[1] = { data };
  104.      uart_write_bytes(uartNumber, (char *)toSend, 1);
  105.  }
  106.  //----------------------------------------------------------------------------
  107.  /*
  108.   * @brief:     send number as ascii code
  109.   * @param:     uartNumber: number of uart port over what data will be send
  110.   * @param:     uint32_t: number to send
  111.   * @ret:       none
  112.   *
  113.   */
  114.  void Uart_SendNumber(const int uartNumber, uint32_t x)
  115.  {
  116.      char value[10];
  117.      int i = 0;
  118.      do{
  119.          value[i++] = (char)(x%10) + '0';
  120.          x/=10;
  121.      }while(x);
  122.      while(i)
  123.      {
  124.          Uart_SendByte(uartNumber, (char*)value[--i]);
  125.      }
  126.  }

Idąc od góry, funkcje zawierają inicjalizacje wszystkich uartów, funkcje przesyłająca dane np. ciąg znaków. Wykonuje się on aż do momentu znalezienie znaku 0x00. Następnie wysłanie znaków o określonej długości. To z kolei jest przydatne przy wyświetlaniu jakiejś tablicy np. z zdekodowanymi danymi z odpowiedzią od czujników bądź modułów. Kolejna funkcja przesyła pojedynczy bajt. Ostatnia dekoduje numer i przesyła go w formacie ASCII.

Przykładowa funkcja main, w której UART1 działa w trybie echo wygląda następująco:

  1. void uart1Task(void)
  2. {
  3.     const int uart1_num = UART_NUM_1;
  4.     char line[1] = { 0x66 };
  5.     char *ptr = line;
  6.     Uart1_Init(uart1_num, 115200);
  7.     uint8_t *uart1Buffer = (uint8_t*)malloc(BUFFER_SIZE);
  8.     Uart_Send_Data(uart1_num, (uint8_t*)"Uart1 works\r\n");
  9.     Uart_SendByte(uart1_num,  line[0]);
  10.     Uart_SendNumber(uart1_num, 567832);
  11.     while(1){
  12.         int len1 = uart_read_bytes(uart1_num, uart1Buffer, BUFFER_SIZE, 20 / portTICK_RATE_MS);
  13.         uart_write_bytes(uart1_num, (const char*)uart1Buffer, len1);
  14.     }
  15. }

Pozostałe uarty uruchamiane są w ten sam sposób.

Obsługa zadania dla UARTu 1 zawiera przykładowy sposób działania udostępnionych funkcji. Wszystkie zadania dla poszczególnych uartów zostały umieszczone w osobnych funkcjach.

UART0 jest udostępniony jako port domyślny używany do debugowania oraz wgrywania oprogramowania. Dlatego aby wysłać dane na niego można używać funkcji printf.

Biblioteki do projektu można pobrać pod tym linkiem.