一个基于 GB/T 27930-2015 电动汽车充电通信标准的简化版CAN通信框架示例。该代码模拟充电桩(充电机)与车辆BMS之间的CAN通信流程,涵盖握手、参数配置、充电控制等阶段。
- CAN通信基础定义
c
include <stdint.h>
include <stdbool.h>
include <string.h>
// CAN报文ID定义(GB/T 27930-2015)
define BMS_HANDSHAKE_ID 0x1801F456 // BMS握手报文ID
define CHARGER_HANDSHAKE_ID 0x1802F456 // 充电机握手报文ID
define BMS_PARAM_ID 0x1810F456 // BMS参数报文ID
define CHARGER_PARAM_ID 0x1820F456 // 充电机参数报文ID
define BMS_REALTIME_ID 0x1811F456 // BMS实时数据报文ID
define CHARGER_REALTIME_ID 0x1821F456 // 充电机实时数据报文ID
// CAN报文结构体
typedef struct {
uint32_t id; // CAN ID
uint8_t data8; // 数据域(8字节)
uint8_t len; // 数据长度
} CAN_Frame;
// 充电状态机
typedef enum {
STATE_INIT, // 初始化
STATE_HANDSHAKE, // 握手阶段
STATE_PARAM_CONFIG, // 参数配置
STATE_CHARGING, // 充电中
STATE_STOP // 停止
} ChargingState;
- CAN接口模拟函数(需根据实际硬件实现)
c
// 模拟CAN发送函数(需适配具体CAN控制器,如SocketCAN、STM32 HAL等)
void can_send_frame(const CAN_Frame* frame) {
// 实际代码中需调用硬件驱动发送CAN报文
printf(“CAN发送: ID=0x%08X, 数据=”, frame->id);
for (int i = 0; i < frame->len; i++) {
printf("%02X “, frame->datai);
}
printf(”\n");
}
// 模拟CAN接收函数(非阻塞式)
bool can_receive_frame(CAN_Frame* frame) {
// 实际代码中需从CAN缓冲区读取报文
// 此处模拟接收到BMS握手报文
static bool simulated_recv = false;
if (!simulated_recv) {
frame->id = BMS_HANDSHAKE_ID;
frame->len = 8;
memset(frame->data, 0, 8);
frame->data0 = 0xAA; // 模拟BMS握手数据
simulated_recv = true;
return true;
}
return false;
}
- 充电流程处理函数
3.1 发送充电机握手报文
c
void send_charger_handshake() {
CAN_Frame frame;
frame.id = CHARGER_HANDSHAKE_ID;
frame.len = 8;
// 数据填充示例(具体字段需参考GB/T 27930)
frame.data0 = 0x01; // 协议版本
frame.data1 = 0x02; // 充电机类型
// … 其他字段按标准填充
can_send_frame(&frame);
}
3.2 解析BMS握手报文
c
bool parse_bms_handshake(const CAN_Frame* frame) {
if (frame->id != BMS_HANDSHAKE_ID || frame->len < 8) return false;
// 解析BMS协议版本、电池类型等
uint8_t protocol_ver = frame->data0;
uint8_t battery_type = frame->data1;
// … 校验有效性
return true;
}
3.3 发送充电机参数报文
c
void send_charger_parameters(uint16_t max_voltage, uint16_t max_current) {
CAN_Frame frame;
frame.id = CHARGER_PARAM_ID;
frame.len = 8;
// 电压电流按大端格式填充
frame.data0 = (max_voltage >> 8) & 0xFF;
frame.data1 = max_voltage & 0xFF;
frame.data2 = (max_current >> 8) & 0xFF;
frame.data3 = max_current & 0xFF;
// … 其他参数
can_send_frame(&frame);
}
-
主状态机逻辑
c
int main() {
ChargingState state = STATE_INIT;
CAN_Frame rx_frame;while (1) {
switch (state) {
case STATE_INIT:
printf(“进入初始化状态\n”);
state = STATE_HANDSHAKE;
break;case STATE_HANDSHAKE:// 发送充电机握手报文send_charger_handshake();// 等待BMS握手响应if (can_receive_frame(&rx_frame)) {if (parse_bms_handshake(&rx_frame)) {printf("握手成功!\n");state = STATE_PARAM_CONFIG;}}break;case STATE_PARAM_CONFIG:// 发送充电机最大输出电压、电流send_charger_parameters(7500, 200); // 750V, 200A// 等待BMS参数报文(此处省略解析代码)state = STATE_CHARGING;break;case STATE_CHARGING: {// 模拟充电循环static uint32_t charge_time = 0;printf("充电中... 时间: %d秒\n", charge_time++);// 发送实时数据(电压、电流)CAN_Frame realtime_frame;realtime_frame.id = CHARGER_REALTIME_ID;realtime_frame.len = 8;// 模拟数据:电压=7500mV,电流=20000mArealtime_frame.data0 = 0x75; // 7500mV高字节realtime_frame.data1 = 0x00; // 7500mV低字节realtime_frame.data2 = 0x4E; // 20000mA高字节realtime_frame.data3 = 0x20; // 20000mA低字节can_send_frame(&realtime_frame);// 持续10秒后停止if (charge_time > 10) state = STATE_STOP;break;}case STATE_STOP:printf("充电结束!\n");return 0; }
}
}
-
关键流程说明
-
握手阶段:
- 充电机发送
CHARGER_HANDSHAKE_ID
报文。 - BMS回复
BMS_HANDSHAKE_ID
报文,双方确认协议版本和参数。
- 充电机发送
-
参数配置:
- 充电机发送最大输出电压/电流(
CHARGER_PARAM_ID
)。 - BMS发送电池参数(
BMS_PARAM_ID
),代码中需补充解析逻辑。
- 充电机发送最大输出电压/电流(
-
充电阶段:
- 双方周期性发送实时数据(
CHARGER_REALTIME_ID
和BMS_REALTIME_ID
)。 - 监测电压、电流是否超限,实现过压/过流保护。
- 双方周期性发送实时数据(
-
停止条件:
- 达到预设充电时间或BMS发送停止指令。
-
扩展建议
-
超时重试机制:
c
define HANDSHAKE_TIMEOUT_MS 3000
uint32_t handshake_start_time = get_current_time();
while (state == STATE_HANDSHAKE) {
if (get_current_time() - handshake_start_time > HANDSHAKE_TIMEOUT_MS) {
printf(“握手超时!\n”);
state = STATE_STOP;
}
// … 接收处理
} -
校验和验证:
c
bool validate_checksum(const CAN_Frame* frame) {
uint8_t checksum = 0;
for (int i = 0; i < frame->len - 1; i++) {
checksum += frame->datai;
}
return (checksum == frame->dataframe->len - 1);
} -
多线程处理:
- 使用独立线程处理CAN接收和状态机逻辑(需平台支持)。
- 测试输出示例
进入初始化状态
CAN发送: ID=0x1802F456, 数据=01 02 00 00 00 00 00 00
CAN接收: ID=0x1801F456, 数据=AA 00 00 00 00 00 00 00
握手成功!
CAN发送: ID=0x1820F456, 数据=75 00 4E 20 00 00 00 00
充电中… 时间: 0秒
CAN发送: ID=0x1821F456, 数据=75 00 4E 20 00 00 00 00
充电中… 时间: 1秒
…
充电结束!
该代码为简化实现,实际开发需根据GB/T 27930标准完善以下内容:
- 完整报文字段解析(如电池类型、充电模式等)。
- 错误状态处理(NRC码)。
- 硬件CAN驱动适配(如STM32的HAL库、Linux SocketCAN)。
- 实时性优化(中断驱动、DMA传输)。