ESP32 使用CANBUS(TJA1051-ESP32-Arduino-CAN)

CANBUS 简介

CAN 是 Controller Area Network 的缩写(以下称为 CAN),是 ISO国际标准化的串行通信协议。
CAN总线协议已被国际标准化组织认证,技术比较成熟,控制的芯片已经商品化,性价比高,特别适用于分布式测控系统之间的数据通讯。

TWAI 简介

在ESP32中的外设有TWAI,是可兼容CANBUS控制器。
TWAI 是一种适用于汽车和工业应用的实时串行通信协议。它与ISO11898-1经典帧兼容,因此可以支持标准帧格式(11位ID)和扩展帧格式(29位ID)。
ESP32 包含 1 个 TWAI 控制器,可配置为通过外部收发器在 TWAI 总线上进行通信。
TWAI 是一种多主、多播、异步、串行通信协议。
TWAI 还支持错误检测和信令以及内置消息优先级。具体请查看以下官方文档:

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

CANBUS 和 TWAI 的兼容性

CANNUS和ESP32的TWAI在CAN通信协议上是兼容通用的,但在具体使用时有一些区别需要注意:

  • CANBUS是一种串行通信协议和总线标准,定义了CAN Identifier、数据帧、仲裁等机制。TWAI完全兼容这些协议。
  • TWAI作为CAN控制器已经实现了CAN协议的物理层和部分数据链路层。直接基于TWAI开发可以省去部分工作。
  • 但CANbus网络还需要CAN收发器、隔离保护、终端电阻等电路设计。ESP32的TWAI只是一个节点,仍需其他电路设计才能组成总线。
  • TWAI支持CAN2.0A/2.0B标准,具有CAN FD的高速数据率特性。需要注意是否所有设备都兼容CAN FD。
  • TWAI的驱动需要通过ESP-IDF来开发,不能直接使用Arduino的CAN库,需要适当修改。
  • TWAI的一些高级功能需要根据ESP32开发手册设定,如过滤器、屏蔽机制等。
  • 如果是全新设计,建议都采用支持CAN FD的设备,可以发挥TWAI的最大性能。

综上所述,在CAN协议上两者是兼容的,但在具体使用时,需要注意ESP32 TWAI的一些特性,综合电路设计来实现CAN通信。了解两者区别后,可以充分利用好TWAI的性能。

实验效果

2个ESP32通过CANBUS进行通信

元件说明

vzc8ff7das8

TJA1051 高速、低功耗、CAN 收发器 是CAN 控制器和物理总线之间的接口,为CAN 控制器提供差动发送和接收功能。

该收发器专为汽车行业的高速CAN 应用设计,传输速率高达1Mbit/s。

改进了电磁兼容性(EMC)和静电放电(ESD)性能。

收发器在断电或处于低功耗模式时,在总线上不可见。

TJA1051T/3 和TJA1051TK/3 的I/O 口可直接与3V~5V 的微控制器接口连接。

TJA1051 不支持可总线唤醒的待机模式。

引脚说明

  1. TXD - 发送数据输入
  2. GND - 地
  3. VCC - 电源电压
  4. RXD - 接收数据输出
  5. n.c. - 悬空(不要连接)
  6. CANL - 低电平CAN总线线
  7. CANH - 高电平CAN总线线
  8. S - 静默模式控制输入

BOM表

名称 数量
ESP32 x2
TJA1051 收发器模块 x2
面包板 x2
跳线(杜邦线) 若干

接线图

oi26iu45hj4hj34

j4hj35hk46jh3

安装需要用到的库

https://github.com/miwagner/ESP32-Arduino-CAN

程序代码

#include <Arduino.h>
#include <ESP32CAN.h>
#include <CAN_config.h>

CAN_device_t CAN_cfg;               // CAN Config
unsigned long previousMillis = 0;   // will store last time a CAN Message was send
const int interval = 1000;          // interval at which send CAN Messages (milliseconds)
const int rx_queue_size = 10;       // Receive Queue size

void setup() {
  Serial.begin(115200);
  Serial.println("Basic Demo - ESP32-Arduino-CAN");
  CAN_cfg.speed = CAN_SPEED_125KBPS;
  CAN_cfg.tx_pin_id = GPIO_NUM_5;
  CAN_cfg.rx_pin_id = GPIO_NUM_4;
  CAN_cfg.rx_queue = xQueueCreate(rx_queue_size, sizeof(CAN_frame_t));
  // Init CAN Module
  ESP32Can.CANInit();
}

void loop() {

  CAN_frame_t rx_frame;

  unsigned long currentMillis = millis();

  // Receive next CAN frame from queue
  if (xQueueReceive(CAN_cfg.rx_queue, &rx_frame, 3 * portTICK_PERIOD_MS) == pdTRUE) {

    if (rx_frame.FIR.B.FF == CAN_frame_std) {
      printf("New standard frame");
    }
    else {
      printf("New extended frame");
    }

    if (rx_frame.FIR.B.RTR == CAN_RTR) {
      printf(" RTR from 0x%08X, DLC %d\r\n", rx_frame.MsgID,  rx_frame.FIR.B.DLC);
    }
    else {
      printf(" from 0x%08X, DLC %d, Data ", rx_frame.MsgID,  rx_frame.FIR.B.DLC);
      for (int i = 0; i < rx_frame.FIR.B.DLC; i++) {
        printf("0x%02X ", rx_frame.data.u8[i]);
      }
      printf("\n");
    }
  }
  // Send CAN Message
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    CAN_frame_t tx_frame;
    tx_frame.FIR.B.FF = CAN_frame_std;
    tx_frame.MsgID = 0x001;
    tx_frame.FIR.B.DLC = 8;
    tx_frame.data.u8[0] = 0x00;
    tx_frame.data.u8[1] = 0x01;
    tx_frame.data.u8[2] = 0x02;
    tx_frame.data.u8[3] = 0x03;
    tx_frame.data.u8[4] = 0x04;
    tx_frame.data.u8[5] = 0x05;
    tx_frame.data.u8[6] = 0x06;
    tx_frame.data.u8[7] = 0x07;
    ESP32Can.CANWriteFrame(&tx_frame);
  }
}

2个ESP32都上传代码,上电,打开串口监视器即可看到每秒发送过来的数据。