【ESP32-IDF筆記】09-UART配置和使用

環境配置

Visual Studio Code :版本1.98.2
ESP32:ESP32-S3
ESP-IDF:V5.4
支持型號:ESP32、ESP32-C2、ESP32-C3、ESP32-C5、ESP32-C6、ESP32-C61、ESP32-H2、ESP32-P4、 ESP32-S2、ESP32-S3

簡介

通用異步接收器/發送器 (UART) 屬于一種硬件功能,通過使用 RS232、RS422、RS485 等常見異步串行通信接口來處理通信時序要求和數據幀。UART 是實現不同設備之間全雙工或半雙工數據交換的一種常用且經濟的方式。

ESP32-S3 芯片有 3 個 UART 控制器(也稱為端口),每個控制器都有一組相同的寄存器以簡化編程并提高靈活性。

每個 UART 控制器可以獨立配置波特率、數據位長度、位順序、停止位位數、奇偶校驗位等參數。所有具備完整功能的 UART 控制器都能與不同制造商的 UART 設備兼容,并且支持紅外數據協會 (IrDA) 定義的標準協議。

主要特性

UART 控制器具有如下特性:

  • 支持三個可預分頻的時鐘源
  • 可編程收發波特率
  • 三個 UART 的發送 FIFO 以及接收 FIFO 共享 1024 x 8-bit RAM
  • 全雙工異步通信
  • 支持輸入信號波特率自檢功能
  • 支持 5/6/7/8 位數據長度
  • 支持 1/1.5/2 個停止位
  • 支持奇偶校驗位
  • 支持 AT_CMD 特殊字符檢測
  • 支持 RS485 協議
  • 支持 IrDA 協議
  • 支持 GDMA 高速數據通信
  • 支持 UART 喚醒模式
  • 支持軟件流控和硬件流控

UART 架構

在這里插入圖片描述
在這里插入圖片描述
圖 26.3-2 為 UART 基本架構圖。UART 模塊工作在兩個時鐘域:APB_CLK 時鐘域和 Core 時鐘域。UART Core有三個時鐘源:80-MHz APB_CLK、RC_FAST_CLK 以及晶振時鐘 XTAL_CLK(詳情請參考章節 7 復位和時鐘)。可以通過配置 UART_SCLK_SEL 來選擇時鐘源。分頻器用于對時鐘源進行分頻,然后產生時鐘信號來驅動UART Core 模塊。

UART 控制器可以分為兩個功能塊:發送塊和接收塊。

發送塊包含一個發送 FIFO 用于緩存待發送的數據。軟件可以通過 APB 總線向 Tx_FIFO 寫數據,也可以通過GDMA 將數據搬入 Tx_FIFO。Tx_FIFO_Ctrl 用于控制 Tx_FIFO 的讀寫過程,當 Tx_FIFO 非空時,Tx_FSM 通過Tx_FIFO_Ctrl 讀取數據,并將數據按照配置的幀格式轉化成比特流。比特流輸出信號 txd_out 可以通過配置UART_TXD_INV 寄存器實現取反功能。

接收塊包含一個接收 FIFO 用于緩存待處理的數據。輸入比特流 rxd_in 可以輸入到 UART 控制器。可以通過UART_RXD_INV 寄存器實現取反。Baudrate_Detect 通過檢測最小比特流輸入信號的脈寬來測量輸入信號的波特率。Start_Detect 用于檢測數據的 START 位,當檢測到 START 位之后,Rx_FSM 通過 Rx_FIFO_Ctrl 將幀解析后的數據存入 Rx_FIFO 中。軟件可以通過 APB 總線讀取 Rx_FIFO 中的數據也可以使用 GDMA 方式進行數據接收。

HW_Flow_Ctrl 通過標準 UART RTS 和 CTS(rtsn_out 和 ctsn_in)流控信號來控制 rxd_in 和 txd_out 的數據流。SW_Flow_Ctrl 通過在發送數據流中插入特殊字符以及在接收數據流中檢測特殊字符來進行數據流的控制。

當 UART 處于 Light-sleep 狀態(詳情請參考章節 10 低功耗管理 (RTC_CNTL))時,Wakeup_Ctrl 開始計算rxd_in 的上升沿個數,當上升沿個數大于等于(UART_ACTIVE_THRESHOLD + 3)時產生 wake_up 信號給 RTC模塊,由 RTC 來喚醒 ESP32-S3 芯片。

功能描述

UART RAM

在這里插入圖片描述

ESP32-S3 芯片中三個 UART 控制器共用 1024 x 8-bit RAM 空間。如圖 26.4-1 所示,RAM 以 block 為單位進行分配,1 block 為 128x8 bits。圖 26.4-1 所示為默認情況下三個 UART 控制器的 Tx_FIFO 和 Rx_FIFO 占用 RAM的情況。通過配置 UART_TX_SIZE 可以對 UARTn 的 Tx_FIFO 以 1 block 為單位進行擴展,通過配置UART_RX_SIZE 可以對 UARTn 的 Rx_FIFO 以 1 block 為單位進行擴展:

  • UART0 Tx_FIFO 可以從地址 0 擴展到整個 RAM 空間;
  • UART1 Tx_FIFO 可以從地址 128 擴展到 RAM 的尾地址;
  • UART2 Tx_FIFO 可以從地址 256 擴展到 RAM 的尾地址;
  • UART0 Rx_FIFO 可以從地址 512 擴展到 RAM 的尾地址;
  • UART1 Rx_FIFO 可以從地址 640 擴展到 RAM 的尾地址;
  • UART2 Rx_FIFO 可以從地址 768 擴展到 RAM 的尾地址。

需要注意的是所有 UART 的 FIFO 起始地址是固定的,因此前一個 UART 的 FIFO 空間向后擴展會占用后面 UART的 FIFO 空間。比如,設置 UART0 的 UART_TX_SIZE 為 2,則 UART0 Tx_FIFO 的地址從 0 擴展到 255。這時,UART1 Tx_FIFO 的默認空間被占用,這時將不能使用 UART1 發送器功能。

當三個 UART 控制器都不工作時,可以通過置位 UART_MEM_FORCE_PD 來使 RAM 進入低功耗狀態。

UARTn 的 Tx_FIFO 可以通過置位 UART_TXFIFO_RST 來復位, UARTn 的 Rx_FIFO 可以通過置位UART_RXFIFO_RST 來復位。

配置 UART_TXFIFO_EMPTY_THRHD 可以設置 Tx_FIFO 空信號閾值,當存儲在 Tx_FIFO 中的數據量等于或小于UART_TXFIFO_EMPTY_THRHD 時會產生中斷 UART_TXFIFO_EMPTY_INT;配置 UART_RXFIFO_FULL_THRHD可以設置 Rx_FIFO 滿信號閾值,當儲存在 Rx_FIFO 中的數據量大于 UART_RXFIFO_FULL_THRHD 會產生中斷UART_RXFIFO_FULL_INT。另外,當 Rx_FIFO 中儲存的數據量超過其能存儲的最大值時,會產生UART_RXFIFO_OVF_INT 中斷。

對于 TX FIFO 和 RX FIFO 的訪問,可以通過 APB 總線或者 GDMA 外設接口兩種方式。通過 APB 總線訪問,即通過寄存器 UART_FIFO_REG 訪問 FIFO。您可以寫 UART_RXFIFO_RD_BYTE 將數據存入 TX FIFO,也可以讀取該字段獲取 RX FIFO 中的數據。

喚醒

UART0 和 UART1 支持喚醒功能。當 UART 處于 Light-sleep 狀態時,Wakeup_Ctrl 開始統計 rxd_in 的上升沿個數,當上升沿個數大于等于(UART_ACTIVE_THRESHOLD + 3)時產生 wake_up 信號給 RTC 模塊,由 RTC 來喚醒 ESP32-S3 芯片。

使用 UART 喚醒之后,需要通過在 Active 模式下向 UART 傳輸數據或是復位整個 UART 模塊清除 wake_up 信號,否則下一次喚醒所需的上升沿個數將減少。

回環功能

UARTn 支持回環功能。置位 UART_LOOPBACK 即開啟 UART 的回環測試功能。此時 UART 的輸出信號 txd_out和其輸入信號 rxd_in 相連,rtsn_out 和 ctsn_in 相連,dtrn_out 和 dsrn_out 相連。數據之后通過 txd_out 發送。當接收的數據與發送的數據相同時表明 UART 能夠正常發送和接收數據。

UART 中斷

  • UART_AT_CMD_CHAR_DET_INT:當接收器檢測到 AT_CMD 字符時觸發此中斷。
  • UART_RS485_CLASH_INT:在 RS485 模式下檢測到發送器和接收器之間的沖突時觸發此中斷。
  • UART_RS485_FRM_ERR_INT:在 RS485 模式下檢測到發送塊發送的數據幀錯誤時觸發此中斷。
  • UART_RS485_PARITY_ERR_INT:在 RS485 模式下檢測到發送塊發送的數據校驗位錯誤時觸發此中斷。
  • UART_TX_DONE_INT:當發送器發送完 FIFO 中的所有數據時觸發此中斷。
  • UART_TX_BRK_IDLE_DONE_INT:發送器發送完最后一個數據后保持空閑狀態時觸發此中斷。標記為空閑狀態的最短時間由閾值決定(可配置)。
  • UART_TX_BRK_DONE_INT:當發送 FIFO 中的數據發送完之后發送器完成了發送 NULL 則觸發此中斷。
  • UART_GLITCH_DET_INT:當接收器在起始位的中點處檢測到毛刺時觸發此中斷。
  • UART_SW_XOFF_INT:UART_SW_FLOW_CON_EN 置位時,當接收器接收到 XOFF 字符時觸發此中斷。
  • UART_SW_XON_INT:UART_SW_FLOW_CON_EN 置位時,當接收器接收到 XON 字符時觸發此中斷。
  • UART_RXFIFO_TOUT_INT:當接收器接收一個字節的時間大于 UART_RX_TOUT_THRHD 時觸發此中斷。
  • UART_BRK_DET_INT:當接收器在停止位之后檢測到一個 NULL(即傳輸一個 NULL 的時間內保持邏輯低電平)時觸發此中斷。
  • UART_CTS_CHG_INT:當接收器檢測到 CTSn 信號的沿變化時觸發此中斷。
  • UART_DSR_CHG_INT:當接收器檢測到 DSRn 信號的沿變化時觸發此中斷。
  • UART_RXFIFO_OVF_INT:當接收器接收到的數據量多于 FIFO 的存儲量時觸發此中斷。
  • UART_FRM_ERR_INT:當接收器檢測到數據幀錯誤時觸發此中斷。
  • UART_PARITY_ERR_INT:當接收器檢測到校驗位錯誤時觸發此中斷。
  • UART_TXFIFO_EMPTY_INT:當發送 FIFO 中的數據量少于 UART_TXFIFO_EMPTY_THRHD 所指定的值時觸發此中斷。
  • UART_RXFIFO_FULL_INT:當接收器接收到的數據多于 UART_RXFIFO_FULL_THRHD 所指定的值時觸發此中斷。
  • UART_WAKEUP_INT:UART 被喚醒時產生此中斷

功能概述

下文介紹了如何使用 UART 驅動程序的函數和數據類型在 ESP32-S3 和其他 UART 設備之間建立通信。基本編程流程分為以下幾個步驟:

  1. 設置通信參數 - 設置波特率、數據位、停止位等
  2. 設置通信管腳 - 分配連接設備的管腳
  3. 安裝驅動程序 - 為 UART 驅動程序分配 ESP32-S3 資源
  4. 運行 UART 通信 - 發送/接收數據
  5. 使用中斷 - 觸發特定通信事件的中斷
  6. 刪除驅動程序 - 如無需 UART 通信,則釋放已分配的資源

步驟 1 到 3 為配置階段,步驟 4 為 UART 運行階段,步驟 5 和 6 為可選步驟。

UART 驅動程序函數通過 uart_port_t 識別不同的 UART 控制器。調用以下所有函數均需此標識。

設置通信參數

UART 通信參數可以在一個步驟中完成全部配置,也可以在多個步驟中單獨配置。

一次性配置所有參數

調用函數 uart_param_config() 并向其傳遞 uart_config_t 結構體,uart_config_t 結構體應包含所有必要的參數。請參考以下示例。

const uart_port_t uart_num = UART_NUM_2;
uart_config_t uart_config = {.baud_rate = 115200,.data_bits = UART_DATA_8_BITS,.parity = UART_PARITY_DISABLE,.stop_bits = UART_STOP_BITS_1,.flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS,.rx_flow_ctrl_thresh = 122,
};
// Configure UART parameters
ESP_ERROR_CHECK(uart_param_config(uart_num, &uart_config));
分步依次配置每個參數

調用下表中的專用函數,能夠單獨配置特定參數。如需重新配置某個參數,也可使用這些函數。

單獨配置特定參數的函數

配置參數函數
波特率uart_set_baudrate()
傳輸位調用 uart_set_word_length() 設置 uart_word_length_t
奇偶控制調用 uart_parity_t 設置 uart_set_parity()
停止位調用 uart_set_stop_bits() 設置 uart_stop_bits_t
硬件流控模式調用 uart_set_hw_flow_ctrl() 設置 uart_hw_flowcontrol_t
通信模式調用 uart_set_mode() 設置 uart_mode_t

表中每個函數都可使用 _get_ 對應項來查看當前設置值。例如,查看當前波特率值,請調用 uart_get_baudrate()

設置通信管腳

通信參數設置完成后,可以配置其他 UART 設備連接的 GPIO 管腳。調用函數 uart_set_pin(),指定配置 Tx、Rx、RTS 和 CTS 信號的 GPIO 管腳編號。如要為特定信號保留當前分配的管腳編號,可傳遞宏 UART_PIN_NO_CHANGE

請為不使用的管腳都指定為宏 UART_PIN_NO_CHANGE

// Set UART pins(TX: IO4, RX: IO5, RTS: IO18, CTS: IO19)
ESP_ERROR_CHECK(uart_set_pin(UART_NUM_2, 4, 5, 18, 19));

安裝驅動程序

通信管腳設置完成后,請調用 uart_driver_install() 安裝驅動程序并指定以下參數:

  • UART 控制器編號
  • Tx 環形緩沖區的大小
  • Rx 環形緩沖區的大小
  • 指向事件隊列句柄的指針
  • 事件隊列大小
  • 分配中斷的標志

該函數將為 UART 驅動程序分配所需的內部資源。

// Setup UART buffered IO with event queue
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_2, uart_buffer_size, \uart_buffer_size, 10, &uart_queue, 0));

此步驟完成后,可連接外部 UART 設備檢查通信。

運行 UART 通信

串行通信由每個 UART 控制器的有限狀態機 (FSM) 控制。

發送數據的過程分為以下步驟:

  1. 將數據寫入 Tx FIFO 緩沖區
  2. FSM 序列化數據
  3. FSM 發送數據

接收數據的過程類似,只是步驟相反:

  1. FSM 處理且并行化傳入的串行流
  2. FSM 將數據寫入 Rx FIFO 緩沖區
  3. 從 Rx FIFO 緩沖區讀取數據

因此,應用程序僅會通過 uart_write_bytes()uart_read_bytes() 從特定緩沖區寫入或讀取數據,其余工作由 FSM 完成。

發送數據

發送數據準備好后,調用函數 uart_write_bytes(),并向其傳遞數據緩沖區的地址和數據長度。該函數會立即或在有足夠可用空間時將數據復制到 Tx 環形緩沖區,隨后退出。當 Tx FIFO 緩沖區中有可用空間時,中斷服務例程 (ISR) 會在后臺將數據從 Tx 環形緩沖區移動到 Tx FIFO 緩沖區。調用函數請參考以下代碼。

// 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));

函數 uart_write_bytes_with_break()uart_write_bytes() 類似,但在傳輸結束時會添加串行中斷信號。“串行中斷信號”意味著 Tx 線保持低電平的時間長于一個數據幀。

// Write data to UART, end with a break signal.
uart_write_bytes_with_break(uart_num, "test break\n",strlen("test break\n"), 100);

能夠將數據寫入 Tx FIFO 緩沖區的另一函數是 uart_tx_chars()。 與 uart_write_bytes() 不同,此函數在沒有可用空間之前不會阻塞。相反,它將寫入所有可以立即放入硬件 Tx FIFO 的數據,然后返回寫入的字節數。

“配套”函數 uart_wait_tx_done() 用于監聽 Tx FIFO 緩沖區的狀態,并在緩沖區為空時返回。

// Wait for packet to be sent
const uart_port_t uart_num = UART_NUM_2;
ESP_ERROR_CHECK(uart_wait_tx_done(uart_num, 100)); // wait timeout is 100 RTOS ticks (TickType_t)
接收數據

一旦 UART 接收了數據,并將其保存在 Rx FIFO 緩沖區中,就需要使用函數 uart_read_bytes() 檢索數據。讀取數據之前,調用 uart_get_buffered_data_len() 能夠查看 Rx FIFO 緩沖區中可用的字節數。請參考以下示例。

// Read data from UART.
const uart_port_t 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));
length = uart_read_bytes(uart_num, data, length, 100);

如果不再需要 Rx FIFO 緩沖區中的數據,可以調用 uart_flush() 清空緩沖區。

軟件流控

如果硬件流控處于禁用狀態,可使用函數 uart_set_rts()uart_set_dtr() 分別手動設置 RTS 和 DTR 信號電平。

通信方式選擇

UART 控制器支持多種通信模式,使用函數 uart_set_mode() 可以選擇模式。選擇特定模式后,UART 驅動程序將處理已連接 UART 設備的相應行為。例如,使用 RTS 線控制 RS485 驅動芯片,能夠實現半雙工 RS485 通信。

// Setup UART in rs485 half duplex mode
ESP_ERROR_CHECK(uart_set_mode(uart_num, UART_MODE_RS485_HALF_DUPLEX));

使用中斷

根據特定的 UART 狀態或檢測到的錯誤,可以生成許多不同的中斷。ESP32-S3 技術參考手冊 > UART 控制器 (UART) > UART 中斷 和 UHCI 中斷 [PDF] 中提供了可用中斷的完整列表。調用 uart_enable_intr_mask()uart_disable_intr_mask() 能夠分別啟用或禁用特定中斷。

UART 驅動提供了一種便利的方法來處理特定的中斷,即將中斷包裝成相應的事件。這些事件定義在 uart_event_type_t 中,FreeRTOS 隊列功能可將這些事件報告給用戶應用程序。

要接收已發生的事件,請調用 uart_driver_install() 函數并獲取返回的事件隊列句柄。

UART 驅動可處理的事件包括:

  • FIFO 空間溢出 (UART_FIFO_OVF):當接收到的數據超過 FIFO 的存儲能力時,Rx FIFO 會觸發中斷。

    • (可選)配置 FIFO 閾值:在結構體 uart_intr_config_t 中輸入閾值,然后調用 uart_intr_config() 使能配置。這有助于驅動及時處理 RX FIFO 中的數據,避免 FIFO 溢出。
    • 啟用中斷:調用函數 uart_enable_rx_intr()
    • 禁用中斷:調用函數 uart_disable_rx_intr()
    const uart_port_t uart_num = UART_NUM_2;
    // Configure a UART interrupt threshold and timeout
    uart_intr_config_t uart_intr = {.intr_enable_mask = UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT,.rxfifo_full_thresh = 100,.rx_timeout_thresh = 10,
    };
    ESP_ERROR_CHECK(uart_intr_config(uart_num, &uart_intr));// Enable UART RX FIFO full threshold and timeout interrupts
    ESP_ERROR_CHECK(uart_enable_rx_intr(uart_num));
    
  • 模式檢測 (UART_PATTERN_DET):在檢測到重復接收/發送同一字符的“模式”時觸發中斷,例如,模式檢測可用于檢測命令字符串末尾是否存在特定數量的相同字符(“模式”)。可以調用以下函數:

    • 配置并啟用此中斷:調用 uart_enable_pattern_det_baud_intr()
    • 禁用中斷:調用 uart_disable_pattern_det_intr()
    //Set UART pattern detect function
    uart_enable_pattern_det_baud_intr(EX_UART_NUM, '+', PATTERN_CHR_NUM, 9, 0, 0);
    
  • 其他事件:UART 驅動可處理的其他事件包括數據接收 (UART_DATA)、環形緩沖區已滿 (UART_BUFFER_FULL)、在停止位后檢測到 NULL (UART_BREAK)、奇偶校驗錯誤 (UART_PARITY_ERR)、以及幀錯誤 (UART_FRAME_ERR)。

括號中的字符串為相應的事件名稱。請參考 peripherals/uart/uart_events 中處理 UART 事件的示例。

刪除驅動程序

如不再需要與 uart_driver_install() 建立通信,則可調用 uart_driver_delete() 刪除驅動程序,釋放已分配的資源。

宏指令

API 還定義了一些宏指令。例如,UART_HW_FIFO_LEN 定義了硬件 FIFO 緩沖區的長度,UART_BITRATE_MAX 定義了 UART 控制器支持的最大波特率。

應用示例

示例1-uart_async_rxtxtasks

**說明:**演示了通過同一 UART 接口完成兩個獨立任務的通信。
其中一個任務定期發送 “Hello world”,另一個任務接收并打印 UART 接收到的數據。

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include "driver/uart.h"
#include "string.h"
#include "driver/gpio.h"static const int RX_BUF_SIZE = 1024;#define TXD_PIN (GPIO_NUM_4)
#define RXD_PIN (GPIO_NUM_5)void init(void)
{const uart_config_t uart_config = {.baud_rate = 115200,.data_bits = UART_DATA_8_BITS,.parity = UART_PARITY_DISABLE,.stop_bits = UART_STOP_BITS_1,.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,.source_clk = UART_SCLK_DEFAULT,};// We won't use a buffer for sending data.uart_driver_install(UART_NUM_1, RX_BUF_SIZE * 2, 0, 0, NULL, 0);uart_param_config(UART_NUM_1, &uart_config);uart_set_pin(UART_NUM_1, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
}int sendData(const char* logName, const char* data)
{const int len = strlen(data);const int txBytes = uart_write_bytes(UART_NUM_1, data, len);ESP_LOGI(logName, "Wrote %d bytes", txBytes);return txBytes;
}static void tx_task(void *arg)
{static const char *TX_TASK_TAG = "TX_TASK";esp_log_level_set(TX_TASK_TAG, ESP_LOG_INFO);while (1) {sendData(TX_TASK_TAG, "Hello world");vTaskDelay(2000 / portTICK_PERIOD_MS);}
}static void rx_task(void *arg)
{static const char *RX_TASK_TAG = "RX_TASK";esp_log_level_set(RX_TASK_TAG, ESP_LOG_INFO);uint8_t* data = (uint8_t*) malloc(RX_BUF_SIZE + 1);while (1) {const int rxBytes = uart_read_bytes(UART_NUM_1, data, RX_BUF_SIZE, 1000 / portTICK_PERIOD_MS);if (rxBytes > 0) {data[rxBytes] = 0;ESP_LOGI(RX_TASK_TAG, "Read %d bytes: '%s'", rxBytes, data);ESP_LOG_BUFFER_HEXDUMP(RX_TASK_TAG, data, rxBytes, ESP_LOG_INFO);}}free(data);
}void app_main(void)
{init();xTaskCreate(rx_task, "uart_rx_task", 1024 * 2, NULL, configMAX_PRIORITIES - 1, NULL);xTaskCreate(tx_task, "uart_tx_task", 1024 * 2, NULL, configMAX_PRIORITIES - 2, NULL);
}

示例2-uart_echo

**說明:**演示了使用 UART 接口回顯接收到的所有數據。
引腳連接示意:

  -----------------------------------------------------------------------------------------| Target chip Interface | Kconfig Option     | Default ESP Pin      | External UART Pin || ----------------------|--------------------|----------------------|--------------------| Transmit Data (TxD)   | EXAMPLE_UART_TXD   | GPIO4                | RxD               || Receive Data (RxD)    | EXAMPLE_UART_RXD   | GPIO5                | TxD               || Ground                | n/a                | GND                  | GND               |-----------------------------------------------------------------------------------------

代碼

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
#include "esp_log.h"/**
這是一個示例,它將接收到的任何數據通過配置的UART回傳給發送者,并且硬件流控制關閉。它不使用UART驅動程序事件隊列。- 端口:配置的UART- 接收(Rx)緩沖區:開啟- 發送(Tx)緩沖區:關閉- 流控制:關閉- 事件隊列:關閉- 引腳分配:見下方定義(參見Kconfig)*/#define ECHO_TEST_TXD (GPIO_NUM_4)
#define ECHO_TEST_RXD (GPIO_NUM_5)#define ECHO_UART_PORT_NUM      (UART_NUM_1)
#define ECHO_UART_BAUD_RATE     (115200)
#define ECHO_TASK_STACK_SIZE    (1024)static const char *TAG = "UART TEST";#define BUF_SIZE (1024)static void echo_task(void *arg)
{/* 配置UART驅動程序的參數,包括通信引腳并安裝驅動程序。*/uart_config_t uart_config = {.baud_rate = ECHO_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,.source_clk = UART_SCLK_DEFAULT,};int intr_alloc_flags = 0;#if CONFIG_UART_ISR_IN_IRAMintr_alloc_flags = ESP_INTR_FLAG_IRAM;
#endifESP_ERROR_CHECK(uart_driver_install(ECHO_UART_PORT_NUM, BUF_SIZE, 0, 0, NULL, intr_alloc_flags));ESP_ERROR_CHECK(uart_param_config(ECHO_UART_PORT_NUM, &uart_config));ESP_ERROR_CHECK(uart_set_pin(UART_NUM_1, ECHO_TEST_TXD , ECHO_TEST_RXD , UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));// 配置一個臨時緩沖區來處理傳入的數據。uint8_t *data = (uint8_t *) malloc(BUF_SIZE);while (1) {// 從串口讀數據int len = uart_read_bytes(ECHO_UART_PORT_NUM, data, (BUF_SIZE - 1), 20 / portTICK_PERIOD_MS);// 寫數據到串口uart_write_bytes(ECHO_UART_PORT_NUM, (const char *) data, len);if (len) {data[len] = '\0';ESP_LOGI(TAG, "Recv str: %s", (char *) data);}}
}void app_main(void)
{xTaskCreate(echo_task, "uart_echo_task", ECHO_TASK_STACK_SIZE, NULL, 10, NULL);
}

示例3 uart_echo_rs485

**說明:**這是一個示例,它會在RS485網絡中將接收到的UART端口數據原封不動地返回給發送者。該示例使用ESP-IDF UART軟件驅動程序,并在RS485半雙工傳輸模式下工作,需要外部連接總線驅動器。此示例展示的方法可以在用戶應用程序中用于在RS485網絡中發送/接收數據。

         VCC ---------------+                               +--------------- VCC|                               |+-------x-------+               +-------x-------+RXD <------| RO            |               |             RO|-----> RXD|              B|---------------|B              |TXD ------>| DI  MAX483    |    \  /       |    MAX483   DI|<----- TXD
ESP32 BOARD         |               |   RS-485 side |               |  SERIAL ADAPTER SIDERTS --+--->| DE            |    /  \       |             DE|---+|    |              A|---------------|A              |   |+----| /RE           |               |            /RE|---+-- RTS+-------x-------+               +-------x-------+|                               |---                             ---

將USB轉RS485適配器連接到計算機,然后將適配器的A/B輸出線與連接到ESP32芯片的RS485驅動器的相應A/B輸出線相連。
引腳說明:

  ------------------------------------------------------------------------------------------------------------------------------|  UART Interface       | #define            | Default pin for ESP32 | Default pins for others   | External RS485 Driver Pin || ----------------------|--------------------|-----------------------|---------------------------|---------------------------|| Transmit Data (TxD)   | CONFIG_MB_UART_TXD | GPIO4                | GPIO9                     | DI                        || Receive Data (RxD)    | CONFIG_MB_UART_RXD | GPIO5                | GPIO8                     | RO                        || Request To Send (RTS) | CONFIG_MB_UART_RTS | GPIO18                | GPIO10                    | ~RE/DE                    || Ground                | n/a                | GND                   | GND                       | GND                       |------------------------------------------------------------------------------------------------------------------------------

代碼

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "driver/uart.h"
#include "freertos/queue.h"
#include "esp_log.h"
#include "sdkconfig.h"/*** This is a example which echos any data it receives on UART back to the sender using RS485 interface in half duplex mode.
*/
#define TAG "RS485_ECHO_APP"// Note: Some pins on target chip cannot be assigned for UART communication.
// Please refer to documentation for selected board and target to configure pins using Kconfig.
#define ECHO_TEST_TXD           (GPIO_NUM_4)
#define ECHO_TEST_RXD           (GPIO_NUM_5)// RTS for RS485 Half-Duplex Mode manages DE/~RE
#define ECHO_TEST_RTS           (GPIO_NUM_18)// CTS is not used in RS485 Half-Duplex Mode
#define ECHO_TEST_CTS           (UART_PIN_NO_CHANGE)#define BUF_SIZE                (127)
#define BAUD_RATE               (115200)// Read packet timeout
#define PACKET_READ_TICS        (100 / portTICK_PERIOD_MS)
#define ECHO_TASK_STACK_SIZE    (1024*4)
#define ECHO_TASK_PRIO          (10)
#define ECHO_UART_PORT          (UART_NUM_1)// UART超時閾值=接收引腳上狀態不變的符號數量(約10個時鐘周期)
#define ECHO_READ_TOUT          (3) // 3.5T * 8 = 28 ticks, TOUT=3 -> ~24..33 ticksstatic void echo_send(const int port, const char* str, uint8_t length)
{if (uart_write_bytes(port, str, length) != length) {ESP_LOGE(TAG, "Send data critical failure.");// 在這里添加代碼以處理發送失敗的情況。}
}// 一個使用UART硬件流控制的回顯測試示例。
static void echo_task(void *arg)
{const int uart_num = ECHO_UART_PORT;uart_config_t uart_config = {.baud_rate = 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_DEFAULT,};// Set UART log levelesp_log_level_set(TAG, ESP_LOG_INFO);ESP_LOGI(TAG, "Start RS485 application test and configure UART.");// 安裝UART驅動程序(這里不需要事件隊列)// 在這個示例中,我們甚至沒有為發送數據使用緩沖區。ESP_ERROR_CHECK(uart_driver_install(uart_num, BUF_SIZE * 2, 0, 0, NULL, 0));// 配置UART參數ESP_ERROR_CHECK(uart_param_config(uart_num, &uart_config));ESP_LOGI(TAG, "UART set pins, mode and install driver.");// 根據KConfig設置配置UART引腳。ESP_ERROR_CHECK(uart_set_pin(uart_num, ECHO_TEST_TXD, ECHO_TEST_RXD, ECHO_TEST_RTS, ECHO_TEST_CTS));// 設置RS485半雙工模式ESP_ERROR_CHECK(uart_set_mode(uart_num, UART_MODE_RS485_HALF_DUPLEX));// 設置UART TOUT功能的讀取超時ESP_ERROR_CHECK(uart_set_rx_timeout(uart_num, ECHO_READ_TOUT));// 為UART分配緩沖區uint8_t* data = (uint8_t*) malloc(BUF_SIZE);ESP_LOGI(TAG, "UART start receive loop.\r");echo_send(uart_num, "Start RS485 UART test.\r\n", 24);while (1) {//從UART讀取數據int len = uart_read_bytes(uart_num, data, BUF_SIZE, PACKET_READ_TICS);//將數據寫回UARTif (len > 0) {echo_send(uart_num, "\r\n", 2);char prefix[] = "RS485 Received: [";echo_send(uart_num, prefix, (sizeof(prefix) - 1));ESP_LOGI(TAG, "Received %u bytes:", len);printf("[ ");for (int i = 0; i < len; i++) {printf("0x%.2X ", (uint8_t)data[i]);echo_send(uart_num, (const char*)&data[i], 1);// 如果得到回車符,請添加一個換行符(用于測試多字節收據/緩沖區)if (data[i] == '\r') {echo_send(uart_num, "\n", 1);}}printf("] \n");echo_send(uart_num, "]\r\n", 3);} else {// Echo a "." to show we are alive while we wait for inputecho_send(uart_num, ".", 1);ESP_ERROR_CHECK(uart_wait_tx_done(uart_num, 10));}}vTaskDelete(NULL);
}void app_main(void)
{//一個沒有事件隊列的UART讀寫示例。xTaskCreate(echo_task, "uart_echo_task", ECHO_TASK_STACK_SIZE, NULL, ECHO_TASK_PRIO, NULL);
}

示例4 uart_events

說明 演示了如何使用 UART 驅動程序處理特殊的 UART 事件,從 UART0 讀取數據,并將數據回顯到監視控制臺。

代碼:


#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/uart.h"
#include "esp_log.h"static const char *TAG = "uart_events";/**
端口:UART0接收(Rx)緩沖區:啟用發送(Tx)緩沖區:禁用流控:禁用事件隊列:啟用引腳分配:TxD(默認),RxD(默認)*/#define EX_UART_NUM UART_NUM_0
#define PATTERN_CHR_NUM    (3)         /*!<設置接收器接收到的連續且相同的字符數量。defines a UART pattern*/#define BUF_SIZE (1024)
#define RD_BUF_SIZE (BUF_SIZE)static QueueHandle_t uart0_queue;static void uart_event_task(void *pvParameters)
{uart_event_t event;size_t buffered_size;uint8_t* dtmp = (uint8_t*) malloc(RD_BUF_SIZE);for (;;) {// 等待UART事件。if (xQueueReceive(uart0_queue, (void *)&event, (TickType_t)portMAX_DELAY)) {bzero(dtmp, RD_BUF_SIZE);ESP_LOGI(TAG, "uart[%d] event:", EX_UART_NUM);switch (event.type) {/*我們最好盡快處理數據事件,因為數據事件的數量會遠遠超過其他類型的事件。如果我們花費太多時間在數據事件上,隊列可能會滿。.*/case UART_DATA:		//UART接收數據事件ESP_LOGI(TAG, "[UART DATA]: %d", event.size);uart_read_bytes(EX_UART_NUM, dtmp, event.size, portMAX_DELAY);ESP_LOGI(TAG, "[DATA EVT]:");uart_write_bytes(EX_UART_NUM, (const char*) dtmp, event.size);break;case UART_FIFO_OVF:	// 檢測到硬件FIFO溢出事件ESP_LOGI(TAG, "hw fifo overflow");// 如果FIFO溢出,你應該考慮為你的應用程序添加流量控制。// 斷服務程序已經重置了接收FIFO,作為示例,我們直接在這里清空接收緩沖區以便讀取更多數據。uart_flush_input(EX_UART_NUM);xQueueReset(uart0_queue);break;            case UART_BUFFER_FULL:	//Event of UART ring buffer fullESP_LOGI(TAG, "ring buffer full");// 如果緩沖區已滿,你應該考慮增加緩沖區大小。例如,我們直接在這里刷新接收緩沖區以讀取更多數據。uart_flush_input(EX_UART_NUM);xQueueReset(uart0_queue);break;case UART_BREAK:	//UART接收器檢測到中斷事件ESP_LOGI(TAG, "uart rx break");break;case UART_PARITY_ERR:	// UART奇偶校驗錯誤事件ESP_LOGI(TAG, "uart parity error");break;case UART_FRAME_ERR:	//UART幀錯誤事件 ESP_LOGI(TAG, "uart frame error");break;case UART_PATTERN_DET:	//UART模式檢測uart_get_buffered_data_len(EX_UART_NUM, &buffered_size);int pos = uart_pattern_pop_pos(EX_UART_NUM);ESP_LOGI(TAG, "[UART PATTERN DETECTED] pos: %d, buffered size: %d", pos, buffered_size);if (pos == -1) {// 以前有一個UART_PATTERN_DET事件,但由于模式位置隊列已滿,無法記錄位置。// 我們應該設置更大的隊列大小。例如,我們在這里直接清空接收緩沖區。uart_flush_input(EX_UART_NUM);} else {uart_read_bytes(EX_UART_NUM, dtmp, pos, 100 / portTICK_PERIOD_MS);uint8_t pat[PATTERN_CHR_NUM + 1];memset(pat, 0, sizeof(pat));uart_read_bytes(EX_UART_NUM, pat, PATTERN_CHR_NUM, 100 / portTICK_PERIOD_MS);ESP_LOGI(TAG, "read data: %s", dtmp);ESP_LOGI(TAG, "read pat : %s", pat);}break;default:ESP_LOGI(TAG, "uart event type: %d", event.type);break;}}}free(dtmp);dtmp = NULL;vTaskDelete(NULL);
}void app_main(void)
{esp_log_level_set(TAG, ESP_LOG_INFO);/* 配置UART驅動程序的參數,包括通信引腳并安裝驅動程序。 */uart_config_t uart_config = {.baud_rate = 115200,.data_bits = UART_DATA_8_BITS,.parity = UART_PARITY_DISABLE,.stop_bits = UART_STOP_BITS_1,.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,.source_clk = UART_SCLK_DEFAULT,};// 安裝UART驅動程序,并獲取隊列。uart_driver_install(EX_UART_NUM, BUF_SIZE * 2, BUF_SIZE * 2, 20, &uart0_queue, 0);uart_param_config(EX_UART_NUM, &uart_config);//設置UART日志級別esp_log_level_set(TAG, ESP_LOG_INFO);//設置UART引腳(使用UART0默認引腳,即不作任何更改。)uart_set_pin(EX_UART_NUM, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);//設置UART模式檢測功能uart_enable_pattern_det_baud_intr(EX_UART_NUM, '+', PATTERN_CHR_NUM, 9, 0, 0);//將模式隊列長度重置為最多記錄20個模式位置。.uart_pattern_queue_reset(EX_UART_NUM, 20);//創建一個任務來處理來自中斷服務程序的UART事件xTaskCreate(uart_event_task, "uart_event_task", 3072, NULL, 12, NULL);
}

示例5 uart repl 控制臺

說明: 這個示例演示了如何在默認UART之外的另一個UART上使用REPL控制臺。它還展示了如何將這兩個UART連接在一起,無論是用于測試還是發送命令,無需任何人工交互。
該示例可以在任何至少有兩個UART的ESP板上運行。開發板應通過一根USB線連接到PC,用于燒錄和監控。如果您愿意監控控制臺UART,您可以使用與GPIO引腳兼容的3.3V USB轉串口適配器。.
無需外部連接即可運行示例。然而,如前所述,如果您愿意查看第二個UART(控制臺UART)上的情況,可以將CONSOLE_UART_TX_PIN(默認為5)和CONSOLE_UART_RX_PIN(默認為4)連接到串口轉USB適配器。
示例將默認UART設置為使用DEFAULT_UART_RX_PIN和DEFAULT_UART_TX_PIN。然后,它將在第二個UART上設置REPL控制臺。最后,它將連接兩個UART,以便默認UART能夠向控制臺UART發送命令并接收回復。

UART的示意圖:

                  UART default      UART consoleUSB monitoring <------ TX -----------> RX----+v解析命令并輸出結果|                 Optional 3.3VRX <----------- TX<---+  (----------->) Serial-to-USBAdapter

如果一切順利,串行端口的默認輸出應該是“結果:成功”。否則,應該是“結果:失敗”.

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/uart.h"
#include "soc/uart_periph.h"
#include "esp_rom_gpio.h"
#include "driver/gpio.h"
#include "hal/gpio_hal.h"
#include "sdkconfig.h"
#include "esp_console.h"
#include "linenoise/linenoise.h"
#include <string.h>#define DEFAULT_UART_CHANNEL    (0)
#define CONSOLE_UART_CHANNEL    (1 - DEFAULT_UART_CHANNEL)
#define DEFAULT_UART_RX_PIN     (3)
#define DEFAULT_UART_TX_PIN     (2)
#define CONSOLE_UART_RX_PIN     (4)
#define CONSOLE_UART_TX_PIN     (5)#define UARTS_BAUD_RATE         (115200)
#define TASK_STACK_SIZE         (2048)
#define READ_BUF_SIZE           (1024)/* 由“consoletest”命令打印的消息。它也將被默認的UART用于檢查第二個UART的回復。由于此處行結束符不標準(\,\\r\,\\r...),我們不在這個字符串中包含它們。 */
const char test_message[] = "This is an example string, if you can read this, the example is a success!";/**
此函數將默認UART發送端連接到控制臺UART接收端,將默認UART接收端連接到控制臺UART發送端。目的是通過讀取接收FIFO直接向控制臺發送命令并獲取回復。*/
static void connect_uarts(void)
{esp_rom_gpio_connect_out_signal(DEFAULT_UART_RX_PIN, UART_PERIPH_SIGNAL(1, SOC_UART_TX_PIN_IDX), false, false);esp_rom_gpio_connect_in_signal(DEFAULT_UART_RX_PIN, UART_PERIPH_SIGNAL(0, SOC_UART_RX_PIN_IDX), false);esp_rom_gpio_connect_out_signal(DEFAULT_UART_TX_PIN, UART_PERIPH_SIGNAL(0, SOC_UART_TX_PIN_IDX), false, false);esp_rom_gpio_connect_in_signal(DEFAULT_UART_TX_PIN, UART_PERIPH_SIGNAL(1, SOC_UART_RX_PIN_IDX), false);
}/**
斷開默認的UART與控制臺UART的連接,這在測試完成后使用一次,這樣我們可以在UART上打印消息而不將其發送回控制臺UART。否則,我們將陷入無限循環。*/
static void disconnect_uarts(void)
{esp_rom_gpio_connect_out_signal(CONSOLE_UART_TX_PIN, UART_PERIPH_SIGNAL(1, SOC_UART_TX_PIN_IDX), false, false);esp_rom_gpio_connect_in_signal(CONSOLE_UART_RX_PIN, UART_PERIPH_SIGNAL(1, SOC_UART_RX_PIN_IDX), false);
}/**
配置并安裝默認的UART,然后將其連接到控制臺UART。*/
static void configure_uarts(void)
{/* Configure parameters of an UART driver,* communication pins and install the driver */uart_config_t uart_config = {.baud_rate = UARTS_BAUD_RATE,.data_bits = UART_DATA_8_BITS,.parity    = UART_PARITY_DISABLE,.stop_bits = UART_STOP_BITS_1,.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,.source_clk = UART_SCLK_DEFAULT,};ESP_ERROR_CHECK(uart_driver_install(DEFAULT_UART_CHANNEL, READ_BUF_SIZE * 2, 0, 0, NULL, 0));ESP_ERROR_CHECK(uart_param_config(DEFAULT_UART_CHANNEL, &uart_config));ESP_ERROR_CHECK(uart_set_pin(DEFAULT_UART_CHANNEL, DEFAULT_UART_TX_PIN, DEFAULT_UART_RX_PIN,UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));connect_uarts();
}/**
當調用命令`consoletest`時將被調用的函數。它將簡單地打印上面定義的`test_message`。*/
static int console_test(int argc, char **argv)
{printf("%s\n", test_message);return 0;
}/**
在另一個主要任務中執行的功能(如主要任務執行REPL控制臺)。
它將發送“consoletest”命令到控制臺UART,然后通過RX讀取響應。
響應應包含test_message字符串。*/
static void send_commands(void* arg)
{static char data[READ_BUF_SIZE];char command[] = "consoletest\n";int len = 0;void* substring = NULL;/*忽略控制臺發送的第一條條消息。 */do {len = uart_read_bytes(DEFAULT_UART_CHANNEL, data, READ_BUF_SIZE, 100 / portTICK_PERIOD_MS);} while (len == 0);if (len == -1) {goto end;}/* 向控制臺UART發送命令`consoletest`. */len = uart_write_bytes(DEFAULT_UART_CHANNEL, command, sizeof(command));if (len == -1) {goto end;}/* 從控制臺獲取答案,并給它一些延遲. */do {len = uart_read_bytes(DEFAULT_UART_CHANNEL, data, READ_BUF_SIZE - 1, 250 / portTICK_PERIOD_MS);} while (len == 0);if (len == -1) {goto end;}/**檢查是否可以在接收到的消息中找到test_message。在此之前,我們需要添加一個NULL字符來終止字符串。*/data[len] = 0;substring = strcasestr(data, test_message);end:/* 這絕對不能再把任何東西發送到控制臺了! */disconnect_uarts();printf("Result: %s\n", substring == NULL ? "Failure" : "Success");vTaskDelete(NULL);
}void app_main(void)
{esp_console_repl_t *repl = NULL;esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();repl_config.prompt = "repl >";const esp_console_cmd_t cmd = {.command = "consoletest",.help = "Test console by sending a message",.func = &console_test,};esp_console_dev_uart_config_t uart_config = {.channel = CONSOLE_UART_CHANNEL,.baud_rate = UARTS_BAUD_RATE,.tx_gpio_num = CONSOLE_UART_TX_PIN,.rx_gpio_num = CONSOLE_UART_RX_PIN,};/*** 由于我們沒有實際的串行終端,(我們只是使用默認的UART發送和接收命令),* 所以我們不會處理任何轉義序列,因此最簡單的方法是將控制臺設置為“啞”模式。 */linenoiseSetDumbMode(1);ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &repl));configure_uarts();const esp_console_cmd_t cmd = {.command = "consoletest",.help = "Test console by sending a message",.func = &console_test,};ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));	// 當然,調用這個函數,可以注冊多條指令,很多很多條!再想想這能做什么?!!/* 創建一個任務,用于通過第二UART發送和接收命令. */xTaskCreate(send_commands, "send_commands_task", TASK_STACK_SIZE, NULL, 10, NULL);ESP_ERROR_CHECK(esp_console_start_repl(repl));
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/89869.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/89869.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/89869.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

在 .NET Core 和 React 中使用 WebSockets 和 SignalR 進行實時數據傳輸

對于需要即時更新和通知的應用程序來說&#xff0c;實時數據傳輸至關重要。在 .NET Core 中&#xff0c;WebSocket 和 SignalR 提供了強大的工具來實現客戶端和服務器之間的實時通信。在本指南中&#xff0c;我們將探討如何在 .NET Core 應用程序中使用 WebSocket 和 SignalR 實…

第八十六篇 大數據排序算法:從廚房整理到分布式排序的智慧

目錄一、基礎排序算法&#xff1a;生活場景中的計算智慧1.1 冒泡排序&#xff1a;圖書館的書籍整理1.2 插入排序&#xff1a;廚房調料的整理藝術二、高效排序算法&#xff1a;大數據處理的利器2.1 快速排序&#xff1a;音樂APP的智能歌單2.2 歸并排序&#xff1a;學校成績單的合…

開源 | V3.1.1慧知開源重卡運營充電樁平臺 - 重卡運營充電樁平臺管理解決方案;企業級完整代碼 多租戶、模擬器、多運營商、多小程序;

【開源免費版】推薦一套企業級開源充電樁平臺&#xff1a;完整代碼包含多租戶、硬件模擬器、多運營商、多小程序&#xff0c;汽車 電動自行車、云快充協議&#xff1b;——(慧哥)慧知開源充電樁平臺&#xff1b;https://liwenhui.blog.csdn.net/article/details/148242725?spm…

ONLYOFFICE 協作空間 企業版使用秘籍-8.使用虛擬數據房間,處理機密文檔更安全

在當今快節奏的社會中&#xff0c;信息已成為極其關鍵的資源&#xff0c;因此&#xff0c;保護敏感數據至關重要。ONLYOFFICE 協作空間中的虛擬數據房間&#xff08;VDR&#xff09;提供了一個安全便捷的工作空間&#xff0c;確保文檔受到嚴密保護的同時&#xff0c;也能實現輕…

系統架構設計師論文分享-論軟件架構復用

我的軟考歷程 摘要 2023年2月&#xff0c;我所在的公司通過了研發紗線MES系統的立項&#xff0c;該項目為國內紗線工廠提供SAAS服務&#xff0c;旨在提升紗線工廠的數字化和智能化水平。我在該項目中擔任架構設計師&#xff0c;負責該項目的架構設計工作。本文結合我在該項目…

虛擬主機與獨立服務器如何選擇

在搭建和維護網站時&#xff0c;選擇合適的服務器套餐至關重要。虛擬主機和獨立服務器是兩種常見的選擇&#xff0c;它們各有優缺點&#xff0c;適用于不同需求的用戶。本文將深入探討這兩種服務器類型的特點&#xff0c;以幫助您為您的網站選擇最合適的服務器解決方案。虛擬主…

NFC的安全技術體系

NFC&#xff08;近場通信&#xff09;技術因廣泛應用于移動支付、身份認證、門禁控制等敏感場景&#xff0c;其安全技術體系是保障用戶數據與交易安全的核心。該體系涵蓋數據傳輸安全、存儲安全、身份認證、防攻擊機制等多個維度&#xff0c;通過硬件隔離、加密算法、協議規范等…

Echarts3D柱狀圖-圓柱體-文字在柱體上垂直顯示的實現方法

全部代碼 <!DOCTYPE html> <html lang"en" style"height: 100%"> <head><meta charset"utf-8"><title>3D柱狀圖-圓柱體-文字豎排</title> </head> <body style"height: 100%; margin: 0"…

【算法訓練營Day08】字符串part2

文章目錄 反轉字符串里的單詞右旋字符串KMP算法雙指針法總結 反轉字符串里的單詞 題目鏈接&#xff1a;151. 反轉字符串中的單詞 雙指針法解題邏輯 head指針遍歷字符串遍歷到單詞首單詞&#xff0c;生成end指針移動到單詞尾部遇到完整單詞收集&#xff0c;壓入棧中head指針移動…

如何使用backtrace定位Linux程序的崩潰位置

在嵌入式Linux開發中&#xff0c;特別是復雜軟件&#xff0c;多人協作開發時&#xff0c;當某人無意間寫了一個代碼bug導致程序崩潰&#xff0c;但又不知道崩潰的具體位置時&#xff0c;單純靠走讀代碼&#xff0c;很難快速的定位問題。 本篇就來介紹一種方法&#xff0c;使用…

十大排序算法匯總

好的&#xff0c;下面為你整理一篇面試全覆蓋、極其深入的十大排序算法總結博客&#xff0c;涵蓋算法原理、復雜度、穩定性、應用場景、工程實踐、C與Python實現&#xff08;含詳細注釋&#xff09;&#xff0c;并對比分析各種排序的優缺點與適用情境。內容力求結構清晰、講解透…

零基礎 “入坑” Java--- 七、數組(二)

文章目錄 一、數組轉字符串二、數組的拷貝三、求數組中元素的平均值四、查找數組中指定元素&#xff08;順序查找&#xff09;五、數組排序&#xff08;冒泡排序&#xff09;六、查找數組中指定元素&#xff08;二分查找&#xff09;七、判斷兩個數組中的元素是否相等八、填充數…

【C++ 真題】P1104 生日

P1104 生日 題目描述 cjf 君想調查學校 OI 組每個同學的生日&#xff0c;并按照年齡從大到小的順序排序。但 cjf 君最近作業很多&#xff0c;沒有時間&#xff0c;所以請你幫她排序。 輸入格式 輸入共有 n 1 n 1 n1 行&#xff0c; 第 1 1 1 行為 OI 組總人數 n n n&…

Oracle DB和PostgreSQL,OpenGauss主外鍵一致性的區別

針對于unique索引在主外鍵上的表現&#xff0c;o和PG的行為確實不一致&#xff0c;測試樣例&#xff1a;PG:測試1&#xff1a;test# CREATE TABLE gdb_editingtemplates ( objectid INTEGER NOT NULL, globalid VARCHAR(38) DEFAULT {00000000-0000-0000-0000-000000000000} …

06.自動化測試概念

自動化測試概念 1. 自動化1.1 回歸測試1.2 自動化分類 1.3 自動化測試金字塔2. web自動化測試3.Selenium 1. 自動化 ? **自動化測試&#xff08;Automated Testing&#xff09;&#xff1a;**是指使用軟件工具或腳本來自動執行測試任務&#xff0c;代替人工進行重復性、繁瑣的…

頁面登錄數據的加密(前端+后端)

本加密過程使用的 AESRSA概要1.使用AES對傳輸數據進行加密AES為對稱加密,加密和解決所需要的key是一樣的,所以攔截到AES key就可以直接解密,所以需要結果RSA進行加密2.對AES的key進行RSA加密RSA為非對稱加密,客戶端只能獲取到publicKey(公鑰),而解密只能使用服務器的privateKey…

PC端基于SpringBoot架構控制無人機(一):初識無人機控制

一、無人機飛控系統的概述飛控&#xff08;Flight Controller&#xff09;是無人機最為核心的組成部分之一&#xff0c;負責實現無人機的自主飛行控制和穩定飛行。飛控系統的功能決定了無人機的飛行性能&#xff0c;包括飛行的穩定性、操控的響應速度、導航的精確度等。通過飛控…

QT6 源(154)模型視圖架構里的列表視圖 QListView:先學習屬性部分,

&#xff08;1&#xff09;屬性總圖&#xff0c;以及測試程序的框架 &#xff1a; 開始屬性的學習 &#xff1a; &#xff08;2&#xff09; 繼續屬性學習 &#xff1a; &#xff08;3&#xff09; 謝謝

MySQL——9、事務管理

事務管理 1、什么是事務&#xff1f;2、事務常見操作方式3、事務隔離級別4、數據庫并發場景4.1、讀-寫4.2、RR與RC的本質區別 1、什么是事務&#xff1f; mysql是基于CS模式的&#xff0c;是一套網絡服務&#xff0c;所以我們是可以在本地連接上遠程服務器的mysql服務端的。my…

Python之面向對象詳解(一篇足矣)

目錄 一、初階面向對象 1. 初識面向對象 1.1 對象和self 1.2 常見成員 1.3 應用示例 將數據封裝到一個對象&#xff0c;便于以后使用。 將數據封裝到對象中&#xff0c;在方法中對原始數據進行加工處理。 根據類創建多個對象&#xff0c;在方法中對對象中的數據進行修改…