精通 ESP32 TWAI | 使用 FreeRTOS 任務進行 CAN Bus 數據發送與接收


在之前的文章中,我們詳細探討了 ESP32 CAN Bus 的基礎概念與應用方法。然而,隨著開發需求的提高,單純使用阻塞或輪詢方式已經無法滿足高效通信的要求。因此,本篇文章將帶您深入了解如何利用 ESP32 TWAI 驅動,結合 FreeRTOS 任務,實現多任務環境下的高效 CAN Bus 數據發送與接收。

ESP32 TWAI

簡介

TWAI,也就是兩線制汽車接口,實際上就是大家熟知的 CAN Bus(Controller Area Network)。它是一種多主機、多點通信協議,廣泛應用於汽車電子系統中,用於各個電子控制單元(ECU)之間的通信。CAN Bus 的優勢在於其高可靠性和實時性,能夠在嚴苛的環境下穩定工作。

ESP32 TWAI 與 FreeRTOS Task

ESP32 提供了內建的 TWAI 控制器,這使得它在處理 CAN Bus 通信時顯得尤為得心應手。結合 FreeRTOS 多任務處理能夠更高效地進行數據發送和接收,並且更好地管理系統資源。

TWAI 與 FreeRTOS 任務結合的主要優勢:

  • 硬件級支持: ESP32 的 TWAI 是硬件原生支持的 CAN Bus 驅動,相比於基於外部模組的 CAN 實現,它能夠提供更穩定和高效的數據傳輸,並內建錯誤處理功能。
  • FreeRTOS 任務的多任務管理: 使用 FreeRTOS 任務,可以將數據的發送與接收操作分別放置於不同的任務中,實現並行處理,避免阻塞式操作對系統其他功能的影響。
  • 實時性: 通過 FreeRTOS,可以設置不同任務的優先級,確保關鍵數據的發送與接收具有更高的實時性。
  • 可擴展性: 任務模式易於擴展,可以輕鬆添加其他功能,例如數據記錄、錯誤分析或低功耗模式的實現。

開發環境

在開始編程之前,請確保已完成以下準備工作:

創建 FreeRTOS 發送任務

發送任務負責定期向 CAN Bus 發送數據。

void send_task(void *arg) {
    while (1) {
        twai_message_t message = {
            .identifier = 0x123,  // Set message ID
            .data_length_code = 8,  // Set data length
            .data = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}  // Data to send
        };

        esp_err_t err = twai_transmit(&message, pdMS_TO_TICKS(1000));
        if (err == ESP_OK) {
            ESP_LOGI(TAG, "Message sent successfully");
        } else {
            ESP_LOGE(TAG, "Message transmission failed: %s", esp_err_to_name(err));
        }

        vTaskDelay(pdMS_TO_TICKS(1000));  // Send message every second
    }
}

創建 FreeRTOS 接收任務

接收任務負責從 CAN Bus 中獲取並處理數據。

void receive_task(void *arg) {
    while (1) {
        twai_message_t message;
        esp_err_t err = twai_receive(&message, pdMS_TO_TICKS(1000));
        if (err == ESP_OK) {
            ESP_LOGI(TAG, "Message received: ID=0x%X, Data=[%02X %02X %02X %02X %02X %02X %02X %02X]",
                     message.identifier,
                     message.data[0], message.data[1], message.data[2], message.data[3],
                     message.data[4], message.data[5], message.data[6], message.data[7]);
        } else if (err == ESP_ERR_TIMEOUT) {
            ESP_LOGW(TAG, "Reception timed out");
        } else {
            ESP_LOGE(TAG, "Message reception failed: %s", esp_err_to_name(err));
        }
    }
}

初始化 ESP32 TWAI 驅動和 main

配置並安裝 ESP32 TWAI 驅動。

#include "driver/twai.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"

#define TAG "TWAI"

void app_main() {
    // Configure TWAI driver
    twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(GPIO_NUM_21, GPIO_NUM_22, TWAI_MODE_NORMAL);
    twai_timing_config_t t_config = TWAI_TIMING_CONFIG_1MBITS();  // Set to 1 Mbps
    twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();  // Accept all messages

    // Install TWAI driver
    esp_err_t err = twai_driver_install(&g_config, &t_config, &f_config);
    if (err == ESP_OK) {
        ESP_LOGI(TAG, "TWAI driver installed successfully");
    } else {
        ESP_LOGE(TAG, "Failed to install TWAI driver: %s", esp_err_to_name(err));
        return;
    }

    // Start TWAI driver
    err = twai_start();
    if (err == ESP_OK) {
        ESP_LOGI(TAG, "TWAI driver started successfully");
    } else {
        ESP_LOGE(TAG, "Failed to start TWAI driver: %s", esp_err_to_name(err));
        return;
    }

    // Create tasks
    xTaskCreate(send_task, "send_task", 2048, NULL, 5, NULL);
    xTaskCreate(receive_task, "receive_task", 2048, NULL, 5, NULL);
}

結果輸出

在運行上述代碼後,ESP32 將會:

  • 每秒發送一次數據到 CAN Bus。
  • 接收來自 CAN Bus 的數據並將其打印到串口。

輸出示例:

I (5000) TWAI: Message sent successfully
I (6010) TWAI: Message received: ID=0x123, Data=[01 02 03 04 05 06 07 08]

性能與優化

  • 實時性: 使用 FreeRTOS 任務能實現數據發送與接收的並行處理,確保實時性能。
  • 錯誤處理: TWAI 驅動內建錯誤處理機制,例如自動重傳與錯誤恢復。在開發中,您可以通過設置回調函數進一步處理錯誤。
  • 低功耗模式: ESP32 支持低功耗模式,可以根據需要在閒置時進入低功耗狀態以節省能源。

結論

掌握 ESP32 TWAI 與 FreeRTOS 的結合提供了強大的工具來實現 CAN Bus 通信。無論是數據發送還是接收,都能夠實現高效、穩定的操作。希望本文提供的示例代碼和技術要點能夠幫助您更好地利用 ESP32 進行嵌入式開發。