【双 MCU 架构车载仪表终端(CAN 互联)】STM32H7 <-> STM32F1 双板 CAN 总线收发
1. 引言
进行完两个板子各自的回环收发测试的时候,证明两个板子的 CAN 收发大体没有什么问题,这时就可以进行双板的收发了。不过有些事项还是需要小小注意一下的,下文会提到。
2. 硬件连接
┌───────────────────────────┐ ┌───────────────────────────┐
│ STM32F103C8T6 (节点1) │ │ STM32H757XIH6 (节点2) │
│ CAN1 / bxCAN, 1Mbit/s │ │ FDCAN1, Classic, 1Mbit/s│
│ │ │ │
│ PA12 ───────── CAN_TX │ │ CAN_TX ───────── PB9 │
│ PA11 ───────── CAN_RX │ │ CAN_RX ───────── PB8 │
└───────┬───────────────┬───┘ └───┬───────────────┬───────┘
│ TXD │ RXD │ RXD │ TXD
▼ ▲ ▲ ▼
┌───────┴───────────────┴───┐ ┌───┴───────────────┴───────┐
│ CAN 收发器 │ │ CAN 收发器 #2 │
└───────┬───────────────┬───┘ └───┬───────────────┬───────┘
│ CAN_H │ CAN_L │ CAN_L │ CAN_H
────────┴───────────────┴────────┬───────────────┴───────────────┴─────────
│
(双绞线 CAN_H / CAN_L)
3. STM32H7 侧
3.1 CubeMX 配置
FDCAN 配置:

中断配置:

3.2 代码部分
3.2.1 过滤、中断、FDCAN手动使能
/* USER CODE BEGIN FDCAN1_Init 2 */
/* 全局过滤器:非匹配标准/扩展帧收进 RX FIFO0,远程帧拒收 */
if (HAL_FDCAN_ConfigGlobalFilter(&hfdcan1,
FDCAN_ACCEPT_IN_RX_FIFO0,
FDCAN_ACCEPT_IN_RX_FIFO0,
FDCAN_REJECT,
FDCAN_REJECT) != HAL_OK)
{
Error_Handler();
}
/* 使能中断通知 */
if (HAL_FDCAN_ActivateNotification(&hfdcan1,
FDCAN_IT_RX_FIFO0_NEW_MESSAGE | // 接收新报文事件
FDCAN_IT_TX_COMPLETE | // 发送完成事件
FDCAN_IT_TX_ABORT_COMPLETE, // 发送取消完成事件
FDCAN_TX_BUFFER0) != HAL_OK)
{
Error_Handler();
}
/* 启动 CAN */
if (HAL_FDCAN_Start(&hfdcan1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE END FDCAN1_Init 2 */
3.2.2 app_fdcan.c
#include "app_fdcan.h"
#include "mylog.h"
#include "fdcan.h"
#include "FreeRTOS.h"
#include "task.h"
#include "cmsis_os2.h"
#include <string.h>
#define FDCAN_RX_BUF_SIZE 16 // 软件接收缓冲区深度(帧),16 对单帧间隔收发足够
typedef struct
{
uint32_t ID; // 报文 ID(标准或扩展)
uint8_t Length; // 数据长度
uint8_t Data[8]; // 数据域
} CAN_FrameTypeDef;
static CAN_FrameTypeDef FDCAN_RxBuf[FDCAN_RX_BUF_SIZE]; // 环形缓冲区
static volatile uint8_t FDCAN_RxHead = 0; // 写指针,仅中断回调修改
static volatile uint8_t FDCAN_RxTail = 0; // 读指针,仅主循环修改
static volatile uint32_t FDCAN_TxCompleteBuffers = 0; // 传输缓冲区完成标志位
static volatile uint32_t FDCAN_TxAbortCompleteBuffers = 0; // 传输缓冲区中止完成标志位
static volatile uint32_t FDCAN_RxFifoFull = 0; // FIFO 满标志位
static volatile uint32_t FDCAN_ErrorCode = 0; // 错误码
static volatile uint32_t FDCAN_ErrorStatus = 0; // 错误状态改变标志
static void FDCAN_Transmit(uint32_t ID, uint8_t Length, uint8_t *TxData)
{
FDCAN_TxHeaderTypeDef TxMassage;
TxMassage.BitRateSwitch = FDCAN_BRS_OFF;
TxMassage.DataLength = Length;
TxMassage.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
TxMassage.FDFormat = FDCAN_CLASSIC_CAN;
TxMassage.Identifier = ID;
TxMassage.IdType = FDCAN_STANDARD_ID;
TxMassage.MessageMarker = 0xAA;
TxMassage.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
TxMassage.TxFrameType = FDCAN_DATA_FRAME;
/* 将消息添加到专用Tx缓冲区 */
if (HAL_FDCAN_AddMessageToTxBuffer(&hfdcan1, &TxMassage, TxData, FDCAN_TX_BUFFER0) != HAL_OK)
{
/* 发送失败:多半是 Tx FIFO 满(8 元素),直接返回 */
LOG_ERROR("FDCAN", "AddMessageToTxBuffer failed");
return;
}
LOG_INFO("FDCAN", "AddMessageToTxBuffer success");
/* 启用传输请求 */
if (HAL_FDCAN_EnableTxBufferRequest(&hfdcan1, FDCAN_TX_BUFFER0) != HAL_OK)
{
LOG_ERROR("FDCAN", "EnableTxBufferRequest failed");
return;
}
LOG_INFO("FDCAN", "EnableTxBufferRequest success");
}
/**
* @brief 检查软件接收缓冲区是否为空
* @return uint8_t 1: 为空, 0: 不为空
*/
static uint8_t FDCAN_FIFO_isEmpty(void)
{
if (FDCAN_RxHead == FDCAN_RxTail) // 软件缓冲区为空
{
LOG_WARN("FDCAN", "FIFO is empty");
return 1;
}
LOG_INFO("FDCAN", "FIFO is not empty");
return 0;
}
/**
* @brief 接收 CAN 消息
* @param ID 标准 ID 号指针
* @param Length 数据长度指针
* @param RxData 数据指针
*/
static void FDCAN_Receive(uint32_t *ID, uint8_t *Length, uint8_t *RxData)
{
CAN_FrameTypeDef *frame = &FDCAN_RxBuf[FDCAN_RxTail];
*ID = frame->ID;
*Length = frame->Length;
memcpy(RxData, frame->Data, frame->Length);
FDCAN_RxTail = (FDCAN_RxTail + 1) % FDCAN_RX_BUF_SIZE;
}
static void FDCAN_ProcessEvents(void)
{
uint32_t completeBuffers = FDCAN_TxCompleteBuffers; // 复制传输缓冲区完成标志位
uint32_t abortCompleteBuffers = FDCAN_TxAbortCompleteBuffers; // 复制传输缓冲区中止完成标志位
uint32_t rxFifoFull = FDCAN_RxFifoFull; // 复制接收 FIFO 满标志位
uint32_t errorCode = FDCAN_ErrorCode; // 复制错误码
uint32_t errorStatus = FDCAN_ErrorStatus; // 复制错误状态改变标志位
FDCAN_TxCompleteBuffers = 0; // 清空传输缓冲区完成标志位
FDCAN_TxAbortCompleteBuffers = 0; // 清空传输缓冲区中止完成标志位
FDCAN_RxFifoFull = 0; // 清空接收 FIFO 满标志位
FDCAN_ErrorCode = 0; // 清空错误码
FDCAN_ErrorStatus = 0; // 清空错误状态改变标志位
/* 接收 FIFO 满标志位 */
if ((rxFifoFull & FDCAN_IT_RX_FIFO0_FULL) != 0U) // FIFO0 满
{
LOG_WARN("FDCAN", "CallBack: CAN RxFIFO0 Full");
}
/* 处理传输缓冲区完成标志位 */
if ((completeBuffers & FDCAN_TX_BUFFER0) != 0U) // 传输缓冲区 0 完成
{
LOG_INFO("FDCAN", "CallBack: Tx Buffer 0 Complete");
}
if ((completeBuffers & FDCAN_TX_BUFFER1) != 0U) // 传输缓冲区 1 完成
{
LOG_INFO("FDCAN", "CallBack: Tx Buffer 1 Complete");
}
if ((completeBuffers & FDCAN_TX_BUFFER2) != 0U) // 传输缓冲区 2 完成
{
LOG_INFO("FDCAN", "CallBack: Tx Buffer 2 Complete");
}
/* 处理传输缓冲区中止完成标志位 */
if ((abortCompleteBuffers & FDCAN_TX_BUFFER0) != 0U) // 传输缓冲区 0 中止完成
{
LOG_WARN("FDCAN", "CallBack: Tx Buffer 0 Abort");
}
if ((abortCompleteBuffers & FDCAN_TX_BUFFER1) != 0U) // 传输缓冲区 1 中止完成
{
LOG_WARN("FDCAN", "CallBack: Tx Buffer 1 Abort");
}
if ((abortCompleteBuffers & FDCAN_TX_BUFFER2) != 0U) // 传输缓冲区 2 中止完成
{
LOG_WARN("FDCAN", "CallBack: Tx Buffer 2 Abort");
}
/* 处理错误码 */
if (errorCode != FDCAN_PROTOCOL_ERROR_NONE) // 错误码不为无错误
{
LOG_ERROR("FDCAN", "CallBack: Error: 0x%08lX", errorCode);
}
/* 处理错误状态改变标志位 */
if (errorStatus != FDCAN_PROTOCOL_ERROR_NONE) // 错误状态改变
{
LOG_ERROR("FDCAN", "CallBack: Error Status: 0x%08lX", errorStatus);
}
}
/**
* @brief CAN 任务线程
* @param pvParameters 任务参数指针,本例中未使用
*/
static void FDCAN_Thread(void *pvParameters)
{
for (;;)
{
osDelay(1000);
FDCAN_Transmit(0x123, 8, (uint8_t *)"Hello!");
if (!FDCAN_FIFO_isEmpty())
{
uint32_t ID = 0;
uint8_t Length = 0;
uint8_t RxData[8] = {0};
FDCAN_Receive(&ID, &Length, RxData);
LOG_INFO("FDCAN", "ID: %d, Length: %d, Data: %02X %02X %02X %02X %02X %02X %02X %02X",
ID, Length,
RxData[0],
RxData[1],
RxData[2],
RxData[3],
RxData[4],
RxData[5],
RxData[6],
RxData[7]);
}
FDCAN_ProcessEvents();
}
}
/**
* @brief 启动 CAN 任务线程
*/
void FDCAN_Thread_Start(void)
{
BaseType_t xReturn = pdPASS;
xReturn = xTaskCreate((TaskFunction_t)FDCAN_Thread,
(char *)"FDCAN_Thread",
(uint16_t)1024,
(void *)NULL,
(uint8_t)5,
(TaskHandle_t *)NULL);
if (xReturn != pdPASS)
{
LOG_ERROR("FDCAN", "task create failed");
}
}
/**
* @brief FIFO0 收到报文的中断回调
*/
void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
{
FDCAN_RxHeaderTypeDef RxMessage;
CAN_FrameTypeDef frame;
if (hfdcan->Instance == FDCAN1)
{
switch (RxFifo0ITs)
{
case FDCAN_IT_RX_FIFO0_MESSAGE_LOST: // 报文丢失事件
break;
case FDCAN_IT_RX_FIFO0_FULL: // FIFO 满事件
break;
case FDCAN_IT_RX_FIFO0_WATERMARK: // 水位事件
break;
case FDCAN_IT_RX_FIFO0_NEW_MESSAGE: // 新报文事件
/* 从 Rx FIFO 0 读取消息 */
if (HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &RxMessage, frame.Data) == HAL_OK)
{
frame.ID = RxMessage.Identifier; // ID 已是纯数值,不用再按 IDE 挑 StdId/ExtId
frame.Length = RxMessage.DataLength; // 经典帧 0~8 字节时 DLC 码即字节数
uint8_t next = (FDCAN_RxHead + 1) % FDCAN_RX_BUF_SIZE;
if (next != FDCAN_RxTail) // 缓冲区未满才入队,满了丢弃 newest
{
FDCAN_RxBuf[FDCAN_RxHead] = frame;
FDCAN_RxHead = next;
}
}
break;
default:
break;
}
}
}
/**
* @brief 传输缓冲区完成中断回调
* @param hfdcan CAN 处理结构体指针
* @param BufferIndexes 传输缓冲区索引
*/
void HAL_FDCAN_TxBufferCompleteCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t BufferIndexes)
{
if (hfdcan->Instance == FDCAN1)
{
switch (BufferIndexes)
{
case FDCAN_TX_BUFFER0:
FDCAN_TxCompleteBuffers = FDCAN_TX_BUFFER0;
break;
default:
break;
}
}
}
/**
* @brief 传输缓冲区中止中断回调
* @param hfdcan CAN 处理结构体指针
* @param BufferIndexes 传输缓冲区索引
*/
void HAL_FDCAN_TxBufferAbortCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t BufferIndexes)
{
if (hfdcan->Instance == FDCAN1)
{
switch (BufferIndexes)
{
case FDCAN_TX_BUFFER0:
FDCAN_TxAbortCompleteBuffers = FDCAN_TX_BUFFER0;
break;
default:
break;
}
}
}
/**
* @brief 错误中断回调
* @param hfdcan CAN 处理结构体指针
*/
void HAL_FDCAN_ErrorCallback(FDCAN_HandleTypeDef *hfdcan)
{
if (hfdcan->Instance == FDCAN1)
{
FDCAN_ErrorCode = HAL_FDCAN_GetError(hfdcan);
hfdcan->ErrorCode = HAL_FDCAN_ERROR_NONE; // 读后立即清除,同一错误只经中断上报一次
}
}
/**
* @brief 错误状态中断回调
* @param hfdcan CAN 处理结构体指针
* @param ErrorStatusITs 错误状态中断标志位
*/
void HAL_FDCAN_ErrorStatusCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t ErrorStatusITs)
{
if (hfdcan->Instance == FDCAN1)
{
switch (ErrorStatusITs)
{
case FDCAN_IT_ERROR_PASSIVE:
FDCAN_ErrorStatus = FDCAN_IT_ERROR_PASSIVE;
break;
case FDCAN_IT_ERROR_WARNING:
FDCAN_ErrorStatus = FDCAN_IT_ERROR_WARNING;
break;
case FDCAN_IT_BUS_OFF:
FDCAN_ErrorStatus = FDCAN_IT_BUS_OFF;
break;
default:
break;
}
}
}
3.2.4 app_fdcan.h
#ifndef __APP_FDCAN_H__
#define __APP_FDCAN_H__
#include "main.h"
void FDCAN_Thread_Start(void);
#endif
3.3 关键点注意
FDCAN 与 bxCAN 相反:无过滤器命中时,全局过滤器(RXGFC 复位默认)会拒收所有非匹配帧,报文无法进入 RX FIFO0,相当于“全阻”。因此,这里应直接配置全局过滤器,不要只单独失能过滤器;即使失能了,也要用全局过滤器配置兜底。
禁用过滤器:
FDCAN_FilterTypeDef FDCAN_FilterInitStruct;
FDCAN_FilterInitStruct.FilterConfig = FDCAN_FILTER_DISABLE; // 禁用过滤器
配置全局过滤器:
if (HAL_FDCAN_ConfigGlobalFilter(&hfdcan1,
FDCAN_ACCEPT_IN_RX_FIFO0,
FDCAN_ACCEPT_IN_RX_FIFO0,
FDCAN_REJECT,
FDCAN_REJECT) != HAL_OK)
{
Error_Handler();
}
3.4 串口打印日志
[I][APP] APP Boot ============================================================================
[I][SDRAM] SDRAM init OK
[I][LCD] LCD ID:4384
[I][LCD] LCD init OK
[I][SYSTEM] main() user init OK
[I][RTOS] FreeRTOS init OK
[I][SYSTEM] running
[I][FDCAN] AddMessageToTxBuffer success
[I][FDCAN] EnableTxBufferRequest success
[I][FDCAN] FIFO is not empty
[I][FDCAN] ID: 291, Length: 8, Data: 48 65 6C 6C 6F 21 00 00
[I][FDCAN] CallBack: Tx Buffer 0 Complete
[I][SYSTEM] running
[I][FDCAN] AddMessageToTxBuffer success
[I][FDCAN] EnableTxBufferRequest success
[I][FDCAN] FIFO is not empty
[I][FDCAN] ID: 291, Length: 8, Data: 48 65 6C 6C 6F 21 00 00
[I][FDCAN] CallBack: Tx Buffer 0 Complete
[I][SYSTEM] running
[I][FDCAN] AddMessageToTxBuffer success
[I][FDCAN] EnableTxBufferRequest success
[I][FDCAN] FIFO is not empty
[I][FDCAN] ID: 291, Length: 8, Data: 48 65 6C 6C 6F 21 00 00
[I][FDCAN] CallBack: Tx Buffer 0 Complete
[I][SYSTEM] running
[I][FDCAN] AddMessageToTxBuffer success
[I][FDCAN] EnableTxBufferRequest success
[I][FDCAN] FIFO is not empty
[I][FDCAN] ID: 291, Length: 8, Data: 48 65 6C 6C 6F 21 00 00
[I][FDCAN] CallBack: Tx Buffer 0 Complete
4. STM32F1 侧
4.1 CubeMX 配置
CAN 配置:

中断配置:

引脚配置:

4.2 代码部分
4.2.1 过滤、中断、CAN 手动使能
/* USER CODE BEGIN CAN_Init 2 */
CAN_FilterTypeDef CAN_FilterInitStruct;
CAN_FilterInitStruct.FilterBank = 0;
/**
* @brief 四种过滤器
* 1. 16 位列表模式:四个参数分别存入一组 ID 即可
* 2. 16 位掩码模式:IdHigh 存入第一组 ID,MaskIdHigh 存入对应掩码;IdLow 存入第二组 ID,MaskIdLow 存入对应掩码
* 3. 32 位列表模式:IdHigh 和 IdLow 组合到一起,存入第一组 32 位 ID;MaskIdHigh 和 MaskIdLow 组合到一起,存入第二组 32 位 ID
* 4. 32 位掩码模式:IdHigh 和 IdLow 组合到一起,存入第一组 32 位 ID;MaskIdHigh 和 MaskIdLow 组合到一起,存入 对应掩码位
* 5. 全通模式:IdHigh 和 IdLow 都存入 0x0000,MaskIdHigh 和 MaskIdLow 都存入 0xFFFF,前提是 32 位掩码模式
*/
CAN_FilterInitStruct.FilterIdHigh = 0x0000;
CAN_FilterInitStruct.FilterIdLow = 0x0000;
CAN_FilterInitStruct.FilterMaskIdHigh = 0x0000;
CAN_FilterInitStruct.FilterMaskIdLow = 0x0000;
CAN_FilterInitStruct.FilterScale = CAN_FILTERSCALE_32BIT;
CAN_FilterInitStruct.FilterMode = CAN_FILTERMODE_IDMASK;
CAN_FilterInitStruct.FilterFIFOAssignment = CAN_FILTER_FIFO0;
CAN_FilterInitStruct.FilterActivation = ENABLE;
if (HAL_CAN_ConfigFilter(&hcan, &CAN_FilterInitStruct) != HAL_OK)
{
LOG_ERROR("CAN", "ConfigFilter failed");
Error_Handler();
}
/* 使能发送邮箱、接收 FIFO 0 和 CAN 状态错误中断 */
if (HAL_CAN_ActivateNotification(&hcan,
CAN_IT_TX_MAILBOX_EMPTY | // 发送邮箱空中断
CAN_IT_RX_FIFO0_MSG_PENDING | // 接收 FIFO 0 消息待处理中断
CAN_IT_RX_FIFO0_FULL | // 接收 FIFO 0 满中断
CAN_IT_RX_FIFO0_OVERRUN | // 接收 FIFO 0 溢出中断
CAN_IT_ERROR) != HAL_OK) // CAN 状态错误中断
{
LOG_ERROR("CAN", "ActivateNotification failed");
Error_Handler();
}
/* 启动 CAN 模块 */
if (HAL_CAN_Start(&hcan) != HAL_OK)
{
LOG_ERROR("CAN", "Start failed");
Error_Handler();
}
/* USER CODE END CAN_Init 2 */
4.2.2 app_can.c
#include "app_can.h"
#include "mylog.h"
#include "can.h"
#include <string.h>
#define CAN_RX_BUF_SIZE 16 // 软件接收缓冲区深度(帧),16 对单帧间隔收发足够
typedef struct
{
uint32_t ID; // 报文 ID(标准或扩展)
uint8_t Length; // 数据长度
uint8_t Data[8]; // 数据域
} CAN_FrameTypeDef;
static CAN_FrameTypeDef CAN_RxBuf[CAN_RX_BUF_SIZE]; // 环形缓冲区
static volatile uint8_t CAN_RxHead = 0; // 写指针,仅中断回调修改
static volatile uint8_t CAN_RxTail = 0; // 读指针,仅主循环修改
static volatile uint32_t CAN_TxCompleteMailboxes = 0; // 传输邮箱完成标志位
static volatile uint32_t CAN_TxAbortMailboxes = 0; // 传输邮箱中止标志位
static volatile uint32_t CAN_RxFifoFull = 0; // FIFO 满标志位
static volatile uint32_t CAN_ErrorCode = 0; // 错误码
/**
* @brief 发送 CAN 消息
* @param ID 标准 ID 号
* @param Length 数据长度
* @param TxData 数据指针
*/
void CAN_Transmit(uint32_t ID, uint8_t Length, uint8_t *TxData)
{
CAN_TxHeaderTypeDef TxMessage; // 发送消息结构体
uint32_t TxMailbox; // 接收返回存储 Tx 消息的邮箱号
/* 填充帧头信息 */
TxMessage.StdId = ID; // 标准 ID 号
TxMessage.ExtId = ID; // 扩展 ID 号
TxMessage.IDE = CAN_ID_STD; // 扩展 ID 标志位,选择标准 ID 类型
TxMessage.RTR = CAN_RTR_DATA; // 数据帧 & 遥控帧
TxMessage.DLC = Length; // 数据长度
/* 打印当前空闲邮箱数量 */
LOG_INFO("CAN", "Free Tx mailbox count: %lu", HAL_CAN_GetTxMailboxesFreeLevel(&hcan));
/* 将发送消息添加到发送邮箱队列并激活相应的传输请求 */
uint8_t result = HAL_CAN_AddTxMessage(&hcan, &TxMessage, TxData, &TxMailbox);
if (result != HAL_OK)
{
LOG_ERROR("CAN", "Add TxMessage to Mailbox or Activate transmission request Failed, return");
return;
}
LOG_INFO("CAN", "Add TxMessage to Mailbox and Activate transmission request Succeed, Mailbox: 0x%02X", TxMailbox);
}
/**
* @brief 检查软件接收缓冲区是否为空
* @param None
* @return uint8_t 1: 为空, 0: 不为空
*/
uint8_t CAN_RxFIFO_isEmpty(void)
{
if (CAN_RxHead == CAN_RxTail) // 软件缓冲区为空
{
LOG_WARN("CAN", "CAN RxFIFO is empty");
return 1;
}
LOG_INFO("CAN", "CAN RxFIFO not empty");
return 0;
}
/**
* @brief 接收 CAN 消息
* @param ID 标准 ID 号指针
* @param Length 数据长度指针
* @param RxData 数据指针
*/
void CAN_Receive(uint32_t *ID, uint8_t *Length, uint8_t *RxData)
{
CAN_FrameTypeDef *frame = &CAN_RxBuf[CAN_RxTail];
*ID = frame->ID;
*Length = frame->Length;
memcpy(RxData, frame->Data, frame->Length);
CAN_RxTail = (CAN_RxTail + 1) % CAN_RX_BUF_SIZE;
}
/**
* @brief 在主循环中处理 CAN 中断事件
*/
void CAN_ProcessEvents(void)
{
uint32_t completeMailboxes = CAN_TxCompleteMailboxes; // 复制传输邮箱完成标志位
uint32_t abortMailboxes = CAN_TxAbortMailboxes; // 复制传输邮箱中止标志位
uint32_t rxFifoFull = CAN_RxFifoFull; // 复制接收 FIFO 满标志位
uint32_t errorCode = CAN_ErrorCode; // 复制错误码
CAN_TxCompleteMailboxes = 0; // 清空传输邮箱完成标志位
CAN_TxAbortMailboxes = 0; // 清空传输邮箱中止标志位
CAN_RxFifoFull = 0; // 清空接收 FIFO 满标志位
CAN_ErrorCode = 0; // 清空错误码
/* 接收 FIFO 满标志位 */
if ((rxFifoFull & CAN_IT_RX_FIFO0_FULL) != 0U) // FIFO0 满
{
LOG_WARN("CAN", "Callback: CAN RxFIFO0 Full");
}
/* 处理传输邮箱完成标志位 */
if ((completeMailboxes & CAN_TX_MAILBOX0) != 0U) // 传输邮箱 0 完成
{
LOG_INFO("CAN", "Callback: Tx Mailbox 0 Complete");
}
if ((completeMailboxes & CAN_TX_MAILBOX1) != 0U) // 传输邮箱 1 完成
{
LOG_INFO("CAN", "Callback: Tx Mailbox 1 Complete");
}
if ((completeMailboxes & CAN_TX_MAILBOX2) != 0U) // 传输邮箱 2 完成
{
LOG_INFO("CAN", "Callback: Tx Mailbox 2 Complete");
}
/* 处理传输邮箱中止标志位 */
if ((abortMailboxes & CAN_TX_MAILBOX0) != 0U) // 传输邮箱 0 中止
{
LOG_WARN("CAN", "Callback: Tx Mailbox 0 Abort");
}
if ((abortMailboxes & CAN_TX_MAILBOX1) != 0U) // 传输邮箱 1 中止
{
LOG_WARN("CAN", "Callback: Tx Mailbox 1 Abort");
}
if ((abortMailboxes & CAN_TX_MAILBOX2) != 0U) // 传输邮箱 2 中止
{
LOG_WARN("CAN", "Callback: Tx Mailbox 2 Abort");
}
/* 处理错误码 */
if (errorCode != HAL_CAN_ERROR_NONE) // 错误码不为无错误
{
LOG_ERROR("CAN", "Callback: Error: 0x%08lX", errorCode);
}
}
/**
* @brief FIFO0 收到报文的中断回调(中断上下文,禁止调用 mylog)
*/
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcanPtr)
{
CAN_RxHeaderTypeDef RxMessage;
CAN_FrameTypeDef frame;
/* 从 FIFO FIFO0 读取消息 */
if (HAL_CAN_GetRxMessage(hcanPtr, CAN_RX_FIFO0, &RxMessage, frame.Data) == HAL_OK)
{
frame.ID = (RxMessage.IDE == CAN_ID_STD) ? RxMessage.StdId : RxMessage.ExtId; // 读出标准 ID 号或扩展 ID 号
frame.Length = RxMessage.DLC; // 数据长度
uint8_t next = (CAN_RxHead + 1) % CAN_RX_BUF_SIZE;
if (next != CAN_RxTail) // 缓冲区未满才入队,满了丢弃 newest
{
CAN_RxBuf[CAN_RxHead] = frame;
CAN_RxHead = next;
}
}
}
/**
* @brief FIFO0 满回调
* @param hcan 指向 CAN_HandleTypeDef 结构体的指针
*/
void HAL_CAN_RxFifo0FullCallback(CAN_HandleTypeDef *hcan)
{
(void)hcan;
CAN_RxFifoFull |= CAN_IT_RX_FIFO0_FULL;
}
/**
* @brief 传输邮箱 0 完成回调
* @param hcan 指向 CAN_HandleTypeDef 结构体的指针
*/
void HAL_CAN_TxMailbox0CompleteCallback(CAN_HandleTypeDef *hcan)
{
(void)hcan;
CAN_TxCompleteMailboxes |= CAN_TX_MAILBOX0;
}
/**
* @brief 传输邮箱 1 完成回调
* @param hcan 指向 CAN_HandleTypeDef 结构体的指针
*/
void HAL_CAN_TxMailbox1CompleteCallback(CAN_HandleTypeDef *hcan)
{
(void)hcan;
CAN_TxCompleteMailboxes |= CAN_TX_MAILBOX1;
}
/**
* @brief 传输邮箱 2 完成回调
* @param hcan 指向 CAN_HandleTypeDef 结构体的指针
*/
void HAL_CAN_TxMailbox2CompleteCallback(CAN_HandleTypeDef *hcan)
{
(void)hcan;
CAN_TxCompleteMailboxes |= CAN_TX_MAILBOX2;
}
/**
* @brief 传输邮箱 0 中止回调
* @param hcan 指向 CAN_HandleTypeDef 结构体的指针
*/
void HAL_CAN_TxMailbox0AbortCallback(CAN_HandleTypeDef *hcan)
{
(void)hcan;
CAN_TxAbortMailboxes |= CAN_TX_MAILBOX0;
}
/**
* @brief 传输邮箱 1 中止回调
* @param hcan 指向 CAN_HandleTypeDef 结构体的指针
*/
void HAL_CAN_TxMailbox1AbortCallback(CAN_HandleTypeDef *hcan)
{
(void)hcan;
CAN_TxAbortMailboxes |= CAN_TX_MAILBOX1;
}
/**
* @brief 传输邮箱 2 中止回调
* @param hcan 指向 CAN_HandleTypeDef 结构体的指针
*/
void HAL_CAN_TxMailbox2AbortCallback(CAN_HandleTypeDef *hcan)
{
(void)hcan;
CAN_TxAbortMailboxes |= CAN_TX_MAILBOX2;
}
/**
* @brief 错误回调
* @param hcan 指向 CAN_HandleTypeDef 结构体的指针
*/
void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan)
{
CAN_ErrorCode |= HAL_CAN_GetError(hcan);
}
4.2.3 app_can.h
#ifndef __APP_CAN_H__
#define __APP_CAN_H__
#include "main.h"
void CAN_Transmit(uint32_t ID, uint8_t Length, uint8_t *TxData);
uint8_t CAN_RxFIFO_isEmpty(void);
void CAN_Receive(uint32_t *ID, uint8_t *Length, uint8_t *RxData);
void CAN_ProcessEvents(void);
#endif
4.3 关键点注意
STM32F1 CAN 外设的引脚一定要配置成上拉。因为初始化成功是有一个硬性要求,CAN 初始化成功必须要在输入引脚上连续接收11 个隐形电平(逻辑电平 1) 才行,节点在请求 CAN BUS 使用权之前,需要先监听总线的状态,只有总线处于空闲状态的时候,发送节点才能发送起始位(SOF:Start Of Bit),之后,请求发送报文的各个节点进行仲裁,当监听到总线 ≥ 11 Bits 隐性位。也就是说,CAN BUS 由忙碌进入空闲,最快 11 Bits 时间,这里的 11 Bits 对应的 Bit Time = 1/500Kbps = 2us。即:CAN BUS 由忙碌进入空闲的最快 11 * 2us = 22us。如果总线上一直有节点请求发送,则 CAN BUS 会一直处于繁忙的状态。为了区分出每帧 CAN 报文,CAN报文之间需要预留一定空间,即:帧间隔,IFC(Inter Frame Space),由3个隐性位构成。
所以所以,在上电后最好将单片机的 CAN 对应引脚上拉为高电平。
4.4 串口打印日志
[I][APP] APP Boot ============================================================================
[I][SYSTEM] main() user init OK
[I][SYSTEM] running
[I][CAN] Free Tx mailbox count: 3
[I][CAN] Add TxMessage to Mailbox and Activate transmission request Succeed, Mailbox: 0x01
[I][CAN] CAN RxFIFO not empty
[I][CAN] ID: 291, Length: 8, Data: 48 65 6C 6C 6F 21 00 00
[I][CAN] Callback: Tx Mailbox 0 Complete
[I][SYSTEM] running
[I][CAN] Free Tx mailbox count: 3
[I][CAN] Add TxMessage to Mailbox and Activate transmission request Succeed, Mailbox: 0x01
[I][CAN] CAN RxFIFO not empty
[I][CAN] ID: 291, Length: 8, Data: 48 65 6C 6C 6F 21 00 00
[I][CAN] Callback: Tx Mailbox 0 Complete
[I][SYSTEM] running
[I][CAN] Free Tx mailbox count: 3
[I][CAN] Add TxMessage to Mailbox and Activate transmission request Succeed, Mailbox: 0x01
[I][CAN] CAN RxFIFO not empty
[I][CAN] ID: 291, Length: 8, Data: 48 65 6C 6C 6F 21 00 00
[I][CAN] Callback: Tx Mailbox 0 Complete
[I][SYSTEM] running
[I][CAN] Free Tx mailbox count: 3
[I][CAN] Add TxMessage to Mailbox and Activate transmission request Succeed, Mailbox: 0x01
[I][CAN] CAN RxFIFO not empty
[I][CAN] ID: 291, Length: 8, Data: 48 65 6C 6C 6F 21 00 00
[I][CAN] Callback: Tx Mailbox 0 Complete
[I][SYSTEM] running
[I][CAN] Free Tx mailbox count: 3
[I][CAN] Add TxMessage to Mailbox and Activate transmission request Succeed, Mailbox: 0x01
[I][CAN] CAN RxFIFO not empty
[I][CAN] ID: 291, Length: 8, Data: 48 65 6C 6C 6F 21 00 00
[I][CAN] Callback: Tx Mailbox 0 Complete
博客导航
本文来自博客园,作者:膝盖中箭卫兵,转载请注明原文链接:https://www.cnblogs.com/hai-feng-dev/p/23539060

浙公网安备 33010602011771号
https://orcid.org/0000-0001-5102-772X