You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过USB将Teensy 4.1 CAN总线CSV数据转换为MDF4格式

实现方案与示例代码

一、Teensy 4.1端:SD卡CSV数据的USB传输

有两种便捷的USB传输方式,可根据需求选择:

方式1:USB Mass Storage(直接挂载SD卡为U盘)

这是最简便的方式:

  • 在Arduino IDE中,选择Tools -> USB Type -> Serial + Mass Storage
  • 编译上传代码后,电脑会自动识别Teensy的SD卡为可移动磁盘,直接复制can_data.csv文件到本地即可。

方式2:串口主动传输(适合自动化触发)

如果需要通过指令触发数据传输,可使用串口发送CSV文件内容:

#include <SD.h>
#include <USBSerial.h>

#define CS_PIN 10
#define CSV_FILE "can_data.csv"

File csvFile;

void setup() {
  Serial.begin(115200);
  while (!Serial); // 等待电脑串口连接

  // 初始化SD卡
  if (!SD.begin(CS_PIN)) {
    Serial.println("SD init failed");
    return;
  }
}

void loop() {
  // 接收电脑指令,'T'表示触发传输
  if (Serial.available()) {
    char cmd = Serial.read();
    if (cmd == 'T') {
      csvFile = SD.open(CSV_FILE);
      if (!csvFile) {
        Serial.println("Open CSV failed");
        return;
      }

      // 逐字节发送文件内容
      while (csvFile.available()) {
        Serial.write(csvFile.read());
      }
      csvFile.close();
      Serial.println("\nTRANSFER_DONE"); // 传输结束标记
    }
  }
}

二、电脑端C语言:接收数据并转换为MDF4格式

步骤1:串口接收CSV数据到本地

以Windows平台为例(Linux可替换为termios串口API):

#include <stdio.h>
#include <windows.h>
#include <string.h>

#define SERIAL_PORT "COM3" // 替换为你的Teensy串口
#define BAUD_RATE CBR_115200
#define BUF_SIZE 1024

void receive_csv(const char* save_path) {
  HANDLE hSerial = CreateFile(SERIAL_PORT, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  if (hSerial == INVALID_HANDLE_VALUE) {
    printf("Open serial port failed\n");
    return;
  }

  // 配置串口参数
  DCB dcb = {0};
  dcb.DCBlength = sizeof(dcb);
  GetCommState(hSerial, &dcb);
  dcb.BaudRate = BAUD_RATE;
  dcb.ByteSize = 8;
  dcb.StopBits = ONESTOPBIT;
  dcb.Parity = NOPARITY;
  SetCommState(hSerial, &dcb);

  // 设置超时
  COMMTIMEOUTS timeouts = {0};
  timeouts.ReadIntervalTimeout = 50;
  timeouts.ReadTotalTimeoutConstant = 50;
  SetCommTimeouts(hSerial, &timeouts);

  // 读取并保存数据
  FILE* fp = fopen(save_path, "wb");
  if (!fp) {
    printf("Create local CSV failed\n");
    CloseHandle(hSerial);
    return;
  }

  char buffer[BUF_SIZE];
  DWORD bytesRead;
  printf("Receiving data...\n");

  while (1) {
    ReadFile(hSerial, buffer, BUF_SIZE-1, &bytesRead, NULL);
    if (bytesRead == 0) continue;
    buffer[bytesRead] = '\0';
    fwrite(buffer, 1, bytesRead, fp);
    fflush(fp);

    // 检测传输结束标记
    if (strstr(buffer, "TRANSFER_DONE") != NULL) {
      printf("Receive complete\n");
      break;
    }
  }

  fclose(fp);
  CloseHandle(hSerial);
}

步骤2:CSV转MDF4格式(基于libmdf4库)

使用libmdf4(轻量级C语言MDF4操作库)实现转换,需先将libmdf4编译为静态/动态库链接到项目:

#include <mdf/mdf.h>
#include <stdio.h>
#include <stdint.h>

void csv_to_mdf4(const char* csv_path, const char* mdf_path) {
  FILE* csv_fp = fopen(csv_path, "r");
  if (!csv_fp) {
    printf("Open CSV for convert failed\n");
    return;
  }

  // 创建MDF4文件
  mdf_file* mdf = mdf_create(mdf_path, MDF_VERSION_410);
  if (!mdf) {
    printf("Create MDF4 file failed\n");
    fclose(csv_fp);
    return;
  }

  // 配置CAN总线参数
  mdf_can_bus bus = {
    .bus_type = MDF_CAN_BUS_TYPE_CAN,
    .baudrate = 500000, // 替换为你的CAN总线波特率
    .name = "Vehicle CAN Bus"
  };
  mdf_can_bus_handle bus_hdl = mdf_add_can_bus(mdf, &bus);

  // 解析CSV并写入CAN帧
  char line[256];
  fgets(line, sizeof(line), csv_fp); // 跳过表头

  while (fgets(line, sizeof(line), csv_fp)) {
    uint64_t timestamp;
    uint32_t can_id;
    uint8_t dlc;
    uint8_t data[8] = {0};

    // 解析CSV行(格式:timestamp_us,can_id_hex,dlc,data1_hex,...,data8_hex)
    sscanf(line, "%llu,%x,%hhu,%hhx,%hhx,%hhx,%hhx,%hhx,%hhx,%hhx,%hhx",
           &timestamp, &can_id, &dlc,
           &data[0], &data[1], &data[2], &data[3],
           &data[4], &data[5], &data[6], &data[7]);

    mdf_can_frame frame = {
      .id = can_id,
      .dlc = dlc,
      .timestamp = timestamp,
      .flags = (can_id & 0x80000000) ? MDF_CAN_FRAME_FLAG_EXTENDED : MDF_CAN_FRAME_FLAG_NONE
    };
    memcpy(frame.data, data, dlc);

    mdf_write_can_frame(mdf, bus_hdl, &frame);
  }

  // 收尾并关闭文件
  mdf_close(mdf);
  fclose(csv_fp);
  printf("MDF4 convert complete\n");
}

// 主函数
int main() {
  receive_csv("local_can.csv");
  csv_to_mdf4("local_can.csv", "can_data.mf4");
  return 0;
}

三、关键注意事项

  • 确保Teensy保存的CSV格式与电脑端解析逻辑一致,建议固定为时间戳(微秒),CAN_ID(十六进制),数据长度,数据字节1(十六进制),...,数据字节8(十六进制)
  • libmdf4编译时需根据平台(Windows/Linux)调整编译参数,可参考库源码中的编译说明
  • 串口波特率需与Teensy端保持一致,示例中为115200

内容的提问来源于stack exchange,提问作者bruno vicente

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 02:41:01