基于STM32的CAN总线双向通信例程
一、硬件连接方案
STM32A (主设备) STM32B (从设备)
--------------------- ---------------------
CAN1_TX (PA12) →→→ CAN1_RX (PA11)
CAN1_RX (PA11) ←←← CAN1_TX (PA12)
GND -------------- GND
总线两端各接120Ω终端电阻
二、CubeMX配置
-
时钟配置
-
HSE: 8MHz外部晶振
-
PLL: 42MHz系统时钟
-
APB1: 21MHz (CAN时钟源)
-
-
CAN参数配置
// CAN1配置 hcan1.Instance = CAN1; hcan1.Init.Prescaler = 6; // 分频系数 hcan1.Init.Mode = CAN_MODE_NORMAL; // 正常模式 hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ; hcan1.Init.TimeSeg1 = CAN_BS1_8TQ; // 8时间单元 hcan1.Init.TimeSeg2 = CAN_BS2_3TQ; // 3时间单元 hcan1.Init.TimeTriggeredMode = DISABLE; hcan1.Init.AutoBusOff = DISABLE; hcan1.Init.AutoWakeUp = DISABLE; hcan1.Init.AutoRetransmission = ENABLE; // 自动重传 -
过滤器配置
// 接收所有标准帧 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; HAL_CAN_ConfigFilter(&hcan1, &filter);
三、核心代码实现
1. CAN初始化
void MX_CAN1_Init(void)
{
CAN_HandleTypeDef hcan1;
hcan1.Instance = CAN1;
HAL_CAN_Init(&hcan1);
// 启动CAN并激活接收中断
HAL_CAN_Start(&hcan1);
HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);
}
2. 数据发送函数
void CAN_Send_Cmd(uint8_t cmd, uint16_t value)
{
CAN_TxHeaderTypeDef TxHeader;
uint8_t TxData[8] = {0};
TxHeader.StdId = 0x100 + cmd; // 命令ID范围0x100-0x1FF
TxHeader.IDE = CAN_ID_STD;
TxHeader.RTR = CAN_RTR_DATA;
TxHeader.DLC = 2;
TxData[0] = (value >> 8) & 0xFF;
TxData[1] = value & 0xFF;
HAL_CAN_AddTxMessage(&hcan1, &TxHeader, TxData, NULL);
}
3. 接收中断处理
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
CAN_RxHeaderTypeDef RxHeader;
uint8_t RxData[8] = {0};
HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader, RxData);
switch(RxHeader.StdId)
{
case 0x101: // 电机控制指令
Motor_SetSpeed(RxData[0], RxData[1]);
break;
case 0x102: // 传感器数据请求
Sensor_SendData();
break;
}
}
四、通信协议设计
| 字段 | 说明 | 长度 |
|---|---|---|
| Start Byte | 固定为0xAA | 1 Byte |
| Command ID | 功能标识(如0x01=启动电机) | 1 Byte |
| Data Length | 数据长度(1-8字节) | 1 Byte |
| Payload | 实际数据 | 1-8 Byte |
| CRC | CRC-8校验 | 1 Byte |
示例数据帧:
0xAA 0x01 0x02 0x10 0x27 0x00 0x00 0x00 0x3C
五、多节点通信实现
// 节点地址映射表
const uint8_t node_addr[3] = {0x01, 0x02, 0x03};
// 广播消息发送
void CAN_Broadcast(uint8_t cmd, uint8_t* data, uint8_t len)
{
CAN_TxHeaderTypeDef TxHeader;
TxHeader.StdId = 0x00; // 广播ID
TxHeader.IDE = CAN_ID_STD;
TxHeader.RTR = CAN_RTR_DATA;
TxHeader.DLC = len + 2; // 包含cmd和len
TxData[0] = cmd;
TxData[1] = len;
memcpy(&TxData[2], data, len);
HAL_CAN_AddTxMessage(&hcan1, &TxHeader, TxData, NULL);
}
// 定向消息发送
void CAN_Direct_Send(uint8_t target_addr, uint8_t cmd, uint8_t* data, uint8_t len)
{
CAN_TxHeaderTypeDef TxHeader;
TxHeader.StdId = (target_addr << 3) | 0x04; // 地址左移3位 + 类型标识
TxHeader.IDE = CAN_ID_STD;
TxHeader.RTR = CAN_RTR_DATA;
TxHeader.DLC = len + 2;
TxData[0] = cmd;
TxData[1] = len;
memcpy(&TxData[2], data, len);
HAL_CAN_AddTxMessage(&hcan1, &TxHeader, TxData, NULL);
}
六、错误处理机制
void CAN_Error_Callback(void)
{
uint32_t error = HAL_CAN_GetError(&hcan1);
if(error & HAL_CAN_ERROR_BOF) {
printf("总线关闭,尝试恢复...\r\n");
HAL_CAN_ResetError(&hcan1);
HAL_CAN_Start(&hcan1);
}
if(error & HAL_CAN_ERROR_PASSIVE) {
printf("进入错误被动状态!\r\n");
HAL_Delay(1000);
HAL_CAN_Stop(&hcan1);
MX_CAN1_Init(); // 重新初始化
}
}
参考代码 基于STM32的CAN与CAN之间的通信例程 www.youwenfan.com/contentcnr/101724.html
七、测试流程
-
回环测试
- 将CAN_TX/RX短接,发送测试数据验证硬件
while(1) { CAN_Send_Cmd(0x01, 0xABCD); HAL_Delay(1000); } -
双机通信测试
-
主机发送指令:
0xAA 0x10 0x02 0x34 0x12 -
从机应答:
0xAA 0x20 0x02 0x56 0x78
-
-
总线负载测试
- 同时发送10个节点数据,监控总线利用率
for(int i=0; i<10; i++) { CAN_Direct_Send(i+1, 0x55, test_data, 4); HAL_Delay(50); }
通过本方案可实现多节点CAN总线通信系统,支持标准帧/扩展帧、广播通信、错误恢复等完整功能。实际应用中建议根据具体需求调整过滤器配置和协议设计。

浙公网安备 33010602011771号