下面我將提供一個完整的STM32F7 CAN總線通信實現方案,包含中斷驅動的收發隊列管理。
1. CAN總線配置與隊列定義
can_bus.h
#ifndef __CAN_BUS_H
#define __CAN_BUS_H#include "stm32f7xx_hal.h"
#include "queue.h"// CAN消息結構體
typedef struct {uint32_t id; // 標準ID或擴展IDuint8_t data[8]; // 數據域uint8_t len; // 數據長度uint8_t format; // 0-標準幀, 1-擴展幀uint8_t type; // 0-數據幀, 1-遠程幀
} CAN_Msg_t;// CAN隊列配置
#define CAN_TX_QUEUE_SIZE 16
#define CAN_RX_QUEUE_SIZE 32// CAN總線初始化
void CAN_Init(CAN_HandleTypeDef *hcan);// 消息發送/接收接口
bool CAN_SendMsg(CAN_HandleTypeDef *hcan, CAN_Msg_t *msg);
bool CAN_GetMsg(CAN_Msg_t *msg);// 中斷處理函數
void CAN_IRQHandler(CAN_HandleTypeDef *hcan);#endif
can_bus.c
#include "can_bus.h"// CAN發送接收隊列
static Queue_t can_tx_queue;
static Queue_t can_rx_queue;// CAN初始化
void CAN_Init(CAN_HandleTypeDef *hcan) {// 初始化隊列Queue_Init(&can_tx_queue);Queue_Init(&can_rx_queue);// CAN過濾器配置 (示例配置)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;filter.SlaveStartFilterBank = 14;HAL_CAN_ConfigFilter(hcan, &filter);// 啟動CANHAL_CAN_Start(hcan);// 激活CAN中斷HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING | CAN_IT_TX_MAILBOX_EMPTY);
}// 發送CAN消息 (非阻塞)
bool CAN_SendMsg(CAN_HandleTypeDef *hcan, CAN_Msg_t *msg) {// 嘗試直接發送if (HAL_CAN_GetTxMailboxesFreeLevel(hcan) > 0) {CAN_TxHeaderTypeDef tx_header;tx_header.StdId = msg->format ? 0 : msg->id;tx_header.ExtId = msg->format ? msg->id : 0;tx_header.IDE = msg->format ? CAN_ID_EXT : CAN_ID_STD;tx_header.RTR = msg->type ? CAN_RTR_REMOTE : CAN_RTR_DATA;tx_header.DLC = msg->len;tx_header.TransmitGlobalTime = DISABLE;uint32_t mailbox;if (HAL_CAN_AddTxMessage(hcan, &tx_header, msg->data, &mailbox) == HAL_OK) {return true;}}// 發送郵箱已滿,放入隊列return Queue_SendToBackFromISR(&can_tx_queue, msg);
}// 接收CAN消息 (非阻塞)
bool CAN_GetMsg(CAN_Msg_t *msg) {return Queue_Receive(&can_rx_queue, msg);
}// CAN中斷處理
void CAN_IRQHandler(CAN_HandleTypeDef *hcan) {// RX FIFO0中斷處理if (__HAL_CAN_GET_FLAG(hcan, CAN_FLAG_FOV0) || __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_FF0)) {__HAL_CAN_CLEAR_FLAG(hcan, CAN_FLAG_FOV0 | CAN_FLAG_FF0);}if (__HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_RX_FIFO0_MSG_PENDING)) {CAN_RxHeaderTypeDef rx_header;CAN_Msg_t msg;if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rx_header, msg.data) == HAL_OK) {msg.id = rx_header.IDE == CAN_ID_STD ? rx_header.StdId : rx_header.ExtId;msg.len = rx_header.DLC;msg.format = rx_header.IDE == CAN_ID_EXT;msg.type = rx_header.RTR == CAN_RTR_REMOTE;Queue_SendToBackFromISR(&can_rx_queue, &msg);}}// TX郵箱空中斷處理if (__HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_TX_MAILBOX_EMPTY)) {// 檢查并發送隊列中的消息while (HAL_CAN_GetTxMailboxesFreeLevel(hcan) > 0) {CAN_Msg_t msg;if (Queue_Receive(&can_tx_queue, &msg)) {CAN_SendMsg(hcan, &msg); // 遞歸調用,但不會無限遞歸} else {break;}}}
}
2. 主程序集成示例
main.c
#include "stm32f7xx_hal.h"
#include "can_bus.h"CAN_HandleTypeDef hcan1;int main(void) {HAL_Init();SystemClock_Config();// CAN初始化hcan1.Instance = CAN1;hcan1.Init.Prescaler = 6;hcan1.Init.Mode = CAN_MODE_NORMAL;hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;hcan1.Init.TimeSeg1 = CAN_BS1_13TQ;hcan1.Init.TimeSeg2 = CAN_BS2_2TQ;hcan1.Init.TimeTriggeredMode = DISABLE;hcan1.Init.AutoBusOff = DISABLE;hcan1.Init.AutoWakeUp = DISABLE;hcan1.Init.AutoRetransmission = ENABLE;hcan1.Init.ReceiveFifoLocked = DISABLE;hcan1.Init.TransmitFifoPriority = DISABLE;HAL_CAN_Init(&hcan1);CAN_Init(&hcan1);while (1) {// 接收處理CAN_Msg_t rx_msg;if (CAN_GetMsg(&rx_msg)) {// 處理接收到的CAN消息// ...}// 示例:周期發送測試消息static uint32_t last_tick = 0;if (HAL_GetTick() - last_tick > 1000) {last_tick = HAL_GetTick();CAN_Msg_t tx_msg;tx_msg.id = 0x123;tx_msg.len = 8;tx_msg.format = 0; // 標準幀tx_msg.type = 0; // 數據幀for (int i = 0; i < 8; i++) {tx_msg.data[i] = i;}CAN_SendMsg(&hcan1, &tx_msg);}// 其他任務...}
}// CAN中斷服務函數
void CAN1_RX0_IRQHandler(void) {CAN_IRQHandler(&hcan1);
}void CAN1_TX_IRQHandler(void) {CAN_IRQHandler(&hcan1);
}
3. 關鍵實現細節說明
雙隊列設計:
發送隊列(can_tx_queue):緩存待發送消息
接收隊列(can_rx_queue):緩存接收到的消息
中斷驅動機制:
接收中斷:當FIFO收到消息時觸發,將消息存入接收隊列
發送中斷:當發送郵箱空閑時觸發,從發送隊列取出消息發送
流量控制:
當CAN控制器發送郵箱滿時,消息暫存到發送隊列
當接收隊列滿時,新消息將被丟棄(可根據需求修改)
性能優化:
直接使用HAL庫提供的郵箱狀態檢查
在中斷中只做必要的最小操作
擴展性考慮:
支持標準幀和擴展幀
支持數據幀和遠程幀
可輕松擴展為多CAN總線支持
4. 實際應用建議
- 錯誤處理增強:
// 在CAN_Init中添加錯誤中斷使能
HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING |CAN_IT_TX_MAILBOX_EMPTY |CAN_IT_ERROR |CAN_IT_BUSOFF |CAN_IT_LAST_ERROR_CODE);
- 診斷功能:
// 添加CAN狀態監控
typedef struct {uint32_t tx_count;uint32_t rx_count;uint32_t tx_queue_full;uint32_t rx_queue_full;uint32_t errors;
} CAN_Stats_t;// 在中斷處理中更新統計信息
- 多幀傳輸支持:
// 對于長消息,可以實現分段/重組邏輯
#define CAN_MTU 8 // CAN最大傳輸單元typedef struct {uint32_t id;uint16_t total_len;uint16_t offset;uint8_t data[64]; // 假設最大支持64字節數據
} CAN_LongMsg_t;
這個實現提供了完整的CAN總線通信框架,您可以根據具體應用需求進行調整和擴展。