精通 ESP32 UART | 完整的發送與接收數據指南


ESP32 UART(通用非同步收發器)在這個完整的指南中,我們將探討 UART 的基礎知識、如何在 ESP32 上設置 UART,並提供實用的數據傳輸示例。無論您是初學者還是經驗豐富的開發者,掌握 UART 通信都能顯著提升您的項目。

ESP32 UART

簡介

UART(Universal Asynchronous Receiver Transmitter)是一種基於硬體的協議,通常用於微控制器與其他設備之間的串行通信。它使用兩條線:TX(傳輸)和 RX(接收)。數據以一位一位的方式在這兩條線上傳送,並且 UART 通信是異步的,這意味著不需要同步時鐘信號來控制數據傳輸。

UART

UART 是一種硬件通信協議,使設備之間能進行串行通信。它使用兩條數據線:

  • TX(傳輸):將數據發送到另一個設備。
  • RX(接收):從另一個設備接收數據。

UART 通信是異步的,這意味著它不需要共享時鐘信號。相反,雙方設備需要協商一個波特率(每秒傳輸的比特數),以確保正確的數據傳輸。

ESP32 UART 硬體特性

我們將使用 ESP32 的 UART 端口來進行串行通信。這是硬體接線的基本流程:

  • TX 引腳:將 ESP32 的 TX(傳輸端)引腳連接到接收設備的 RX(接收端)引腳。
  • RX 引腳:將 ESP32 的 RX 引腳連接到發送設備的 TX 引腳。
  • GND 引腳:將 ESP32 的 GND 引腳連接到外部設備的 GND。

接線示意圖:

Module            PC or Mac
ESP32 (TX) ----> 外部設備 (RX)
ESP32 (RX) <---- 外部設備 (TX)
GND -------------------- GND

使用 ESP32-PICO-KIT 開發板

硬體背景

  • GPIO16 和 GPIO17 是 ESP32 模組內部 UART0 的預設腳位,與 USB-to-UART 接口相連,用於燒錄程式和序列監視。
  • 這些腳位被內部使用,無法作為外部 UART 通訊的 TX 和 RX 腳位。

替代方法

  • 改用 GPIO21 (TX) 和 GPIO22 (RX) 作為 UART2 的傳輸和接收腳位。
  • GPIO21 和 GPIO22 是通用腳位,可用於 UART 或其他外設通訊,且在 ESP32-PICO-KIT 中是自由可用的。

編寫代碼

首先,我們需要初始化 ESP32 UART 並配置波特率、數據位、停止位等參數。ESP32 支援最多三個 UART 埠,每個 UART 埠都可以配置不同的設置。

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "esp_log.h"

static const char *TAG = "uart_test";

// UART configuration
#define UART_NUM UART_NUM_2        // Using UART2
#define UART_TX_PIN GPIO_NUM_21    // TX Pin
#define UART_RX_PIN GPIO_NUM_22    // RX Pin
#define UART_BAUD_RATE 115200      // Baud rate
#define BUF_SIZE 256               // Buffer size

// UART initialization function
static void uart_init() {
    // Check if UART driver is already installed, and remove it if necessary
    if (uart_is_driver_installed(UART_NUM)) {
        uart_driver_delete(UART_NUM);
        vTaskDelay(pdMS_TO_TICKS(100));  // Allow time for proper cleanup
    }

    // UART configuration structure
    uart_config_t uart_config = {
        .baud_rate = UART_BAUD_RATE,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .rx_flow_ctrl_thresh = 122,
        .source_clk = UART_SCLK_APB,
    };

    // Configure UART parameters
    esp_err_t err = uart_param_config(UART_NUM, &uart_config);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "UART parameter config failed with error: %d", err);
        return;
    }

    // Set UART pins
    err = uart_set_pin(UART_NUM, UART_TX_PIN, UART_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "UART set pin failed with error: %d", err);
        return;
    }

    // Install UART driver
    err = uart_driver_install(UART_NUM, BUF_SIZE, BUF_SIZE, 0, NULL, 0);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "UART driver install failed with error: %d", err);
        return;
    }

    ESP_LOGI(TAG, "UART initialized successfully");
}

// UART transmission task
void uart_tx_task(void *arg) {
    const char* test_str = "Hi UART\n";
    while(1) {
        // Send test string
        if(uart_write_bytes(UART_NUM, test_str, strlen(test_str)) > 0) {
            ESP_LOGI(TAG, "Sent: %s", test_str);
        } else {
            ESP_LOGE(TAG, "Failed to send data");
        }
        vTaskDelay(pdMS_TO_TICKS(2000));  // Send data every 2 seconds
    }
}

// UART reception task
void uart_rx_task(void *arg) {
    uint8_t data[BUF_SIZE];  // Buffer to store received data
    const char* welcome_msg = "Hello, welcome!\n";
    
    while(1) {
        // Read data from UART
        int len = uart_read_bytes(UART_NUM, data, sizeof(data) - 1, pdMS_TO_TICKS(100));
        if(len > 0) {
            data[len] = '\0';  // Null-terminate the received string
            ESP_LOGI(TAG, "Received %d bytes: %s", len, data);
            
            // Check if received string matches "Hi UART"
            if (strncmp((char*)data, "Hi UART", 7) == 0) {
                uart_write_bytes(UART_NUM, welcome_msg, strlen(welcome_msg));  // Send welcome message
                ESP_LOGI(TAG, "Sent welcome message");
            }
        }
        vTaskDelay(pdMS_TO_TICKS(50));  // Add delay to avoid task overload
    }
}

void app_main(void)
{
    // Wait for the system to stabilize
    vTaskDelay(pdMS_TO_TICKS(1000));
    
    // Initialize UART
    uart_init();
    
    // Allow additional time for initialization to complete
    vTaskDelay(pdMS_TO_TICKS(100));
    
    // Create tasks for sending and receiving
    xTaskCreate(uart_tx_task, "uart_tx_task", 2048, NULL, 5, NULL);
    xTaskCreate(uart_rx_task, "uart_rx_task", 2048, NULL, 5, NULL);
    
    ESP_LOGI(TAG, "UART tasks created");
}

代碼解釋

這段代碼配置了 ESP32-PICO-KIT V4 的 UART2,使用 GPIO21 作為 TX (傳送腳位) 和 GPIO22 作為 RX (接收腳位)。實現了一個簡單的通信系統,具體如下:

  • UART 初始化 (uart_init):配置了 UART 的參數,如波特率、數據位、停止位和流控制,並設置了 TX 和 RX 腳位(GPIO21 和 GPIO22),最後安裝了 UART 驅動程式。
  • UART 發送任務 (uart_tx_task):每隔 2 秒發送一次字符串 "Hi UART\n"。如果傳送成功,會記錄已發送的訊息;如果失敗,則記錄錯誤訊息。
  • UART 接收任務 (uart_rx_task):持續監聽 UART2 的輸入資料。如果接收到的訊息以 "Hi UART" 開頭,則回傳 "Hello, welcome!\n"

輸出說明

ESP32 UART 初始化:

  • 成功初始化 UART 後,會記錄相關訊息,顯示設置了 TX 腳位 (GPIO21) 和 RX 腳位 (GPIO22)。
  • 若在配置過程中發生錯誤,則會顯示錯誤訊息。

ESP32 UART 發送任務 (uart_tx_task):

  • 每 2 秒發送一次字符串 "Hi UART\n"。如果發送成功,會記錄發送的訊息:
I (698413) uart_test: Sent: Hi UART

ESP32 UART 接收任務 (uart_rx_task):

  • 持續監聽接收到的資料。如果接收到的訊息是 "Hi UART",會回應 "Hello, welcome!\n" 並記錄:
I (722413) uart_test: Received 7 bytes: Hi UART
I (726413) uart_test: Sent welcome message

結果輸出

  • GPIO 腳位:TX 和 RX 腳位分別設為 GPIO21 和 GPIO22,這些腳位可用於 UART 通信。
  • 工具:可以使用 Tera Term、CoolTerm 或 PuTTY 等終端程式來與 ESP32 進行串行通信,並查看 ESP32 的輸出。
I (0) uart_test: UART initialized successfully
I (1) uart_test: Sent: Hi UART
I (2000) uart_test: Received 7 bytes: Hi UART
I (2001) uart_test: Sent welcome message

結論

掌握 ESP32 的 UART 通信為您的項目開啟了無限可能。通過理解基礎知識並實施本文提供的示例,您將能夠在設計中集成可靠且高效的串行通信。無論是用於簡單的數據傳輸還是更複雜的應用,UART 都是嵌入式系統世界中一個多功能且寶貴的工具。

ESP32 提供了強大的硬體支持,擁有多個 UART 端口,使其成為需要串行通信的項目的理想選擇。我們希望這篇指南能幫助您快速入門,並在您的下一個 ESP32 項目中順利實現 UART 功能!