关于ESP32-S2 实现 TWAI CAN 通讯

关于ESP32-S2 实现 TWAI CAN 通讯

功能介绍

ESP32-S2 具有1个 CAN控制器支持:

  • 兼容ISO 11898-1 协议(CAN2.0)
  • 工作模式: 正常模式、只听模式(不影响总线)、无响应模式(传输期间无需ACK,可方便自检)
  • 支持接收单/双过滤
  • 支持错误处理

函数说明

配置和安装驱动

#include "driver/twai.h"

//安装CAN驱动
esp_err_t twai_driver_install(
		const twai_general_config_t *g_config, //基本配置
		const twai_timing_config_t *t_config,  //时序配置
		const twai_filter_config_t *f_config)  //过滤器配置
//启动CAN驱动
esp_err_t twai_start(void)
//停用CAN驱动
esp_err_t twai_stop(void)
//卸载CAN驱动
esp_err_t twai_driver_uninstall(void)

结构体说明

//CAN接口基本配置
twai_general_config_t g_config = {
		.mode = TWAI_MODE_NORMAL , //TWAI_MODE_NORMAL / TWAI_MODE_NO_ACK / TWAI_MODE_LISTEN_ONLY
		.tx_io = 2, //IO号
		.rx_io = 3, //IO号
    .clkout_io = TWAI_IO_UNUSED, //io号,不用为-1
    .bus_off_io = TWAI_IO_UNUSED,//io号,不用为-1
    .tx_queue_len = 5, //发送队列长度,0-禁用发送队列
    .rx_queue_len = 5,//接收队列长度
    .alerts_enabled = TWAI_ALERT_NONE,  //警告标志 TWAI_ALERT_ALL 可开启所有警告
    .clkout_divider = 0,//1 to 14 , 0-不用
    .intr_flags = ESP_INTR_FLAG_LEVEL1}//中断优先级
        
//CAN接口时序配置官方提供了1K to 1Mbps的常用配置
twai_timing_config_t t_config = TWAI_TIMING_CONFIG_1MBITS(); //TWAI_TIMING_CONFIG_500KBITS()

//过滤器配置
twai_filter_config_t f_config = {
		.acceptance_code = 0, //验证代码
		.acceptance_mask = 0xFFFFFFFF, //验证掩码 0xFFFFFFFF表示全部接收
		.single_filter = true}//true:单过滤器模式 false:双过滤器模式

获取TWAI状态信息

/获取twai状态
esp_err_t twai_get_status_info(twai_status_info_t *status_info)
//返回值:ESP_OK: 成功;ESP_ERR_INVALID_ARG: 参数无效;ESP_ERR_INVALID_STATE:驱动未安装

//状态信息
twai_status_info_t status_info={
		.state=,//TWAI_STATE_STOPPED / TWAI_STATE_RUNNING / TWAI_STATE_BUS_OFF / TWAI_STATE_RECOVERING
		.msgs_to_tx=,//发送队列消息数       
		.msgs_to_rx=,//接收队列消息数    
		.tx_error_counter=, //发送错误计数
		.rx_error_counter=, //接收错误计数
		.tx_failed_count=,  //发送失败计数
		.rx_missed_count=,  //因接收队列满丢失的消息数
		.rx_overrun_count=, //因接收buf(64Byte)满而丢失的消息数
		.arb_lost_count=,   //仲裁失败计数
		.bus_error_count=};//总线错误计数

接收/发送消息

//发送消息到发送队列排队,如发送队列为空则立即发送,为满则等待ticks_to_wait时间
esp_err_t twai_transmit(
		const twai_message_t *message, //消息包
		TickType_t ticks_to_wait)//超时时间
/***********************************************
* 	返回值:
*	ESP_OK:发送成功
*	ESP_ERR_INVALID_ARG:参数无效
*	ESP_ERR_TIMEOUT:等待TX队列时超时
*	ESP_FAIL:TX队列已禁用,且当前正在传输另一条消息
*	ESP_ERR_INVALID_STATE:CAN驱动程序未运行或未安装
*	ESP_ERR_NOT_SUPPORTED:只听模式不支持发送
*************************************************/

//从接收队列接收1个消息,若队列为空,则阻塞
esp_err_t twai_receive(
		twai_message_t *message, //消息包
		TickType_t ticks_to_wait)//超时
/***********************************************
* 	返回值:
*	ESP_OK:接收成功
*	ESP_ERR_INVALID_ARG:参数无效
*	ESP_ERR_TIMEOUT:等待队列时超时
*	ESP_FAIL:TX队列已禁用,且当前正在传输另一条消息
*	ESP_ERR_INVALID_STATE:CAN驱动程序未运行或未安装
*	ESP_ERR_NOT_SUPPORTED:只听模式不支持发送
*************************************************/


//发送/接收消息帧结构
twai_message_t send_message1 = {
		.extd = 1,//0-标准帧; 1-扩展帧
		.rtr = 0,//0-数据帧; 1-远程帧
		.ss = 1, //0-错误重发; 1-单次发送(仲裁或丢失时消息不会被重发),对接收消息无效
		.self = 0,//0-不接收自己发送的消息,1-接收自己发送的消息,对接收消息无效
		.dlc_non_comp = 0,// 0-数据长度不大于8(ISO 11898-1); 1-数据长度大于8(非标);
		.identifier = ID0, //11/29位ID
		.data_length_code = 0, //DLC数据长度4bit位宽
		.data = {0, 0 , 0 , 0 ,0 ,0 ,0 ,0}};//发送数据,对远程帧无效

CAN收发带过滤测试

测试采用两个ESP32-S2、和两个TJA1050 高速CAN收发器,启动一个ESP32-S2为Master 作为发送端、另外一个作为Slave 端 ,连线如下:

ESP32-S2 Master CAN收发器 双绞线 其他CAN收发器 ESP32-S2 Slave
GND GND GND GND
5V 5V 5V 5V
GPIO4 CAN RX CAN RX GPIO4
GPIO5 CAN TX CAN TX GPIO5
CAN H CAN High CAN H
CAN L CAN Low CAN L

master分别发送ID号为0x55b和0x55e的消息包,slave设置过滤器,只接收ID为0x55e的扩展帧,并将数据做处理后以0xcc的ID发送到主机。需要注意的是,正常收发数据需配置: twai_general_config_t->.mode = TWAI_MODE_NORMAL;twai_message_t->self = 0; 另外使用过滤器注意移位。

主机代码

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

#define EXAMPLE_TAG "TWAI master"
#define SENDMSG 0
#define RECEIVEMSG 1
#define RX_GPIO_PIN     GPIO_NUM_4
#define TX_GPIO_PIN     GPIO_NUM_5

/* --------------------------- Tasks and Functions -------------------------- */

void printf_msg(int flag, twai_message_t *msg) // flag:0-send 1-receive
{
    int j;
    if (flag)
        printf("Receive: ");
    else
        printf("Send   : ");
    if (msg->extd)
        printf("Extended ");
    else
        printf("Standard ");
    if (msg->rtr)
        printf("Remote Frame, ");
    else
        printf("Data  Frame,  ");
    printf("ID: 0x%x    ", msg->identifier);
    if (msg->rtr == 0)
    {
        for (j = 0; j < msg->data_length_code; j++)
        {
            printf("D%d: %d\t", j, msg->data[j]);
        }
        printf("\n");
    }
    else
        printf("\n");
}

static void twai_transmit_task(void *arg)
{

      int i;
      /*
      twai_message_t s1 = {
          .extd = 0,                         // 0-标准帧; 1-扩展帧
          .rtr = 0,                          // 0-数据帧; 1-远程帧
          .ss = 1,                           // 0-错误重发; 1-单次发送(仲裁或丢失时消息不会被重发),对接收消息无效
          .self = 0,                         // 0-不接收自己发送的消息,1-接收自己发送的消息,对接收消息无效
          .dlc_non_comp = 0,                 // 0-数据长度不大于8(ISO 11898-1); 1-数据长度大于8(非标);
          .identifier = 0x55b,                  // 11/29位ID
          .data_length_code = 4,             // DLC数据长度4bit位宽
          .data = {0, 0, 0, 0, 0, 0, 0, 0}}; //发送数据,对远程帧无效
      */
  
  
      twai_message_t s1;
      s1.identifier = 0x55b;
      s1.extd = 1;
      s1.self = 1;
      s1.data_length_code = 4;
      for (int i = 0; i < 8; i++) {
          s1.data[i] = 0;
      }
  
      vTaskDelay(pdMS_TO_TICKS(1000));
      for (i = 0; i < 3; i++) //发送3个标准数据帧
      {
          s1.data[0] = i;
          ESP_ERROR_CHECK(twai_transmit(&s1, portMAX_DELAY));
          printf_msg(SENDMSG, &s1);
      }
  
      s1.rtr = 1;
      s1.data_length_code = 6;
      vTaskDelay(pdMS_TO_TICKS(1000));
      for (i = 0; i < 1; i++) //发送1个标准远程帧
      {
          ESP_ERROR_CHECK(twai_transmit(&s1, portMAX_DELAY));
          printf_msg(SENDMSG, &s1);
      }
  
      s1.extd = 1;
      s1.rtr = 0;
      s1.identifier = 0x55e;
      s1.data_length_code = 5;
      vTaskDelay(pdMS_TO_TICKS(1000));
      for (i = 0; i < 3; i++) //发送3个扩展数据帧
      {
          s1.data[0] = i;
          ESP_ERROR_CHECK(twai_transmit(&s1, portMAX_DELAY));
          printf_msg(SENDMSG, &s1);
      }
  
      s1.rtr = 1;
      s1.data_length_code = 3;
      vTaskDelay(pdMS_TO_TICKS(1000));
      for (i = 0; i < 1; i++) //发送1个扩展远程帧
      {
          ESP_ERROR_CHECK(twai_transmit(&s1, portMAX_DELAY));
          printf_msg(SENDMSG, &s1);
      }

      vTaskDelete(NULL);
}

static void twai_receive_task(void *arg)
{
    twai_message_t r1;
    for (int i = 0; i < 8; i++) //发送1个扩展远程帧
    {
        ESP_ERROR_CHECK(twai_receive(&r1, portMAX_DELAY));
        printf_msg(RECEIVEMSG, &r1);
    }
    vTaskDelete(NULL);
}

void app_main(void)
{
    /*
    // CAN接口基本配置
    twai_general_config_t g_config = {
        .mode = TWAI_MODE_NORMAL,            // TWAI_MODE_NORMAL / TWAI_MODE_NO_ACK / TWAI_MODE_LISTEN_ONLY
        .tx_io = 2,                          // IO号
        .rx_io = 3,                          // IO号
        .clkout_io = TWAI_IO_UNUSED,         // io号,不用为-1
        .bus_off_io = TWAI_IO_UNUSED,        // io号,不用为-1
        .tx_queue_len = 5,                   //发送队列长度,0-禁用发送队列
        .rx_queue_len = 5,                   //接收队列长度
        .alerts_enabled = TWAI_ALERT_NONE,   //警告标志 TWAI_ALERT_ALL 可开启所有警告
        .clkout_divider = 0,                 // 1 to 14 , 0-不用
        .intr_flags = ESP_INTR_FLAG_LEVEL1}; //中断优先级
    */

    // CAN接口基本配置
    twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(TX_GPIO_PIN, RX_GPIO_PIN, TWAI_MODE_NO_ACK);
    // CAN接口时序配置官方提供了1K to 1Mbps的常用配置
    twai_timing_config_t t_config = TWAI_TIMING_CONFIG_1MBITS(); // TWAI_TIMING_CONFIG_500KBITS()

    //过滤器配置
    twai_filter_config_t f_config = {
        .acceptance_code = 0,          //验证代码
        .acceptance_mask = 0xFFFFFFFF, //验证掩码 0xFFFFFFFF表示全部接收
        .single_filter = true};        // true:单过滤器模式 false:双过滤器模式

    ESP_ERROR_CHECK(twai_driver_install(&g_config, &t_config, &f_config));
    ESP_LOGI(EXAMPLE_TAG, "Driver installed");
    ESP_ERROR_CHECK(twai_start());
    ESP_LOGI(EXAMPLE_TAG, "Driver started");

    xTaskCreatePinnedToCore(twai_receive_task, "TWAI_rx", 4096, NULL, 8, NULL, tskNO_AFFINITY);
    xTaskCreatePinnedToCore(twai_transmit_task, "TWAI_tx", 4096, NULL, 9, NULL, tskNO_AFFINITY);

    vTaskDelay(pdMS_TO_TICKS(10000)); //运行10s

    twai_status_info_t status_info;
    twai_get_status_info(&status_info);
    while (status_info.msgs_to_tx != 0)
    {
        ESP_ERROR_CHECK(twai_get_status_info(&status_info));
    }
    ESP_ERROR_CHECK(twai_stop()); // Stop the TWAI Driver
    ESP_LOGI(EXAMPLE_TAG, "Driver stopped");
    ESP_ERROR_CHECK(twai_driver_uninstall());
    ESP_LOGI(EXAMPLE_TAG, "Driver uninstalled");
}

从机代码

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

#define EXAMPLE_TAG "TWAI slave"
#define SENDMSG 0
#define RECEIVEMSG 1
#define RX_GPIO_PIN     GPIO_NUM_4
#define TX_GPIO_PIN     GPIO_NUM_5

/* --------------------------- Tasks and Functions -------------------------- */

void printf_msg(int flag, twai_message_t *msg) // flag:0-send 1-receive
{
    int j;
    if (flag)
        printf("Receive: ");
    else
        printf("Send   : ");
    if (msg->extd)
        printf("Extended ");
    else
        printf("Standard ");
    if (msg->rtr)
        printf("Remote Frame, ");
    else
        printf("Data  Frame,  ");
    printf("ID: 0x%x    ", msg->identifier);
    if (msg->rtr == 0)
    {
        for (j = 0; j < msg->data_length_code; j++)
        {
            printf("D%d: %d\t", j, msg->data[j]);
        }
        printf("\n");
    }
    else
        printf("\n");
}

static void twai_receive_task(void *arg)
{
    int j;
    // twai_message_t s1 = {
    //     .extd = 0,                         // 0-标准帧; 1-扩展帧
    //     .rtr = 0,                          // 0-数据帧; 1-远程帧
    //     .ss = 1,                           // 0-错误重发; 1-单次发送(仲裁或丢失时消息不会被重发),对接收消息无效
    //     .self = 0,                         // 0-不接收自己发送的消息,1-接收自己发送的消息,对接收消息无效
    //     .dlc_non_comp = 0,                 // 0-数据长度不大于8(ISO 11898-1); 1-数据长度大于8(非标);
    //     .identifier = 0xcc,                 // 11/29位ID
    //     .data_length_code = 4,             // DLC数据长度4bit位宽
    //     .data = {0, 0, 0, 0, 0, 0, 0, 0}}; //发送数据,对远程帧无效



    twai_message_t s1;
    s1.identifier = 0xcc;
    s1.extd = 1;
    s1.self = 1;
    s1.data_length_code = 4;
    for (int i = 0; i < 8; i++) {
        s1.data[i] = 0;
    }

    twai_message_t r1;
    for (int i = 0; i < 8; i++)
    {
        ESP_ERROR_CHECK(twai_receive(&r1, portMAX_DELAY));
        printf_msg(RECEIVEMSG, &r1);
        s1.extd = r1.extd;
        s1.rtr = r1.rtr;
        s1.data_length_code = r1.data_length_code;
        if (r1.rtr == 0)
        {
            for (j = 0; j < r1.data_length_code; j++)
                s1.data[j] = 255 - r1.data[j]; // 255减去原始数据回传
        }
        ESP_ERROR_CHECK(twai_transmit(&s1, portMAX_DELAY));
    }
    vTaskDelete(NULL);
}

void app_main(void)
{
    /*
    // CAN接口基本配置
    twai_general_config_t g_config = {
        .mode = TWAI_MODE_NORMAL,            // TWAI_MODE_NORMAL / TWAI_MODE_NO_ACK / TWAI_MODE_LISTEN_ONLY
        .tx_io = 21,                          // IO号
        .rx_io = 22,                          // IO号
        .clkout_io = TWAI_IO_UNUSED,         // io号,不用为-1
        .bus_off_io = TWAI_IO_UNUSED,        // io号,不用为-1
        .tx_queue_len = 5,                   //发送队列长度,0-禁用发送队列
        .rx_queue_len = 5,                   //接收队列长度
        .alerts_enabled = TWAI_ALERT_NONE,   //警告标志 TWAI_ALERT_ALL 可开启所有警告
        .clkout_divider = 0,                 // 1 to 14 , 0-不用
        .intr_flags = ESP_INTR_FLAG_LEVEL1}; //中断优先级

    */

    // CAN接口基本配置
    twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(TX_GPIO_PIN, RX_GPIO_PIN, TWAI_MODE_NO_ACK);


    // CAN接口时序配置官方提供了1K to 1Mbps的常用配置
    twai_timing_config_t t_config = TWAI_TIMING_CONFIG_1MBITS(); // TWAI_TIMING_CONFIG_500KBITS()

    //过滤器配置
    twai_filter_config_t f_config = {
        .acceptance_code = 0x55e << 3, //验证代码
        .acceptance_mask = 0x00000007, //验证掩码 0xFFFFFFFF表示全部接收 7:不关注低3bit
        .single_filter = true};        // true:单过滤器模式 false:双过滤器模式

    ESP_ERROR_CHECK(twai_driver_install(&g_config, &t_config, &f_config));
    ESP_LOGI(EXAMPLE_TAG, "Driver installed");
    ESP_ERROR_CHECK(twai_start());
    ESP_LOGI(EXAMPLE_TAG, "Driver started");

    xTaskCreatePinnedToCore(twai_receive_task, "TWAI_rx", 4096, NULL, 8, NULL, tskNO_AFFINITY);
}

遇到的问题

在使用Arduino 进行编译的时候,会遇到消息发送正常,而无法接收的问题。

尝试,使用 espressif 官方给的例子,也依然无法收取消息

https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/twai.html

Failed to receive message
Message queued for transmission
Failed to receive message
Message queued for transmission
Failed to receive message
Message queued for transmission
Failed to receive message
Message queued for transmission
Failed to receive message
Message queued for transmission
Failed to receive message
Failed to queue message for transmission
Failed to receive message
Failed to queue message for transmission
Failed to receive message
Failed to queue message for transmission
Failed to receive message
Failed to queue message for transmission
Failed to receive message
Failed to queue message for transmission
Failed to receive message
Failed to queue message for transmission
Failed to receive message
Failed to queue message for transmission
Failed to receive message
Failed to queue message for transmission
Failed to receive message
Failed to queue message for transmission
Failed to receive message
Failed to queue message for transmission
Failed to receive message
Failed to queue message for transmission
Failed to receive message
Failed to queue message for transmission
Failed to receive message
Failed to queue message for transmission
Failed to receive message
Failed to queue message for transmission

参考 stack overflow中得知https://stackoverflow.com/questions/68527367/twai-can-rx-messages-receiving-is-not-working-is-not-working-in-the-esp32-s2-cod,该问题是ESP32-S2设备的一个已知问题,其中TWAI接收无法正常工作,而传输将正常工作,影响ESP-IDF TWAI驱动程序的4.3版。根据查阅https://github.com/espressif/esp-idf/issues/5604 合并日志,发现**arduino-esp32**。 V2.0.9 版本已经修复了这个问题,于是在Arduino------> 工具------->管理库------->更新 安装Arduino-esp32 V2.0.9 版本,并且在编译时需要选择 开发板:ESP32 Arduino----->ESP32S2 Dev Module

你可能感兴趣的:(ESP32-S2,Arduino,CAN,通讯,TWAI)