精通 ESP32 UART | 完整的發送與接收數據指南
ESP32 UART(通用非同步收發器)在這個完整的指南中,我們將探討 UART 的基礎知識、如何在 ESP32 上設置 UART,並提供實用的數據傳輸示例。無論您是初學者還是經驗豐富的開發者,掌握 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 功能!