CAN 扩展帧
前言
Controller Area Network (CAN) 总线作为现代嵌入式系统和汽车电子中最重要的通信协议之一,其帧格式的选择直接影响系统的通信能力和扩展性。CAN 扩展帧(Extended Frame Format)作为 CAN 协议的重要组成部分,相比标准帧提供了更多的标识符空间,能够满足复杂系统对更多节点和消息类型的需求。
本文将系统地介绍 CAN 扩展帧的技术原理、帧结构、使用方法、配置要点以及实战应用,帮助工程师深入理解 CAN 扩展帧的特点和优势,掌握其在实际项目中的应用技能,为构建更加灵活和可扩展的 CAN 总线系统提供理论基础和实践指导。
目录
1. CAN 帧格式概述
1.1 CAN 帧格式的发展历程
CAN 协议发展:
CAN 协议自 1983 年由 Robert Bosch GmbH 开发以来,经历了多个版本的演进:
-
CAN 1.0:1986 年发布,仅支持标准帧格式
-
CAN 2.0A:1991 年发布,标准帧格式,11 位标识符
-
CAN 2.0B:1993 年发布,增加扩展帧格式,29 位标识符
-
CAN FD:2011 年提出,2015 年标准化,支持更高数据速率和更大数据长度
扩展帧的诞生背景:
随着 CAN 总线应用的不断扩展,特别是在汽车电子和工业控制领域,标准帧的 11 位标识符空间(最多 2048 个不同 ID)逐渐无法满足复杂系统的需求。扩展帧通过提供 29 位标识符,将 ID 空间扩展到 536,870,912 个,极大地增强了系统的扩展性。
1.2 CAN 帧格式的分类
根据标识符长度分类:
标准帧(Standard Frame):
-
标识符长度:11 位
-
最大 ID 数量:2^11 = 2048 个
-
适用场景:简单系统,节点数量较少
扩展帧(Extended Frame):
-
标识符长度:29 位
-
最大 ID 数量:2^29 = 536,870,912 个
-
适用场景:复杂系统,节点数量较多
根据数据长度分类:
传统 CAN 帧:
-
数据长度:0-8 字节
-
适用标准:CAN 2.0A/B
CAN FD 帧:
-
数据长度:0-64 字节
-
适用标准:CAN FD
1.3 标准帧与扩展帧的兼容性
向下兼容性:
CAN 协议设计具有良好的向下兼容性:
-
支持扩展帧的控制器可以接收和处理标准帧
-
仅支持标准帧的控制器会将扩展帧识别为无效帧并忽略
-
扩展帧格式中包含了标准帧的所有字段
识别机制:
CAN 控制器通过 IDE(Identifier Extension)位来区分标准帧和扩展帧:
-
IDE 位为 0:标准帧
-
IDE 位为 1:扩展帧
// CAN帧类型检测
CAN_Frame_Type detect_can_frame_type(CAN_RxHeaderTypeDef *rx_header) {
if (rx_header->IDE == CAN_ID_EXT) {
return CAN_FRAME_EXTENDED;
} else {
return CAN_FRAME_STANDARD;
}
}
2. CAN 扩展帧的技术原理
2.1 标识符空间扩展
标识符结构:
CAN 扩展帧的 29 位标识符由两部分组成:
-
基本 ID(Base ID):11 位,与标准帧的 ID 格式相同
-
扩展 ID(Extended ID):18 位,额外增加的标识符位
ID 编码方式:
扩展帧的 29 位 ID 采用如下编码方式:
Extended ID (29 bits) = Base ID (11 bits) + Extended ID (18 bits)
Bit numbering (MSB first):
Bit 28 27 ... 18 17 ... 11 10 ... 0
| | | |
| | | Base ID (11 bits)
| | Extended ID (18 bits)
| |
| Extended ID (18 bits)
|
Extended ID (18 bits)
ID 分配策略:
扩展 ID 的分配可以采用多种策略:
-
功能地址:根据消息功能分配 ID
-
节点地址:根据发送节点分配 ID
-
混合地址:结合功能和节点信息
-
层次化地址:采用层次化的 ID 分配结构
2.2 仲裁机制
仲裁原理:
CAN 总线采用非破坏性仲裁机制,通过比较标识符位来确定消息的优先级。扩展帧的仲裁过程与标准帧类似,但需要比较更多的位。
仲裁过程:
-
所有节点同时发送 SOF(Start of Frame)位
-
依次发送标识符位,从最高位开始
-
发送显性位(0)的节点继续发送
-
发送隐性位(1)但检测到显性位的节点退出仲裁
-
扩展帧需要比较 29 位 ID,标准帧只需比较 11 位 ID
优先级规则:
-
标识符值越小,优先级越高
-
扩展帧和标准帧可以在同一总线上共存
-
当扩展帧和标准帧的基本 ID 相同时,标准帧具有更高的优先级
// CAN消息优先级比较
int compare_can_priority(CAN_TxHeaderTypeDef *msg1, CAN_TxHeaderTypeDef *msg2) {
uint32_t id1, id2;
// 获取完整的ID(包括扩展位)
if (msg1->IDE == CAN_ID_EXT) {
id1 = (msg1->ExtId << 11) | msg1->StdId;
} else {
id1 = msg1->StdId | 0x800; // 标准帧在扩展帧空间中具有更高优先级
}
if (msg2->IDE == CAN_ID_EXT) {
id2 = (msg2->ExtId << 11) | msg2->StdId;
} else {
id2 = msg2->StdId | 0x800;
}
if (id1 < id2) {
return -1; // msg1优先级更高
} else if (id1 > id2) {
return 1; // msg2优先级更高
} else {
return 0; // 优先级相同
}
}
2.3 错误检测机制
错误检测方式:
CAN 扩展帧保留了传统 CAN 的所有错误检测机制:
1. 位错误检测
-
发送节点监控总线上的信号
-
如果发送的位值与检测到的位值不符,则检测到位错误
2. 填充错误检测
-
CAN 协议采用位填充技术,连续 5 个相同位后插入一个相反位
-
如果接收节点检测到 6 个连续相同位,则检测到填充错误
3. CRC 错误检测
-
每个 CAN 帧包含 15 位 CRC 校验码
-
接收节点重新计算 CRC,如果与接收到的 CRC 不符,则检测到 CRC 错误
4. 形式错误检测
-
检测帧格式的正确性
-
包括 SOF、IDE、RTR、DLC、ACK、EOF 等字段的格式检查
5. ACK 错误检测
-
发送节点在 ACK 段发送隐性位
-
如果没有收到任何节点的显性位响应,则检测到 ACK 错误
2.4 与标准帧的区别
主要区别:
| 特性 | 标准帧 | 扩展帧 |
|---|---|---|
| 标识符长度 | 11 位 | 29 位 |
| IDE 位 | 0 | 1 |
| SRR 位 | 无 | 1(隐性位) |
| 帧长度 | 44-108 位 | 64-128 位 |
| ID 空间 | 2048 个 | 536,870,912 个 |
| 仲裁时间 | 较短 | 较长 |
| 兼容性 | 所有 CAN 控制器都支持 | 需要 CAN 2.0B 或更高版本 |
帧格式差异:
标准帧格式:
+------+------+------+------+------+------+------+
| SOF | ID | RTR | IDE | DLC | DATA | CRC |
| 1bit |11bit | 1bit | 1bit | 4bit |0-8B |15bit |
+------+------+------+------+------+------+------+
| ACK | EOF | IFS |
| 2bit | 7bit | 3bit |
+------+------+------+
扩展帧格式:
+------+------+------+------+------+------+------+------+
| SOF | ID | SRR | IDE | ID | RTR | DLC | DATA |
| 1bit |11bit | 1bit | 1bit |18bit | 1bit | 4bit |0-8B |
+------+------+------+------+------+------+------+------+
| CRC | ACK | EOF | IFS |
|15bit | 2bit | 7bit | 3bit |
+------+------+------+------+
3. CAN 扩展帧的帧结构
3.1 帧结构详解
CAN 扩展帧由以下几个主要部分组成:
1. 帧起始(SOF - Start of Frame)
-
长度:1 位
-
值:显性位(0)
-
功能:标志帧的开始,用于同步
2. 基本标识符(Base ID)
-
长度:11 位
-
功能:与标准帧的标识符相同
-
格式:从最高位(MSB)到最低位(LSB)发送
3. 替代远程请求(SRR - Substitute Remote Request)
-
长度:1 位
-
值:隐性位(1)
-
功能:替代标准帧中的 RTR 位,在扩展帧中始终为隐性
4. 标识符扩展位(IDE - Identifier Extension)
-
长度:1 位
-
值:显性位(0)表示标准帧,隐性位(1)表示扩展帧
-
功能:标识帧格式类型
5. 扩展标识符(Extended ID)
-
长度:18 位
-
功能:扩展标识符的补充部分
-
格式:从最高位到最低位发送
6. 远程请求位(RTR - Remote Transmission Request)
-
长度:1 位
-
值:显性位(0)表示数据帧,隐性位(1)表示远程请求帧
-
功能:区分数据帧和远程请求帧
7. 数据长度码(DLC - Data Length Code)
-
长度:4 位
-
功能:指示数据字段的字节数
-
范围:0-8 字节(传统 CAN),0-64 字节(CAN FD)
8. 数据字段(Data Field)
-
长度:0-8 字节(传统 CAN),0-64 字节(CAN FD)
-
功能:承载实际的数据内容
-
格式:从最高字节的最高位开始发送
9. 循环冗余校验(CRC - Cyclic Redundancy Check)
-
长度:15 位
-
功能:检测数据传输错误
-
计算范围:从 SOF 到数据字段的所有位
10. CRC 界定符(CRC Delimiter)
-
长度:1 位
-
值:隐性位(1)
-
功能:标志 CRC 字段的结束
11. 确认字段(ACK - Acknowledge)
-
长度:2 位(ACK 槽和 ACK 界定符)
-
ACK 槽:发送节点发送隐性位,接收节点发送显性位表示确认
-
ACK 界定符:隐性位,标志 ACK 字段的结束
12. 帧结束(EOF - End of Frame)
-
长度:7 位
-
值:全部为隐性位(1)
-
功能:标志帧的结束
13. 帧间空间(IFS - Inter Frame Space)
-
长度:3 位
-
值:全部为隐性位(1)
-
功能:帧之间的间隔,允许节点准备下一次发送
3.2 位填充规则
位填充技术:
CAN 协议采用位填充技术来保证时钟同步:
-
当发送器检测到连续 5 个相同的位时,自动插入一个相反的位
-
接收器在接收过程中会自动删除这些填充位
-
位填充适用于除 EOF 和 IFS 之外的所有字段
填充示例:
原始数据:111110000011111
填充后: 11111000000111110
解释:
- 前5个1后插入0
- 中间5个0后插入1
- 后5个1后插入0
位填充实现:
// CAN位填充函数
uint32_t can_bit_stuffing(uint8_t *data, uint32_t length, uint8_t *stuffed_data) {
uint32_t stuffed_length = 0;
uint8_t current_bit = 0;
uint8_t same_bit_count = 0;
for (uint32_t i = 0; i < length; i++) {
for (int j = 7; j >= 0; j--) {
uint8_t bit = (data[i] >> j) & 0x01;
if (bit == current_bit) {
same_bit_count++;
if (same_bit_count == 5) {
// 连续5个相同位,插入相反位
stuffed_data[stuffed_length / 8] &= ~(1 << (7 - (stuffed_length % 8)));
stuffed_data[stuffed_length / 8] |= ((1 - bit) << (7 - (stuffed_length % 8)));
stuffed_length++;
same_bit_count = 1;
}
} else {
same_bit_count = 1;
current_bit = bit;
}
// 写入当前位
stuffed_data[stuffed_length / 8] &= ~(1 << (7 - (stuffed_length % 8)));
stuffed_data[stuffed_length / 8] |= (bit << (7 - (stuffed_length % 8)));
stuffed_length++;
}
}
return (stuffed_length + 7) / 8; // 返回字节数
}
3.3 CRC 计算
CRC 多项式:
CAN 协议使用的 CRC 多项式为:
CRC-15: x^15 + x^14 + x^10 + x^8 + x^7 + x^4 + x^3 + 1
CRC 计算过程:
// CAN CRC计算函数
uint16_t can_calculate_crc(uint8_t *data, uint32_t length) {
uint16_t crc = 0x0000;
const uint16_t polynomial = 0x4599; // CRC-15多项式
for (uint32_t i = 0; i < length; i++) {
for (int j = 7; j >= 0; j--) {
uint8_t bit = (data[i] >> j) & 0x01;
// 左移一位
crc <<= 1;
crc |= bit;
// 如果最高位为1,则与多项式异或
if (crc & 0x8000) {
crc ^= polynomial;
}
}
}
// 左移1位
crc <<= 1;
return crc & 0x7FFF; // 只保留低15位
}
3.4 帧长度计算
标准帧长度计算:
标准帧总长度 = 1 (SOF) + 11 (ID) + 1 (RTR) + 1 (IDE) + 4 (DLC) +
8*DLC (DATA) + 15 (CRC) + 1 (CRC Delimiter) +
2 (ACK) + 7 (EOF) + 3 (IFS)
扩展帧长度计算:
扩展帧总长度 = 1 (SOF) + 11 (Base ID) + 1 (SRR) + 1 (IDE) + 18 (Extended ID) +
1 (RTR) + 4 (DLC) + 8*DLC (DATA) + 15 (CRC) +
1 (CRC Delimiter) + 2 (ACK) + 7 (EOF) + 3 (IFS)
帧长度计算函数:
// CAN帧长度计算
uint32_t calculate_can_frame_length(CAN_TxHeaderTypeDef *tx_header) {
uint32_t length = 0;
// SOF
length += 1;
if (tx_header->IDE == CAN_ID_EXT) {
// 扩展帧
length += 11; // Base ID
length += 1; // SRR
length += 1; // IDE
length += 18; // Extended ID
} else {
// 标准帧
length += 11; // ID
length += 1; // IDE
}
// RTR
length += 1;
// DLC
length += 4;
// DATA
length += 8 * tx_header->DLC;
// CRC
length += 15;
// CRC Delimiter
length += 1;
// ACK
length += 2;
// EOF
length += 7;
// IFS
length += 3;
return length;
}
4. CAN 扩展帧的使用方法
4.1 发送扩展帧
发送扩展帧的步骤:
// 发送CAN扩展帧
HAL_StatusTypeDef can_send_extended_frame(CAN_HandleTypeDef *hcan,
uint32_t ext_id,
uint8_t *data,
uint8_t len) {
CAN_TxHeaderTypeDef tx_header;
uint32_t tx_mailbox;
// 配置发送头
tx_header.StdId = (ext_id >> 18) & 0x7FF; // 基本ID(高11位)
tx_header.ExtId = ext_id & 0x3FFFF; // 扩展ID(低18位)
tx_header.RTR = CAN_RTR_DATA; // 数据帧
tx_header.IDE = CAN_ID_EXT; // 扩展帧
tx_header.DLC = len; // 数据长度
tx_header.TransmitGlobalTime = DISABLE; // 不发送时间戳
// 检查数据长度
if (len > 8) {
return HAL_ERROR;
}
// 添加发送消息
return HAL_CAN_AddTxMessage(hcan, &tx_header, data, &tx_mailbox);
}
批量发送扩展帧:
// 批量发送CAN扩展帧
HAL_StatusTypeDef can_send_extended_frames_batch(CAN_HandleTypeDef *hcan,
CAN_Extended_Frame *frames,
uint32_t count) {
HAL_StatusTypeDef status = HAL_OK;
for (uint32_t i = 0; i < count; i++) {
// 等待发送邮箱可用
uint32_t start_time = HAL_GetTick();
while (HAL_CAN_GetTxMailboxesFreeLevel(hcan) == 0) {
if (HAL_GetTick() - start_time > 100) {
return HAL_TIMEOUT;
}
}
// 发送帧
status = can_send_extended_frame(hcan, frames[i].id,
frames[i].data, frames[i].length);
if (status != HAL_OK) {
break;
}
}
return status;
}
4.2 接收扩展帧
配置过滤器接收扩展帧:
// 配置CAN过滤器接收扩展帧
HAL_StatusTypeDef can_configure_extended_filter(CAN_HandleTypeDef *hcan) {
CAN_FilterTypeDef sFilterConfig = {0};
sFilterConfig.FilterBank = 0;
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
// 配置过滤器接收所有扩展帧
sFilterConfig.FilterIdHigh = 0x0000;
sFilterConfig.FilterIdLow = 0x0000;
sFilterConfig.FilterMaskIdHigh = 0x0000;
sFilterConfig.FilterMaskIdLow = 0x0000;
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.SlaveStartFilterBank = 14;
return HAL_CAN_ConfigFilter(hcan, &sFilterConfig);
}
接收扩展帧中断处理:
// CAN接收中断回调函数
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) {
CAN_RxHeaderTypeDef rx_header;
uint8_t rx_data[8];
// 获取接收消息
if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rx_header, rx_data) == HAL_OK) {
if (rx_header.IDE == CAN_ID_EXT) {
// 处理扩展帧
process_extended_frame(&rx_header, rx_data);
} else {
// 处理标准帧
process_standard_frame(&rx_header, rx_data);
}
}
}
// 处理扩展帧
void process_extended_frame(CAN_RxHeaderTypeDef *header, uint8_t *data) {
// 计算完整的29位ID
uint32_t full_id = (header->ExtId << 11) | header->StdId;
printf("收到扩展帧:n");
printf(" 完整ID: 0x%lxn", full_id);
printf(" 基本ID: 0x%xn", header->StdId);
printf(" 扩展ID: 0x%lxn", header->ExtId);
printf(" 数据长度: %d字节n", header->DLC);
printf(" 数据: ");
for (int i = 0; i < header->DLC; i++) {
printf("%02X ", data[i]);
}
printf("n");
// 根据ID处理不同的消息
switch (full_id) {
case 0x12345678:
process_sensor_data(data, header->DLC);
break;
case 0x87654321:
process_control_command(data, header->DLC);
break;
default:
process_unknown_message(header, data);
break;
}
}
4.3 远程请求帧
发送远程请求帧:
// 发送扩展远程请求帧
HAL_StatusTypeDef can_send_extended_remote_request(CAN_HandleTypeDef *hcan,
uint32_t ext_id,
uint8_t expected_length) {
CAN_TxHeaderTypeDef tx_header;
uint32_t tx_mailbox;
uint8_t dummy_data[8] = {0}; // 远程请求帧数据字段被忽略
// 配置发送头
tx_header.StdId = (ext_id >> 18) & 0x7FF; // 基本ID
tx_header.ExtId = ext_id & 0x3FFFF; // 扩展ID
tx_header.RTR = CAN_RTR_REMOTE; // 远程请求帧
tx_header.IDE = CAN_ID_EXT; // 扩展帧
tx_header.DLC = expected_length; // 期望的数据长度
tx_header.TransmitGlobalTime = DISABLE;
// 发送远程请求帧
return HAL_CAN_AddTxMessage(hcan, &tx_header, dummy_data, &tx_mailbox);
}
处理远程请求帧:
// 处理远程请求帧
void process_remote_request(CAN_RxHeaderTypeDef *header) {
uint32_t full_id = (header->ExtId << 11) | header->StdId;
printf("收到远程请求帧:n");
printf(" 完整ID: 0x%lxn", full_id);
printf(" 期望数据长度: %d字节n", header->DLC);
// 根据请求ID发送相应的数据
switch (full_id) {
case 0x11111111:
send_sensor_data_response(header->DLC);
break;
case 0x22222222:
send_status_data_response(header->DLC);
break;
default:
printf("未知的远程请求ID: 0x%lxn", full_id);
break;
}
}
// 发送传感器数据响应
void send_sensor_data_response(uint8_t requested_length) {
uint8_t sensor_data[8];
uint32_t response_id = 0x11111111;
// 采集传感器数据
collect_sensor_data(sensor_data);
// 发送响应数据
can_send_extended_frame(&hcan1, response_id, sensor_data, requested_length);
}
4.4 混合帧格式通信
同时支持标准帧和扩展帧:
// 配置CAN控制器同时支持标准帧和扩展帧
HAL_StatusTypeDef can_configure_mixed_mode(CAN_HandleTypeDef *hcan) {
// 1. 配置过滤器接收标准帧和扩展帧
CAN_FilterTypeDef filter1, filter2;
// 过滤器0:接收标准帧
filter1.FilterBank = 0;
filter1.FilterMode = CAN_FILTERMODE_IDMASK;
filter1.FilterScale = CAN_FILTERSCALE_16BIT;
filter1.FilterIdHigh = 0x0000;
filter1.FilterIdLow = 0x0000;
filter1.FilterMaskIdHigh = 0x0000;
filter1.FilterMaskIdLow = 0x0000;
filter1.FilterFIFOAssignment = CAN_RX_FIFO0;
filter1.FilterActivation = ENABLE;
// 过滤器1:接收扩展帧
filter2.FilterBank = 1;
filter2.FilterMode = CAN_FILTERMODE_IDMASK;
filter2.FilterScale = CAN_FILTERSCALE_32BIT;
filter2.FilterIdHigh = 0x0000;
filter2.FilterIdLow = 0x0000;
filter2.FilterMaskIdHigh = 0x0000;
filter2.FilterMaskIdLow = 0x0000;
filter2.FilterFIFOAssignment = CAN_RX_FIFO1;
filter2.FilterActivation = ENABLE;
if (HAL_CAN_ConfigFilter(hcan, &filter1) != HAL_OK) {
return HAL_ERROR;
}
if (HAL_CAN_ConfigFilter(hcan, &filter2) != HAL_OK) {
return HAL_ERROR;
}
return HAL_OK;
}
动态帧格式选择:
// 根据ID长度自动选择帧格式
HAL_StatusTypeDef can_send_auto_format(CAN_HandleTypeDef *hcan,
uint32_t id,
uint8_t *data,
uint8_t len) {
if (id <= 0x7FF) {
// 使用标准帧
return can_send_standard_frame(hcan, (uint16_t)id, data, len);
} else {
// 使用扩展帧
return can_send_extended_frame(hcan, id, data, len);
}
}
// 发送标准帧
HAL_StatusTypeDef can_send_standard_frame(CAN_HandleTypeDef *hcan,
uint16_t std_id,
uint8_t *data,
uint8_t len) {
CAN_TxHeaderTypeDef tx_header;
uint32_t tx_mailbox;
tx_header.StdId = std_id;
tx_header.ExtId = 0;
tx_header.RTR = CAN_RTR_DATA;
tx_header.IDE = CAN_ID_STD;
tx_header.DLC = len;
tx_header.TransmitGlobalTime = DISABLE;
return HAL_CAN_AddTxMessage(hcan, &tx_header, data, &tx_mailbox);
}
5. CAN 扩展帧的配置要点
5.1 控制器配置
CAN 控制器初始化配置:
// CAN控制器初始化(支持扩展帧)
HAL_StatusTypeDef MX_CAN1_Init(void) {
hcan1.Instance = CAN1;
hcan1.Init.Prescaler = 4;
hcan1.Init.Mode = CAN_MODE_NORMAL;
hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
hcan1.Init.TimeSeg1 = CAN_BS1_6TQ;
hcan1.Init.TimeSeg2 = CAN_BS2_1TQ;
hcan1.Init.TimeTriggeredMode = DISABLE;
hcan1.Init.AutoBusOff = ENABLE;
hcan1.Init.AutoWakeUp = DISABLE;
hcan1.Init.AutoRetransmission = ENABLE;
hcan1.Init.ReceiveFifoLocked = DISABLE;
hcan1.Init.TransmitFifoPriority = DISABLE;
if (HAL_CAN_Init(&hcan1) != HAL_OK) {
Error_Handler();
}
// 配置过滤器支持扩展帧
if (can_configure_extended_filter(&hcan1) != HAL_OK) {
Error_Handler();
}
return HAL_OK;
}
GPIO 配置:
// CAN GPIO配置
void MX_GPIO_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOB_CLK_ENABLE();
// CAN1_RX PB8
GPIO_InitStruct.Pin = GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF9_CAN1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
// CAN1_TX PB9
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF9_CAN1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
5.2 过滤器配置
扩展帧过滤器配置:
// 配置CAN过滤器接收特定范围的扩展帧
HAL_StatusTypeDef can_configure_filter_for_extended_range(CAN_HandleTypeDef *hcan,
uint32_t start_id,
uint32_t end_id) {
CAN_FilterTypeDef sFilterConfig = {0};
sFilterConfig.FilterBank = 0;
sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST;
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
// 计算过滤器掩码
uint32_t mask = calculate_filter_mask(start_id, end_id);
sFilterConfig.FilterIdHigh = (start_id >> 16) & 0xFFFF;
sFilterConfig.FilterIdLow = start_id & 0xFFFF;
sFilterConfig.FilterMaskIdHigh = (mask >> 16) & 0xFFFF;
sFilterConfig.FilterMaskIdLow = mask & 0xFFFF;
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.SlaveStartFilterBank = 14;
return HAL_CAN_ConfigFilter(hcan, &sFilterConfig);
}
// 计算过滤器掩码
uint32_t calculate_filter_mask(uint32_t start_id, uint32_t end_id) {
uint32_t mask = 0xFFFFFFFF;
uint32_t xor_result = start_id ^ end_id;
// 找到最高的不同位
int highest_diff_bit = 31;
while (highest_diff_bit >= 0 && ((xor_result >> highest_diff_bit) & 0x01) == 0) {
highest_diff_bit--;
}
// 生成掩码
if (highest_diff_bit >= 0) {
mask = ~((1 << (highest_diff_bit + 1)) - 1);
}
return mask;
}
多过滤器配置:
// 配置多个过滤器处理不同类型的扩展帧
HAL_StatusTypeDef can_configure_multiple_filters(CAN_HandleTypeDef *hcan) {
CAN_FilterTypeDef filter;
// 过滤器0:处理传感器数据帧(ID范围:0x10000000-0x1FFFFFFF)
filter.FilterBank = 0;
filter.FilterMode = CAN_FILTERMODE_IDMASK;
filter.FilterScale = CAN_FILTERSCALE_32BIT;
filter.FilterIdHigh = 0x1000;
filter.FilterIdLow = 0x0000;
filter.FilterMaskIdHigh = 0xF000;
filter.FilterMaskIdLow = 0x0000;
filter.FilterFIFOAssignment = CAN_RX_FIFO0;
filter.FilterActivation = ENABLE;
if (HAL_CAN_ConfigFilter(hcan, &filter) != HAL_OK) {
return HAL_ERROR;
}
// 过滤器1:处理控制命令帧(ID范围:0x20000000-0x2FFFFFFF)
filter.FilterBank = 1;
filter.FilterIdHigh = 0x2000;
filter.FilterIdLow = 0x0000;
filter.FilterMaskIdHigh = 0xF000;
filter.FilterMaskIdLow = 0x0000;
filter.FilterFIFOAssignment = CAN_RX_FIFO1;
filter.FilterActivation = ENABLE;
if (HAL_CAN_ConfigFilter(hcan, &filter) != HAL_OK) {
return HAL_ERROR;
}
// 过滤器2:处理诊断帧(特定ID:0x30000000)
filter.FilterBank = 2;
filter.FilterMode = CAN_FILTERMODE_IDLIST;
filter.FilterIdHigh = 0x3000;
filter.FilterIdLow = 0x0000;
filter.FilterMaskIdHigh = 0xFFFF;
filter.FilterMaskIdLow = 0xFFFF;
filter.FilterFIFOAssignment = CAN_RX_FIFO1;
filter.FilterActivation = ENABLE;
if (HAL_CAN_ConfigFilter(hcan, &filter) != HAL_OK) {
return HAL_ERROR;
}
return HAL_OK;
}
5.3 中断配置
CAN 中断配置:
// 配置CAN中断
HAL_StatusTypeDef can_configure_interrupts(CAN_HandleTypeDef *hcan) {
// 启用接收中断
if (HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK) {
return HAL_ERROR;
}
if (HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO1_MSG_PENDING) != HAL_OK) {
return HAL_ERROR;
}
// 启用发送中断
if (HAL_CAN_ActivateNotification(hcan, CAN_IT_TX_MAILBOX_EMPTY) != HAL_OK) {
return HAL_ERROR;
}
// 启用错误中断
if (HAL_CAN_ActivateNotification(hcan, CAN_IT_ERROR) != HAL_OK) {
return HAL_ERROR;
}
// 启用总线状态变化中断
if (HAL_CAN_ActivateNotification(hcan, CAN_IT_BUSOFF) != HAL_OK) {
return HAL_ERROR;
}
if (HAL_CAN_ActivateNotification(hcan, CAN_IT_WAKEUP) != HAL_OK) {
return HAL_ERROR;
}
return HAL_OK;
}
中断优先级配置:
// 配置CAN中断优先级
void can_configure_interrupt_priority() {
// CAN RX0中断
HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn);
// CAN RX1中断
HAL_NVIC_SetPriority(CAN1_RX1_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(CAN1_RX1_IRQn);
// CAN SCE中断(错误和状态变化)
HAL_NVIC_SetPriority(CAN1_SCE_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(CAN1_SCE_IRQn);
// CAN TX中断
HAL_NVIC_SetPriority(CAN1_TX_IRQn, 3, 0);
HAL_NVIC_EnableIRQ(CAN1_TX_IRQn);
}
5.4 错误处理配置
CAN 错误处理:
// CAN错误处理函数
void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan) {
uint32_t error_code = HAL_CAN_GetError(hcan);
// 记录错误信息
can_error_log(error_code);
switch (error_code) {
case HAL_CAN_ERROR_NONE:
// 无错误
break;
case HAL_CAN_ERROR_EWG:
// 位错误
handle_bit_error();
break;
case HAL_CAN_ERROR_EPV:
// 填充错误
handle_stuff_error();
break;
case HAL_CAN_ERROR_EFL:
// 格式错误
handle_format_error();
break;
case HAL_CAN_ERROR_ACK:
// ACK错误
handle_ack_error();
break;
case HAL_CAN_ERROR_BOF:
// 总线关闭错误
handle_bus_off();
break;
case HAL_CAN_ERROR_FD_UNSUPPORTED:
// CAN FD不支持
handle_fd_unsupported();
break;
default:
// 其他错误
handle_other_error(error_code);
break;
}
}
// 总线关闭恢复处理
void handle_bus_off() {
printf("CAN总线关闭,尝试恢复...n");
// 等待总线恢复
HAL_Delay(1000);
// 重新初始化CAN控制器
if (MX_CAN1_Init() == HAL_OK) {
printf("CAN总线恢复成功n");
} else {
printf("CAN总线恢复失败n");
}
}
错误统计和监控:
// CAN错误统计
typedef struct {
uint32_t bit_errors;
uint32_t stuff_errors;
uint32_t crc_errors;
uint32_t form_errors;
uint32_t ack_errors;
uint32_t bus_off_count;
uint32_t wakeup_count;
} CAN_Error_Stats;
CAN_Error_Stats can_error_stats = {0};
// 更新错误统计
void update_can_error_stats(uint32_t error_code) {
switch (error_code) {
case HAL_CAN_ERROR_EWG:
can_error_stats.bit_errors++;
break;
case HAL_CAN_ERROR_EPV:
can_error_stats.stuff_errors++;
break;
case HAL_CAN_ERROR_EFL:
can_error_stats.form_errors++;
break;
case HAL_CAN_ERROR_ACK:
can_error_stats.ack_errors++;
break;
case HAL_CAN_ERROR_BOF:
can_error_stats.bus_off_count++;
break;
case HAL_CAN_ERROR_WAKEUP:
can_error_stats.wakeup_count++;
break;
}
}
// 打印错误统计信息
void print_can_error_stats() {
printf("CAN错误统计:n");
printf(" 位错误: %lun", can_error_stats.bit_errors);
printf(" 填充错误: %lun", can_error_stats.stuff_errors);
printf(" CRC错误: %lun", can_error_stats.crc_errors);
printf(" 格式错误: %lun", can_error_stats.form_errors);
printf(" ACK错误: %lun", can_error_stats.ack_errors);
printf(" 总线关闭次数: %lun", can_error_stats.bus_off_count);
printf(" 唤醒次数: %lun", can_error_stats.wakeup_count);
}
6. CAN 扩展帧的优势与应用场景
6.1 技术优势
1. 更大的 ID 空间
-
扩展帧提供 29 位标识符,支持 536,870,912 个不同的 ID
-
相比标准帧的 2048 个 ID,ID 空间扩大了 262,144 倍
-
能够满足复杂系统对大量节点和消息类型的需求
2. 更好的系统扩展性
-
支持更多的节点接入 CAN 总线
-
能够定义更丰富的消息类型和功能
-
便于系统的模块化设计和扩展
3. 更灵活的 ID 分配策略
-
可以采用层次化的 ID 分配结构
-
支持功能地址、节点地址、混合地址等多种分配方式
-
便于系统的组织和管理
4. 更好的消息分类和管理
-
可以根据消息的功能、优先级、来源等进行分类
-
便于实现消息的过滤和路由
-
提高系统的可维护性和可扩展性
5. 向下兼容性
-
扩展帧格式与标准帧格式兼容
-
支持扩展帧的控制器可以接收和处理标准帧
-
保护现有投资,便于系统升级
6.2 适用场景
1. 汽车电子系统
-
应用需求:大量 ECU 节点,复杂的消息交互
-
典型应用:车身控制系统、动力传动系统、底盘控制系统
-
优势:支持大量节点,便于功能扩展
汽车电子应用示例:
// 汽车电子CAN扩展帧ID分配
typedef enum {
// 动力传动系统 (0x10000000-0x1FFFFFFF)
CAN_ID_ENGINE_CONTROL = 0x10000001,
CAN_ID_TRANSMISSION_CONTROL = 0x10000002,
CAN_ID_BRAKE_SYSTEM = 0x10000003,
CAN_ID_STEERING_SYSTEM = 0x10000004,
// 车身控制系统 (0x20000000-0x2FFFFFFF)
CAN_ID_BODY_CONTROL = 0x20000001,
CAN_ID_LIGHT_CONTROL = 0x20000002,
CAN_ID_WINDOW_CONTROL = 0x20000003,
CAN_ID_CLIMATE_CONTROL = 0x20000004,
// 驾驶员辅助系统 (0x30000000-0x3FFFFFFF)
CAN_ID_ADAS_CONTROL = 0x30000001,
CAN_ID_CRUISE_CONTROL = 0x30000002,
CAN_ID_PARKING_ASSIST = 0x30000003,
CAN_ID_COLLISION_WARNING = 0x30000004,
// 信息娱乐系统 (0x40000000-0x4FFFFFFF)
CAN_ID_INFOTAINMENT = 0x40000001,
CAN_ID_NAVIGATION = 0x40000002,
CAN_ID_AUDIO_SYSTEM = 0x40000003,
CAN_ID_TELEMATICS = 0x40000004,
// 诊断系统 (0x50000000-0x5FFFFFFF)
CAN_ID_DIAGNOSTIC = 0x50000001,
CAN_ID_DTC_REPORT = 0x50000002,
CAN_ID_SOFTWARE_UPDATE = 0x50000003
} Automotive_CAN_Extended_IDs;
2. 工业控制系统
-
应用需求:分布式控制,大量传感器和执行器
-
典型应用:工厂自动化、机器人控制、过程控制
-
优势:支持大量节点,便于系统集成
工业控制应用示例:
// 工业控制CAN扩展帧ID分配
typedef enum {
// 控制器节点 (0x10000000-0x1FFFFFFF)
CAN_ID_MASTER_CONTROLLER = 0x10000001,
CAN_ID_SLAVE_CONTROLLER_1 = 0x10000002,
CAN_ID_SLAVE_CONTROLLER_2 = 0x10000003,
CAN_ID_SLAVE_CONTROLLER_3 = 0x10000004,
// 传感器节点 (0x20000000-0x2FFFFFFF)
CAN_ID_TEMPERATURE_SENSOR = 0x20000001,
CAN_ID_PRESSURE_SENSOR = 0x20000002,
CAN_ID_FLOW_SENSOR = 0x20000003,
CAN_ID_POSITION_SENSOR = 0x20000004,
CAN_ID_VELOCITY_SENSOR = 0x20000005,
// 执行器节点 (0x30000000-0x3FFFFFFF)
CAN_ID_MOTOR_DRIVER_1 = 0x30000001,
CAN_ID_MOTOR_DRIVER_2 = 0x30000002,
CAN_ID_VALVE_CONTROL_1 = 0x30000003,
CAN_ID_VALVE_CONTROL_2 = 0x30000004,
// 人机界面 (0x40000000-0x4FFFFFFF)
CAN_ID_HMI_MAIN = 0x40000001,
CAN_ID_HMI_TOUCHSCREEN = 0x40000002,
CAN_ID_HMI_KEYPAD = 0x40000003,
CAN_ID_HMI_DISPLAY = 0x40000004,
// 通信网关 (0x50000000-0x5FFFFFFF)
CAN_ID_GATEWAY_ETHERNET = 0x50000001,
CAN_ID_GATEWAY_PROFINET = 0x50000002,
CAN_ID_GATEWAY_MODBUS = 0x50000003
} Industrial_CAN_Extended_IDs;
3. 医疗设备
-
应用需求:高精度控制,可靠的数据传输
-
典型应用:医疗监护设备、手术机器人、诊断设备
-
优势:支持复杂的控制算法,便于设备集成
医疗设备应用示例:
// 医疗设备CAN扩展帧ID分配
typedef enum {
// 监护设备 (0x10000000-0x1FFFFFFF)
CAN_ID_ECG_MONITOR = 0x10000001,
CAN_ID_BLOOD_PRESSURE = 0x10000002,
CAN_ID_OXYGEN_SATURATION = 0x10000003,
CAN_ID_TEMPERATURE_MONITOR = 0x10000004,
// 治疗设备 (0x20000000-0x2FFFFFFF)
CAN_ID_INFUSION_PUMP = 0x20000001,
CAN_ID_VENTILATOR = 0x20000002,
CAN_ID_DEFIBRILLATOR = 0x20000003,
CAN_ID_DIALYSIS_MACHINE = 0x20000004,
// 手术设备 (0x30000000-0x3FFFFFFF)
CAN_ID_SURGICAL_ROBOT = 0x30000001,
CAN_ID_LASER_SURGERY = 0x30000002,
CAN_ID_ENDOSCOPE = 0x30000003,
CAN_ID_ANESTHESIA_MACHINE = 0x30000004,
// 数据管理 (0x40000000-0x4FFFFFFF)
CAN_ID_DATA_LOGGER = 0x40000001,
CAN_ID_PATIENT_DATABASE = 0x40000002,
CAN_ID_REMOTE_MONITORING = 0x40000003,
CAN_ID_ALARM_SYSTEM = 0x40000004
} Medical_CAN_Extended_IDs;
4. 智能交通系统
-
应用需求:车辆间通信,基础设施集成
-
典型应用:车联网、智能路灯、交通监控
-
优势:支持大量节点,便于系统扩展
5. 航空航天系统
-
应用需求:高可靠性,实时控制
-
典型应用:飞行控制系统、航空电子设备
-
优势:支持复杂的控制逻辑,便于故障诊断
6.3 与其他协议的比较
CAN 扩展帧 vs Ethernet:
| 特性 | CAN 扩展帧 | Ethernet |
|---|---|---|
| 传输速率 | 最高 8 Mbps (CAN FD) | 10 Mbps-100 Gbps |
| 节点数量 | 理论上无限制 | 受交换机端口限制 |
| 实时性 | 高(确定的延迟) | 一般(可能有冲突) |
| 可靠性 | 高(差分信号,错误检测) | 一般(需要额外的可靠性机制) |
| 成本 | 低 | 高 |
| 复杂度 | 低 | 高 |
CAN 扩展帧 vs FlexRay:
| 特性 | CAN 扩展帧 | FlexRay |
|---|---|---|
| 传输速率 | 最高 8 Mbps | 最高 10 Mbps |
| 拓扑结构 | 总线型 | 总线型或星型 |
| 实时性 | 高 | 很高(时间触发) |
| 可靠性 | 高 | 很高(冗余设计) |
| 复杂度 | 低 | 高 |
| 成本 | 低 | 高 |
CAN 扩展帧 vs LIN:
| 特性 | CAN 扩展帧 | LIN |
|---|---|---|
| 传输速率 | 最高 8 Mbps | 最高 20 kbps |
| 节点数量 | 理论上无限制 | 最多 16 个 |
| 应用场景 | 复杂控制 | 简单控制 |
| 成本 | 中 | 低 |
| 复杂度 | 中 | 低 |
7. CAN 扩展帧的实战案例
7.1 汽车电子控制系统
项目背景:
某汽车制造商开发新一代汽车电子控制系统,需要连接大量 ECU(电子控制单元),实现复杂的车辆控制功能。
项目需求:
-
ECU 节点数量:50 个以上
-
消息类型:200 种以上
-
通信速率:500 kbps
-
实时性要求:控制信号延迟 < 10ms
-
可靠性要求:错误率 < 0.1%
系统架构设计:
1. 硬件架构
// 汽车电子CAN网络硬件架构
typedef struct {
CAN_Controller_Type controller_type;
CAN_Transceiver_Type transceiver_type;
CAN_Bus_Topology topology;
Power_Requirements power;
EMI_Requirements emi;
} Automotive_CAN_Hardware_Architecture;
Automotive_CAN_Hardware_Architecture automotive_hw_arch = {
.controller_type = CAN_CONTROLLER_STM32F4,
.transceiver_type = TRANSCEIVER_TJA1040,
.topology = {
.bus_length = 15.0,
.baudrate = 500000,
.node_count = 50,
.is_terminated = true,
.characteristic_impedance = 120.0
},
.power = {
.voltage = 5.0,
.current = 2.0,
.power = 10.0
},
.emi = {
.emc_level = EMC_LEVEL_4,
.shielding = true,
.twisted_pair = true
}
};
2. ID 分配策略
// 汽车电子CAN扩展帧ID分配策略
typedef enum {
// 基本ID范围分配
BASE_ID_POWERTRAIN = 0x100, // 动力传动系统 (0x100-0x1FF)
BASE_ID_CHASSIS = 0x200, // 底盘系统 (0x200-0x2FF)
BASE_ID_BODY = 0x300, // 车身系统 (0x300-0x3FF)
BASE_ID_ADAS = 0x400, // 驾驶员辅助系统 (0x400-0x4FF)
BASE_ID_INFOTAINMENT = 0x500, // 信息娱乐系统 (0x500-0x5FF)
BASE_ID_DIAGNOSTIC = 0x600 // 诊断系统 (0x600-0x6FF)
} BASE_ID_RANGES;
// 扩展ID位定义
typedef enum {
EXT_ID_FUNCTION_SHIFT = 12, // 功能代码偏移
EXT_ID_NODE_SHIFT = 6, // 节点ID偏移
EXT_ID_INSTANCE_SHIFT = 0 // 实例ID偏移
EXT_ID_FUNCTION_MASK = 0x3F << EXT_ID_FUNCTION_SHIFT,
EXT_ID_NODE_MASK = 0x3F << EXT_ID_NODE_SHIFT,
EXT_ID_INSTANCE_MASK = 0x3F << EXT_ID_INSTANCE_SHIFT
} EXT_ID_BITS;
// 构建完整的扩展ID
uint32_t build_automotive_can_id(BASE_ID_RANGES base_range,
uint8_t function_code,
uint8_t node_id,
uint8_t instance_id) {
uint32_t base_id = base_range;
uint32_t ext_id = 0;
// 构建扩展ID部分
ext_id |= ((function_code & 0x3F) << EXT_ID_FUNCTION_SHIFT);
ext_id |= ((node_id & 0x3F) << EXT_ID_NODE_SHIFT);
ext_id |= ((instance_id & 0x3F) << EXT_ID_INSTANCE_SHIFT);
// 构建完整的29位ID
return (ext_id << 11) | base_id;
}
3. 消息定义
// 汽车电子CAN消息定义
typedef enum {
// 动力传动系统消息
MSG_ID_ENGINE_STATUS = build_automotive_can_id(BASE_ID_POWERTRAIN, 0x01, 0x01, 0x00),
MSG_ID_ENGINE_CONTROL = build_automotive_can_id(BASE_ID_POWERTRAIN, 0x02, 0x01, 0x00),
MSG_ID_TRANSMISSION_STATUS = build_automotive_can_id(BASE_ID_POWERTRAIN, 0x01, 0x02, 0x00),
MSG_ID_TRANSMISSION_CONTROL = build_automotive_can_id(BASE_ID_POWERTRAIN, 0x02, 0x02, 0x00),
// 底盘系统消息
MSG_ID_BRAKE_STATUS = build_automotive_can_id(BASE_ID_CHASSIS, 0x01, 0x01, 0x00),
MSG_ID_BRAKE_CONTROL = build_automotive_can_id(BASE_ID_CHASSIS, 0x02, 0x01, 0x00),
MSG_ID_STEERING_STATUS = build_automotive_can_id(BASE_ID_CHASSIS, 0x01, 0x02, 0x00),
MSG_ID_STEERING_CONTROL = build_automotive_can_id(BASE_ID_CHASSIS, 0x02, 0x02, 0x00),
// 车身系统消息
MSG_ID_BODY_STATUS = build_automotive_can_id(BASE_ID_BODY, 0x01, 0x01, 0x00),
MSG_ID_LIGHT_CONTROL = build_automotive_can_id(BASE_ID_BODY, 0x02, 0x01, 0x00),
MSG_ID_WINDOW_CONTROL = build_automotive_can_id(BASE_ID_BODY, 0x02, 0x02, 0x00),
// 更多消息定义...
} Automotive_CAN_Message_IDs;
4. 数据结构定义
// 发动机状态数据结构
typedef struct {
uint16_t engine_speed; // 发动机转速 (rpm)
uint16_t engine_load; // 发动机负荷 (%)
int16_t coolant_temperature; // 冷却液温度 (°C × 10)
uint16_t oil_pressure; // 机油压力 (kPa)
uint8_t engine_status; // 发动机状态
uint8_t fuel_consumption; // 燃油消耗 (L/100km × 10)
uint8_t ignition_timing; // 点火提前角 (°)
uint8_t reserved; // 预留
} Engine_Status_Data;
// 发动机控制数据结构
typedef struct {
uint16_t target_speed; // 目标转速 (rpm)
uint8_t throttle_position; // 节气门位置 (%)
uint8_t fuel_injection; // 燃油喷射量 (ms × 10)
uint8_t ignition_advance; // 点火提前角 (°)
uint8_t control_mode; // 控制模式
uint8_t diagnostic_request; // 诊断请求
uint8_t reserved[2]; // 预留
} Engine_Control_Data;
5. 通信实现
// 发送发动机状态消息
void send_engine_status(Engine_Status_Data *status) {
uint8_t tx_data[8];
// 数据打包(大端字节序)
tx_data[0] = (status->engine_speed >> 8) & 0xFF;
tx_data[1] = status->engine_speed & 0xFF;
tx_data[2] = (status->engine_load >> 8) & 0xFF;
tx_data[3] = status->engine_load & 0xFF;
tx_data[4] = (status->coolant_temperature >> 8) & 0xFF;
tx_data[5] = status->coolant_temperature & 0xFF;
tx_data[6] = (status->oil_pressure >> 8) & 0xFF;
tx_data[7] = status->oil_pressure & 0xFF;
// 发送扩展帧
can_send_extended_frame(&hcan1, MSG_ID_ENGINE_STATUS, tx_data, 8);
// 发送第二帧数据
tx_data[0] = status->engine_status;
tx_data[1] = status->fuel_consumption;
tx_data[2] = status->ignition_timing;
tx_data[3] = status->reserved;
tx_data[4] = 0; // 预留
tx_data[5] = 0;
tx_data[6] = 0;
tx_data[7] = 0;
can_send_extended_frame(&hcan1, MSG_ID_ENGINE_STATUS + 1, tx_data, 4);
}
// 接收发动机控制消息
void receive_engine_control(CAN_RxHeaderTypeDef *header, uint8_t *data) {
if (header->ExtId == MSG_ID_ENGINE_CONTROL) {
Engine_Control_Data control_data;
// 数据解包
control_data.target_speed = (data[0] << 8) | data[1];
control_data.throttle_position = data[2];
control_data.fuel_injection = data[3];
control_data.ignition_advance = data[4];
control_data.control_mode = data[5];
control_data.diagnostic_request = data[6];
// 执行控制逻辑
execute_engine_control(&control_data);
}
}
7.2 工业自动化控制系统
项目背景:
某工业自动化公司开发分布式控制系统,需要连接大量传感器和执行器,实现复杂的工业控制功能。
项目需求:
-
节点数量:100 个以上
-
传感器类型:温度、压力、流量、位置等
-
执行器类型:电机、阀门、气缸等
-
通信速率:250 kbps
-
实时性要求:控制周期 < 100ms
系统设计:
1. ID 分配方案
// 工业控制CAN扩展帧ID分配方案
typedef enum {
// 系统级消息 (0x00000000-0x0FFFFFFF)
CAN_ID_SYSTEM_HEARTBEAT = 0x00000001,
CAN_ID_SYSTEM_CONFIG = 0x00000002,
CAN_ID_SYSTEM_ALARM = 0x00000003,
// 控制器消息 (0x10000000-0x1FFFFFFF)
CAN_ID_MASTER_CONTROLLER = 0x10000001,
CAN_ID_SLAVE_CONTROLLER_BASE = 0x10010000,
// 传感器消息 (0x20000000-0x2FFFFFFF)
CAN_ID_TEMPERATURE_SENSOR_BASE = 0x20000000,
CAN_ID_PRESSURE_SENSOR_BASE = 0x20010000,
CAN_ID_FLOW_SENSOR_BASE = 0x20020000,
CAN_ID_POSITION_SENSOR_BASE = 0x20030000,
// 执行器消息 (0x30000000-0x3FFFFFFF)
CAN_ID_MOTOR_DRIVER_BASE = 0x30000000,
CAN_ID_VALVE_CONTROL_BASE = 0x30010000,
CAN_ID_CYLINDER_CONTROL_BASE = 0x30020000,
// HMI消息 (0x40000000-0x4FFFFFFF)
CAN_ID_HMI_MAIN = 0x40000001,
CAN_ID_HMI_TOUCHSCREEN = 0x40000002,
CAN_ID_HMI_KEYPAD = 0x40000003,
// 诊断消息 (0x50000000-0x5FFFFFFF)
CAN_ID_DIAGNOSTIC_REQUEST = 0x50000001,
CAN_ID_DIAGNOSTIC_RESPONSE = 0x50000002
} Industrial_CAN_IDs;
// 构建传感器ID
uint32_t build_sensor_id(Sensor_Type type, uint8_t module_id, uint8_t channel_id) {
uint32_t base_id;
switch (type) {
case SENSOR_TEMPERATURE:
base_id = CAN_ID_TEMPERATURE_SENSOR_BASE;
break;
case SENSOR_PRESSURE:
base_id = CAN_ID_PRESSURE_SENSOR_BASE;
break;
case SENSOR_FLOW:
base_id = CAN_ID_FLOW_SENSOR_BASE;
break;
case SENSOR_POSITION:
base_id = CAN_ID_POSITION_SENSOR_BASE;
break;
default:
return 0;
}
return base_id | ((module_id & 0xFF) << 8) | (channel_id & 0xFF);
}
// 构建执行器ID
uint32_t build_actuator_id(Actuator_Type type, uint8_t module_id, uint8_t channel_id) {
uint32_t base_id;
switch (type) {
case ACTUATOR_MOTOR:
base_id = CAN_ID_MOTOR_DRIVER_BASE;
break;
case ACTUATOR_VALVE:
base_id = CAN_ID_VALVE_CONTROL_BASE;
break;
case ACTUATOR_CYLINDER:
base_id = CAN_ID_CYLINDER_CONTROL_BASE;
break;
default:
return 0;
}
return base_id | ((module_id & 0xFF) << 8) | (channel_id & 0xFF);
}
2. 数据采集与控制
// 传感器数据采集
void sensor_data_acquisition() {
// 温度传感器数据
for (uint8_t module = 0; module < 8; module++) {
for (uint8_t channel = 0; channel < 8; channel++) {
uint32_t sensor_id = build_sensor_id(SENSOR_TEMPERATURE, module, channel);
float temperature = read_temperature_sensor(module, channel);
// 发送温度数据
send_temperature_data(sensor_id, temperature);
}
}
// 压力传感器数据
for (uint8_t module = 0; module < 4; module++) {
for (uint8_t channel = 0; channel < 4; channel++) {
uint32_t sensor_id = build_sensor_id(SENSOR_PRESSURE, module, channel);
float pressure = read_pressure_sensor(module, channel);
// 发送压力数据
send_pressure_data(sensor_id, pressure);
}
}
// 其他传感器数据采集...
}
// 发送温度数据
void send_temperature_data(uint32_t sensor_id, float temperature) {
uint8_t tx_data[8];
// 数据转换:温度 × 100,存储为16位整数
int16_t temp_int = (int16_t)(temperature * 100.0f);
// 数据打包
tx_data[0] = (temp_int >> 8) & 0xFF;
tx_data[1] = temp_int & 0xFF;
tx_data[2] = get_sensor_status(sensor_id); // 传感器状态
tx_data[3] = get_sensor_quality(sensor_id); // 信号质量
tx_data[4] = (HAL_GetTick() >> 24) & 0xFF; // 时间戳高位
tx_data[5] = (HAL_GetTick() >> 16) & 0xFF;
tx_data[6] = (HAL_GetTick() >> 8) & 0xFF;
tx_data[7] = HAL_GetTick() & 0xFF;
// 发送扩展帧
can_send_extended_frame(&hcan1, sensor_id, tx_data, 8);
}
3. 执行器控制
// 电机控制
void motor_control(uint8_t module_id, uint8_t channel_id, Motor_Control_Command *cmd) {
uint32_t motor_id = build_actuator_id(ACTUATOR_MOTOR, module_id, channel_id);
uint8_t tx_data[8];
// 数据打包
tx_data[0] = cmd->control_mode;
tx_data[1] = cmd->direction;
tx_data[2] = (cmd->target_speed >> 8) & 0xFF;
tx_data[3] = cmd->target_speed & 0xFF;
tx_data[4] = (cmd->target_position >> 24) & 0xFF;
tx_data[5] = (cmd->target_position >> 16) & 0xFF;
tx_data[6] = (cmd->target_position >> 8) & 0xFF;
tx_data[7] = cmd->target_position & 0xFF;
// 发送控制命令
can_send_extended_frame(&hcan1, motor_id, tx_data, 8);
}
// 电机状态反馈
void receive_motor_status(CAN_RxHeaderTypeDef *header, uint8_t *data) {
// 解析电机ID
uint8_t module_id = (header->ExtId >> 8) & 0xFF;
uint8_t channel_id = header->ExtId & 0xFF;
Motor_Status status;
status.current_speed = (data[0] << 8) | data[1];
status.current_position = (data[2] << 24) | (data[3] << 16) | (data[4] << 8) | data[5];
status.current_current = (data[6] << 8) | data[7];
// 更新电机状态
update_motor_status(module_id, channel_id, &status);
}
7.3 医疗设备控制系统
项目背景:
某医疗设备公司开发高精度医疗监护和治疗设备,需要可靠的通信系统连接各种医疗传感器和执行器。
项目需求:
-
设备类型:监护仪、治疗设备、手术设备
-
数据精度:高分辨率传感器数据
-
可靠性要求:极高(关系到患者安全)
-
实时性要求:控制信号延迟 < 1ms
-
安全性要求:符合医疗设备安全标准
系统设计:
1. 安全关键型 ID 分配
// 医疗设备CAN扩展帧ID分配(安全关键型)
typedef enum {
// 生命支持设备 (0x00000000-0x0FFFFFFF) - 最高优先级
CAN_ID_ECG_MONITOR = 0x00000001,
CAN_ID_VENTILATOR = 0x00000002,
CAN_ID_DEFIBRILLATOR = 0x00000003,
CAN_ID_INFUSION_PUMP = 0x00000004,
// 治疗设备 (0x10000000-0x1FFFFFFF) - 高优先级
CAN_ID_DIALYSIS_MACHINE = 0x10000001,
CAN_ID_ANESTHESIA_MACHINE = 0x10000002,
CAN_ID_LASER_THERAPY = 0x10000003,
// 诊断设备 (0x20000000-0x2FFFFFFF) - 中优先级
CAN_ID_ULTRASOUND = 0x20000001,
CAN_ID_XRAY = 0x20000002,
CAN_ID_CT_SCANNER = 0x20000003,
// 辅助设备 (0x30000000-0x3FFFFFFF) - 低优先级
CAN_ID_HMI_DISPLAY = 0x30000001,
CAN_ID_PRINTER = 0x30000002,
CAN_ID_DATA_LOGGER = 0x30000003
} Medical_CAN_IDs;
// 构建监护设备ID(包含患者ID和设备ID)
uint32_t build_monitor_id(Medical_Device_Type device_type, uint16_t patient_id, uint8_t device_id) {
uint32_t base_id;
switch (device_type) {
case DEVICE_ECG_MONITOR:
base_id = CAN_ID_ECG_MONITOR;
break;
case DEVICE_VENTILATOR:
base_id = CAN_ID_VENTILATOR;
break;
case DEVICE_DEFIBRILLATOR:
base_id = CAN_ID_DEFIBRILLATOR;
break;
case DEVICE_INFUSION_PUMP:
base_id = CAN_ID_INFUSION_PUMP;
break;
default:
return 0;
}
// 构建包含患者ID和设备ID的扩展ID
return (base_id << 18) | ((patient_id & 0xFFFF) << 8) | (device_id & 0xFF);
}
2. 高可靠性通信实现
// 医疗设备高可靠性CAN通信
typedef struct {
uint32_t message_id;
uint8_t data[8];
uint8_t length;
uint32_t timestamp;
uint8_t priority;
uint8_t retry_count;
bool requires_ack;
} Medical_CAN_Message;
// 发送医疗设备消息(带重试和ACK机制)
HAL_StatusTypeDef medical_can_send_message(Medical_CAN_Message *msg, uint8_t max_retries) {
HAL_StatusTypeDef status;
uint8_t retry_count = 0;
while (retry_count < max_retries) {
// 发送消息
status = can_send_extended_frame(&hcan1, msg->message_id, msg->data, msg->length);
if (status != HAL_OK) {
retry_count++;
HAL_Delay(1);
continue;
}
// 如果需要ACK,等待响应
if (msg->requires_ack) {
uint32_t ack_id = msg->message_id + 1; // ACK消息ID = 原ID + 1
uint8_t ack_data[8];
uint32_t start_time = HAL_GetTick();
while (HAL_GetTick() - start_time < 10) { // 10ms超时
if (can_check_message_received(ack_id, ack_data)) {
// 收到ACK,检查确认码
if (ack_data[0] == 0xAA) {
return HAL_OK;
}
}
}
// 未收到ACK,重试
retry_count++;
} else {
// 不需要ACK,直接返回成功
return HAL_OK;
}
}
// 重试次数超限
return HAL_ERROR;
}
3. 患者监护数据传输
// ECG数据结构
typedef struct {
int16_t ecg_lead1; // ECG导联1数据
int16_t ecg_lead2; // ECG导联2数据
int16_t ecg_lead3; // ECG导联3数据
uint8_t heart_rate; // 心率 (bpm)
uint8_t rhythm_status; // 心律状态
uint8_t signal_quality; // 信号质量
uint8_t alarm_status; // 报警状态
} ECG_Data;
// 发送ECG数据
void send_ecg_data(uint16_t patient_id, uint8_t device_id, ECG_Data *ecg_data) {
uint32_t monitor_id = build_monitor_id(DEVICE_ECG_MONITOR, patient_id, device_id);
Medical_CAN_Message msg;
// 数据打包
msg.data[0] = (ecg_data->ecg_lead1 >> 8) & 0xFF;
msg.data[1] = ecg_data->ecg_lead1 & 0xFF;
msg.data[2] = (ecg_data->ecg_lead2 >> 8) & 0xFF;
msg.data[3] = ecg_data->ecg_lead2 & 0xFF;
msg.data[4] = (ecg_data->ecg_lead3 >> 8) & 0xFF;
msg.data[5] = ecg_data->ecg_lead3 & 0xFF;
msg.data[6] = ecg_data->heart_rate;
msg.data[7] = (ecg_data->rhythm_status << 4) | (ecg_data->signal_quality & 0x0F);
msg.message_id = monitor_id;
msg.length = 8;
msg.timestamp = HAL_GetTick();
msg.priority = 0; // 最高优先级
msg.retry_count = 0;
msg.requires_ack = true; // 关键医疗数据需要ACK
// 发送消息(最多3次重试)
medical_can_send_message(&msg, 3);
// 检查报警状态
if (ecg_data->alarm_status != 0) {
send_alarm_message(patient_id, device_id, ecg_data->alarm_status);
}
}
8. 常见问题与解决方案
8.1 兼容性问题
问题 1:扩展帧与标准帧混合通信问题
-
症状:支持扩展帧的节点与仅支持标准帧的节点通信异常
-
可能原因:
-
仅支持标准帧的节点无法识别扩展帧
-
扩展帧的 IDE 位设置错误
-
过滤器配置不正确
- 解决方案:
// 兼容性问题解决方案
void solve_compatibility_issues() {
// 1. 检测节点类型
detect_node_types();
// 2. 配置混合模式过滤器
configure_mixed_mode_filters();
// 3. 实现帧格式自动转换
enable_auto_frame_conversion();
// 4. 监控通信状态
monitor_communication_status();
}
// 检测节点类型
void detect_node_types() {
printf("检测CAN网络节点类型...n");
// 发送测试帧并观察响应
uint32_t test_ids[] = {0x123, 0x12345678};
for (int i = 0; i < sizeof(test_ids)/sizeof(test_ids[0]); i++) {
uint8_t test_data[8] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22};
uint32_t start_time = HAL_GetTick();
if (test_ids[i] <= 0x7FF) {
// 发送标准帧
can_send_standard_frame(&hcan1, (uint16_t)test_ids[i], test_data, 8);
} else {
// 发送扩展帧
can_send_extended_frame(&hcan1, test_ids[i], test_data, 8);
}
// 等待响应
while (HAL_GetTick() - start_time < 100) {
if (check_response_received(test_ids[i])) {
printf("节点支持ID 0x%lxn", test_ids[i]);
break;
}
}
}
}
问题 2:不同厂商 CAN 控制器的互操作性问题
-
症状:不同厂商的 CAN 控制器之间通信不稳定
-
可能原因:
-
扩展帧格式实现细节差异
-
位时间配置不匹配
-
过滤器配置方式不同
-
错误处理机制差异
- 解决方案:
// 提高不同厂商控制器的互操作性
void improve_interoperability() {
// 1. 使用标准配置参数
use_standard_configuration();
// 2. 增加兼容性测试
run_compatibility_tests();
// 3. 实现自适应通信策略
implement_adaptive_communication();
// 4. 详细的错误日志记录
enable_detailed_error_logging();
}
// 使用标准配置参数
void use_standard_configuration() {
// 标准位时间配置
hcan1.Init.Prescaler = 4;
hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
hcan1.Init.TimeSeg1 = CAN_BS1_6TQ;
hcan1.Init.TimeSeg2 = CAN_BS2_1TQ;
// 标准过滤器配置
CAN_FilterTypeDef filter;
filter.FilterBank = 0;
filter.FilterMode = CAN_FILTERMODE_IDMASK;
filter.FilterScale = CAN_FILTERSCALE_32BIT;
filter.FilterIdHigh = 0x0000;
filter.FilterIdLow = 0x0000;
filter.FilterMaskIdHigh = 0x0000;
filter.FilterMaskIdLow = 0x0000;
filter.FilterFIFOAssignment = CAN_RX_FIFO0;
filter.FilterActivation = ENABLE;
HAL_CAN_ConfigFilter(&hcan1, &filter);
printf("已应用标准配置参数n");
}
8.2 性能问题
问题 1:扩展帧通信速率低
-
症状:使用扩展帧时通信速率明显低于标准帧
-
可能原因:
-
扩展帧长度更长,传输时间增加
-
仲裁时间延长,影响总线利用率
-
系统处理扩展帧的开销更大
-
过滤器配置不当
- 解决方案:
// 优化扩展帧通信性能
void optimize_extended_frame_performance() {
printf("优化CAN扩展帧通信性能...n");
// 1. 分析性能瓶颈
analyze_performance_bottlenecks();
// 2. 优化帧结构
optimize_frame_structure();
// 3. 改进过滤器配置
improve_filter_configuration();
// 4. 优化软件处理流程
optimize_software_processing();
// 5. 验证优化效果
verify_performance_improvement();
}
// 分析性能瓶颈
void analyze_performance_bottlenecks() {
// 测量不同帧类型的传输时间
uint8_t test_data[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
uint32_t standard_id = 0x123;
uint32_t extended_id = 0x12345678;
// 测量标准帧传输时间
uint32_t start_time = HAL_GetTick();
for (int i = 0; i < 1000; i++) {
can_send_standard_frame(&hcan1, (uint16_t)standard_id, test_data, 8);
while (HAL_CAN_GetTxMailboxesFreeLevel(&hcan1) < 3);
}
uint32_t standard_time = HAL_GetTick() - start_time;
// 测量扩展帧传输时间
start_time = HAL_GetTick();
for (int i = 0; i < 1000; i++) {
can_send_extended_frame(&hcan1, extended_id, test_data, 8);
while (HAL_CAN_GetTxMailboxesFreeLevel(&hcan1) < 3);
}
uint32_t extended_time = HAL_GetTick() - start_time;
printf("性能分析结果:n");
printf(" 标准帧传输时间 (1000帧): %lu msn", standard_time);
printf(" 扩展帧传输时间 (1000帧): %lu msn", extended_time);
printf(" 性能差异: %.1f%%n", (float)(extended_time - standard_time) / standard_time * 100);
}
问题 2:系统响应时间长
-
症状:扩展帧消息的处理延迟超过预期
-
可能原因:
-
扩展帧的 ID 处理更复杂
-
软件处理逻辑效率低
-
中断处理时间过长
-
系统资源不足
- 解决方案:
// 减少扩展帧处理延迟
void reduce_extended_frame_latency() {
printf("减少CAN扩展帧处理延迟...n");
// 1. 优化中断处理
optimize_interrupt_handling();
// 2. 改进消息处理算法
improve_message_processing();
// 3. 使用DMA传输
enable_dma_transfer();
// 4. 优先级调度优化
optimize_priority_scheduling();
}
// 优化中断处理
void optimize_interrupt_handling() {
// 1. 减少中断服务程序的执行时间
// 2. 使用消息队列进行异步处理
// 3. 优化中断优先级设置
// 配置CAN中断优先级
HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 0, 0); // 最高优先级
HAL_NVIC_SetPriority(CAN1_RX1_IRQn, 1, 0);
HAL_NVIC_SetPriority(CAN1_SCE_IRQn, 2, 0);
HAL_NVIC_SetPriority(CAN1_TX_IRQn, 3, 0);
printf("已优化CAN中断处理n");
}
8.3 可靠性问题
问题 1:扩展帧传输错误率高
-
症状:扩展帧的传输错误率明显高于标准帧
-
可能原因:
-
扩展帧更长,更容易受到干扰
-
信号质量不佳
-
位时间配置不精确
-
硬件设计问题
- 解决方案:
// 降低扩展帧传输错误率
void reduce_extended_frame_errors() {
printf("降低CAN扩展帧传输错误率...n");
// 1. 检查硬件连接
check_hardware_connections();
// 2. 优化信号质量
optimize_signal_quality();
// 3. 调整位时间配置
adjust_bit_time_configuration();
// 4. 增加错误恢复机制
enhance_error_recovery();
}
// 优化信号质量
void optimize_signal_quality() {
// 1. 检查终端电阻
check_termination_resistors();
// 2. 验证总线阻抗匹配
verify_impedance_matching();
// 3. 优化PCB布局
optimize_pcb_layout();
// 4. 增强屏蔽措施
enhance_shielding();
printf("已优化CAN总线信号质量n");
}
问题 2:系统稳定性差
-
症状:使用扩展帧时系统经常出现不稳定现象
-
可能原因:
-
内存管理问题
-
并发访问冲突
-
错误处理不完善
-
资源泄漏
- 解决方案:
// 提高系统稳定性
void improve_system_stability() {
printf("提高CAN扩展帧系统稳定性...n");
// 1. 完善错误处理机制
完善_error_handling();
// 2. 增加系统监控
add_system_monitoring();
// 3. 实现看门狗机制
implement_watchdog();
// 4. 内存管理优化
optimize_memory_management();
}
// 完善错误处理机制
void 完善_error_handling() {
// 1. 详细的错误日志
enable_detailed_error_logging();
// 2. 自动错误恢复
implement_auto_recovery();
// 3. 故障安全机制
add_failsafe_mechanism();
printf("已完善错误处理机制n");
}
8.4 开发调试问题
问题 1:扩展帧调试困难
-
症状:扩展帧的调试比标准帧更加困难
-
可能原因:
-
扩展帧格式更复杂
-
调试工具支持不完善
-
ID 空间太大,难以跟踪
-
过滤器配置复杂
- 解决方案:
// 简化扩展帧调试
void simplify_extended_frame_debugging() {
printf("简化CAN扩展帧调试...n");
// 1. 开发调试工具
develop_debug_tools();
// 2. 实现消息记录功能
implement_message_logging();
// 3. 添加调试接口
add_debug_interface();
// 4. 可视化工具集成
integrate_visualization_tools();
}
// 实现消息记录功能
void implement_message_logging() {
// 消息记录结构体
typedef struct {
uint32_t timestamp;
uint32_t message_id;
uint8_t data[8];
uint8_t length;
uint8_t frame_type; // 0: 标准帧, 1: 扩展帧
} CAN_Message_Log;
// 消息日志缓冲区
CAN_Message_Log message_log[1000];
uint32_t log_index = 0;
// 记录消息函数
void log_can_message(CAN_RxHeaderTypeDef *header, uint8_t *data) {
CAN_Message_Log *log_entry = &message_log[log_index++ % 1000];
log_entry->timestamp = HAL_GetTick();
log_entry->message_id = (header->IDE == CAN_ID_EXT) ?
((header->ExtId << 11) | header->StdId) : header->StdId;
memcpy(log_entry->data, data, header->DLC);
log_entry->length = header->DLC;
log_entry->frame_type = (header->IDE == CAN_ID_EXT) ? 1 : 0;
}
// 导出日志函数
void export_can_log() {
// 实现日志导出到文件或上位机
printf("导出CAN消息日志...n");
}
printf("已实现CAN消息记录功能n");
}
问题 2:过滤器配置复杂
-
症状:扩展帧的过滤器配置比标准帧复杂得多
-
可能原因:
-
32 位过滤器配置更复杂
-
ID 空间大,过滤规则复杂
-
不同控制器的过滤器实现不同
-
调试过滤器配置困难
- 解决方案:
// 简化过滤器配置
void simplify_filter_configuration() {
printf("简化CAN扩展帧过滤器配置...n");
// 1. 开发过滤器配置工具
develop_filter_config_tool();
// 2. 提供配置模板
provide_config_templates();
// 3. 实现配置验证
implement_config_validation();
// 4. 可视化配置界面
create_visual_config_interface();
}
// 开发过滤器配置工具
void develop_filter_config_tool() {
// 过滤器配置结构体
typedef struct {
uint8_t filter_bank;
uint8_t filter_mode;
uint8_t filter_scale;
uint32_t filter_id;
uint32_t filter_mask;
uint8_t fifo_assignment;
bool filter_activation;
} Filter_Config;
// 过滤器配置函数
HAL_StatusTypeDef configure_can_filter(Filter_Config *config) {
CAN_FilterTypeDef filter;
filter.FilterBank = config->filter_bank;
filter.FilterMode = config->filter_mode;
filter.FilterScale = config->filter_scale;
filter.FilterIdHigh = (config->filter_id >> 16) & 0xFFFF;
filter.FilterIdLow = config->filter_id & 0xFFFF;
filter.FilterMaskIdHigh = (config->filter_mask >> 16) & 0xFFFF;
filter.FilterMaskIdLow = config->filter_mask & 0xFFFF;
filter.FilterFIFOAssignment = config->fifo_assignment;
filter.FilterActivation = config->filter_activation ? ENABLE : DISABLE;
filter.SlaveStartFilterBank = 14;
return HAL_CAN_ConfigFilter(&hcan1, &filter);
}
// 快速配置函数
HAL_StatusTypeDef quick_filter_config(uint32_t start_id, uint32_t end_id) {
Filter_Config config;
config.filter_bank = 0;
config.filter_mode = CAN_FILTERMODE_IDMASK;
config.filter_scale = CAN_FILTERSCALE_32BIT;
config.filter_id = start_id;
config.filter_mask = calculate_filter_mask(start_id, end_id);
config.fifo_assignment = CAN_RX_FIFO0;
config.filter_activation = true;
return configure_can_filter(&config);
}
printf("已开发过滤器配置工具n");
}

33

被折叠的 条评论
为什么被折叠?



