ESP32在CAN(TWAI)波特率不同时收发数据,导致总线错误无法恢复

问题描述:

总线上有两个设备,主机:100ms周期发送数据。从机:以不同波特率发送数据,再把从机波特率调节至主机波特率一致无法通信。

环境:VSCODE + IDF-v5.0

问题分析:

我们先看下ESP32技术参考手册中TWAI中对错误状态计数器的描述。当TEC>255时,说明总线发生严重错误,此时ESP32的机制会关闭总线进入离线状态。这也是导致我们无法通信的原因。

问题解决方法:

在ESP32的技术参考手册上也描述了怎么恢复离线状态。

首先我们需要知道CAN(TWAI)的总线状态。这个我们可以通过IDF的API函数来获取中线的各种状态。

代码如下:

/**
 * @brief   Structure to store status information of TWAI driver
 */
typedef struct {
    twai_state_t state;             /**< Current state of TWAI controller (Stopped/Running/Bus-Off/Recovery) */
    uint32_t msgs_to_tx;            /**< Number of messages queued for transmission or awaiting transmission completion */
    uint32_t msgs_to_rx;            /**< Number of messages in RX queue waiting to be read */
    uint32_t tx_error_counter;      /**< Current value of Transmit Error Counter */
    uint32_t rx_error_counter;      /**< Current value of Receive Error Counter */
    uint32_t tx_failed_count;       /**< Number of messages that failed transmissions */
    uint32_t rx_missed_count;       /**< Number of messages that were lost due to a full RX queue (or errata workaround if enabled) */
    uint32_t rx_overrun_count;      /**< Number of messages that were lost due to a RX FIFO overrun */
    uint32_t arb_lost_count;        /**< Number of instances arbitration was lost */
    uint32_t bus_error_count;       /**< Number of instances a bus error has occurred */
} twai_status_info_t;

/**
 * @brief   Get current status information of the TWAI driver
 *
 * @param[out]  status_info     Status information
 *
 * @return
 *      - ESP_OK: Status information retrieved
 *      - ESP_ERR_INVALID_ARG: Arguments are invalid
 *      - ESP_ERR_INVALID_STATE: TWAI driver is not installed
 */
esp_err_t twai_get_status_info(twai_status_info_t *status_info)

同时CAN(TWAI)总线的状态有以下几种状态。

/**
 * @brief   TWAI driver states
 */
typedef enum {
    TWAI_STATE_STOPPED,             /**< Stopped state. The TWAI controller will not participate in any TWAI bus activities */
    TWAI_STATE_RUNNING,             /**< Running state. The TWAI controller can transmit and receive messages */
    TWAI_STATE_BUS_OFF,             /**< Bus-off state. The TWAI controller cannot participate in bus activities until it has recovered */
    TWAI_STATE_RECOVERING,          /**< Recovering state. The TWAI controller is undergoing bus recovery */
} twai_state_t;

当我们获取到现在的总线状态为OFF时,我们就可以知道我们的CAN(TWAI)中线已经离线,我们要开始进入恢复流程,我们直接调用IDF的API进入恢复流程,我们可以通过以下代码来实现。

/**
 * @brief   Start the bus recovery process
 *
 * This function initiates the bus recovery process when the TWAI driver is in
 * the bus-off state. Once initiated, the TWAI driver will enter the recovering
 * state and wait for 128 occurrences of the bus-free signal on the TWAI bus
 * before returning to the stopped state. This function will reset the TX queue,
 * clearing any messages pending transmission.
 *
 * @note    The BUS_RECOVERED alert can be enabled to alert the application when
 *          the bus recovery process completes.
 *
 * @return
 *      - ESP_OK: Bus recovery started
 *      - ESP_ERR_INVALID_STATE: TWAI driver is not in the bus-off state, or is not installed
 */
esp_err_t twai_initiate_recovery(void);

twai_status_info_t twai_status_info;
twai_get_status_info(&twai_status_info);

// 总线关闭
if(twai_status_info.state == TWAI_STATE_BUS_OFF)
{
    // 总线复位
    twai_initiate_recovery();
}

当我们执行完twai_initiate_recovery();函数之后CAN(TWAI)总线会清空所有错误计数,但是此时总线还不能直接收发数据,因为此时我们的总线状态为TWAI_STATE_STOPPED状态,我们此时需要切换到TWAI_STATE_RUNNING状态我们才能正常收发数据,这个状态的切换我们也是通过API函数来切换。代码如下:

twai_start();

问题总结:解决这个问题只需要在收发任务中加入以下代码就可以对总线状态进行监控。进入恢复流程。

// 读取总线状态
twai_status_info_t twai_status_info;

twai_get_status_info(&twai_status_info);
// 总线关闭
if(twai_status_info.state == TWAI_STATE_BUS_OFF)
{
    twai_initiate_recovery();
}
// 总线停止
else if(twai_status_info.state == TWAI_STATE_STOPPED)
{
    twai_start();
}

如果帮到了你,请给我一个关注😍。

### 使用 Arduino 库与 ESP32TWAI CAN 接口驱动小米电机 #### 硬件连接说明 为了使 ESP32 能够通过 TWAI/CAN 总线控制小米电机,硬件连接至关重要。通常情况下,TWAI/CAN 需要两根信号线:CAN_H 和 CAN_L。确保这两条线路正确连接到小米电机控制器上的对应端子上。 #### 初始化 TWAI 模块 在使用前需初始化 TWAI 模块设置波特率、模式和其他参数: ```cpp #include <driver/twai.h> // 定义 TWAI 参数结构体 twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(GPIO_NUM_5, GPIO_NUM_18, TWAI_MODE_NORMAL); twai_timing_config_t t_config = TWAI_TIMING_CONFIG_500KBITS(); twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); void setup() { Serial.begin(115200); // 安装并启动 TWAI 驱动程序 if (twai_driver_install(&g_config, &t_config, &f_config) == ESP_OK && twai_start() == ESP_OK) { Serial.println("TWAI Driver installed and started"); } else { Serial.println("Failed to install or start TWAI driver"); } } ``` 此部分代码配置了 TWAI 接口的工作方式以及通信速率等重要属性[^1]。 #### 发送数据给小米电机 一旦成功安装并启用了 TWAI 驱动器,则可以通过发送标准帧消息来向小米电机发出指令: ```cpp #define MOTOR_ID 0x141 // 假定的小米电机 ID 地址 uint8_t data[] = {0x00}; // 数据载荷数组 void loop() { twai_message_t message; memset(&message, 0, sizeof(twai_message_t)); message.identifier = MOTOR_ID; // 设置目标设备ID message.extd = false; // 是否扩展标识符标志位 message.data_length_code = sizeof(data); // DLC 字段表示有效负载长度 memcpy(message.data, data, sizeof(data));// 复制实际的数据 if (twai_transmit(&message, pdMS_TO_TICKS(10)) == ESP_OK){ Serial.println("Message transmitted successfully."); }else{ Serial.println("Error transmitting message."); } delay(1000); // 每秒尝试一次传输操作 } ``` 上述代码展示了如何构建一个简单的命令包并通过 TWAI 进行广播。请注意修改 `MOTOR_ID` 及其他特定于所使用的电机型号的具体细节。 #### 关闭 TWAI 模块 当不再需要继续利用 TWAI 功能应当停止其运行以释放资源: ```cpp void stopTwai(){ if(twai_stop()==ESP_OK && twai_del_bus_off_callback()==ESP_OK && twai_del_rx_data_queue_full_callback()==ESP_OK && twai_uninstall_driver()==ESP_OK){ Serial.println("TWAI stopped and uninstalled."); }else{ Serial.println("Failed to properly close the TWAI module."); } } void onExit(){stopTwai();} atexit(onExit); ``` 这段附加的函数定义用于安全地关闭和卸载 TWAI 设备,在应用程序结束之前调用它是非常重要的。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值