如何通过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", ×tamp, &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
相关产品推荐
相关产品推荐

