uint8_t CAN_Init(CAN_TypeDef *can, uint32_t mode, uint32_t sjw, uint32_t pres, uint32_t ts1, uint32_t ts2)
{
/* 设置 CAN 驱动的基本参数 */
can_handle_struct.Instance = can; /* 指定要使用的 CAN 外设 */
can_handle_struct.Init.Prescaler = pres; /* 设置预分频器,控制 CAN 通信速率 */
can_handle_struct.Init.Mode = mode; /* 设置 CAN 模式:标准模式/回环模式 */
can_handle_struct.Init.SyncJumpWidth = sjw; /* 设置同步跳距,提升数据同步性能 */
can_handle_struct.Init.TimeSeg1 = ts1; /* 设置时间段 1,调节通信准确性 */
can_handle_struct.Init.TimeSeg2 = ts2; /* 设置时间段 2,调节通信稳定性 */
can_handle_struct.Init.TimeTriggeredMode = DISABLE; /* 禁用时间触发模式,提高灵活性 */
can_handle_struct.Init.AutoBusOff = DISABLE; /* 禁用自动总线关闭功能 */
can_handle_struct.Init.AutoWakeUp = DISABLE; /* 禁用自动唤醒功能 */
can_handle_struct.Init.AutoRetransmission = DISABLE; /* 禁用自动重发功能 */
can_handle_struct.Init.ReceiveFifoLocked = DISABLE; /* 禁用接收 FIFO 锁定,允许溢出覆盖 */
can_handle_struct.Init.TransmitFifoPriority = DISABLE; /* 禁用发送 FIFO 优先级,按先进先出顺序发送 */
/* 初始化 CAN 外设 */
if(HAL_CAN_Init(&can_handle_struct) != HAL_OK) /* HAL函数,实现CAN驱动初始化 */
{
printf("HAL_CAN_Init ERROR\r\n"); /* 如果初始化失败,打印错误信息 */
return 1; /* 返回错误代码 1 */
}
else
{
printf("HAL_CAN_Init OK\r\n"); /* 初始化成功,打印成功信息 */
}
CAN_FilterTypeDef can_filter_struct; /* 定义 CAN 过滤器结构体,用于配置接收消息的过滤规则 */
can_filter_struct.FilterIdHigh = 0x0000; /* 过滤器高位ID */
can_filter_struct.FilterIdLow = 0x0000; /* 过滤器低位ID */
can_filter_struct.FilterMaskIdHigh = 0x0000; /* 设置过滤器掩码的高位 */
can_filter_struct.FilterMaskIdLow = 0x0000; /* 设置过滤器掩码的低位 */
can_filter_struct.FilterFIFOAssignment = CAN_FILTER_FIFO0; /* 指定过滤后的数据存入 FIFO0 */
can_filter_struct.FilterBank = 0; /* 指定过滤器的过滤组序号 */
can_filter_struct.FilterMode = CAN_FILTERMODE_IDMASK; /* 使用 ID 掩码模式,通配 ID 匹配规则 */
can_filter_struct.FilterScale = CAN_FILTERSCALE_32BIT; /* 设置过滤器为 32 位模式 */
can_filter_struct.FilterActivation = CAN_FILTER_ENABLE; /* 启用过滤器 */
can_filter_struct.SlaveStartFilterBank = 14; /* 配置从机过滤器的起始序号 */
/* 配置 CAN 接收过滤器,过滤器用于筛选接收到的 CAN 消息,避免处理不需要的消息。 */
if(HAL_CAN_ConfigFilter(&can_handle_struct, &can_filter_struct) != HAL_OK) /* 调用 HAL 库函数配置过滤器,判断 CAN 接收过滤器是否成功配置 */
{
printf("HAL_CAN_ConfigFilter ERROR\r\n"); /* 如果配置失败,打印错误信息 */
return 2; /* 返回错误代码 2 */
}
else
{
printf("HAL_CAN_ConfigFilter OK\r\n"); /* 配置成功,打印成功信息 */
}
/* 启动 CAN 外设 */
if(HAL_CAN_Start(&can_handle_struct) != HAL_OK) /* 调用 HAL 库函数启动 CAN 外设,判断 CAN 外设是否成功启动 */
{
printf("HAL_CAN_Start ERROR\r\n"); /* 如果启动失败,打印错误信息 */
return 3; /* 返回错误代码 3 */
}
else
{
printf("HAL_CAN_Start OK\r\n"); /* 启动成功,打印成功信息 */
}
return 0; /* 返回 0,表示初始化成功 */
}
/**
* @brief CAN底层驱动函数:引脚配置,时钟配置
此函数会被HAL_CAN_Init()调用
* @param hcan:CAN句柄
* @retval 无
*/
void HAL_CAN_MspInit(CAN_HandleTypeDef *hcan)
{
if(CAN1 == hcan->Instance) /* 判断是否为 CAN1 外设实例 */
{
CAN1_RX_GPIO_CLK_ENABLE(); /* 启用 CAN1 接收(RX)引脚的时钟 */
CAN1_TX_GPIO_CLK_ENABLE(); /* 启用 CAN1 发送(TX)引脚的时钟 */
CAN1_CLK_ENABLE(); /* 启用 CAN1 外设的时钟 */
GPIO_InitTypeDef gpio_init_struct; /* 定义 GPIO 初始化结构体,用于配置 CAN 引脚 */
gpio_init_struct.Pin = CAN1_TX_GPIO_PIN; /* 设置要配置的 GPIO 引脚为 CAN1 TX 引脚 */
gpio_init_struct.Mode = GPIO_MODE_AF_OD; /* 配置为复用功能,开漏模式;开漏输出便于支持总线型通信 */
gpio_init_struct.Pull = GPIO_NOPULL; /* 无上下拉电阻;CAN 通信本身对上下拉没有硬性要求,由硬件电路决定是否需要。 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速模式;保证高频信号传输的质量 */
gpio_init_struct.Alternate = GPIO_AF9_CAN1; /* 设置引脚的复用功能为 CAN1 */
HAL_GPIO_Init(GPIOA, &gpio_init_struct); /* 初始化 CAN1 TX 引脚 */
gpio_init_struct.Pin = CAN1_RX_GPIO_PIN; /* 设置要配置的 GPIO 引脚为 CAN1 RX 引脚 */
HAL_GPIO_Init(GPIOA, &gpio_init_struct); /* 初始化 CAN1 RX 引脚 */
}
}
/**
* @brief CAN 发送一组数据
* @param Id : ID(帧标识符)
* @param IDE : 标识符类型 (标准帧/扩展帧)
* @param RTR : 帧类型 (数据帧/远程帧)
* @param DLC : 数据长度
* @param msg : 数据指针,指向要发送的数据
* @param TxMailBox : 发送邮箱
* @retval 发送状态
* @arg 0, 发送成功;
* @arg 1, 发送失败。
*/
uint8_t CAN_Send_Msg(uint32_t Id, uint32_t IDE, uint32_t RTR, uint32_t DLC, uint8_t *msg, uint32_t TxMailBox)
{
uint16_t t = 0; /* 定义超时计数器,用于检测发送是否超时 */
can_txhandle_struct.StdId = Id; /* 设置标准标识符 (11 位 ID),仅在标准帧模式下有效 */
can_txhandle_struct.ExtId = Id; /* 设置扩展标识符 (29 位 ID),仅在扩展帧模式下有效 */
can_txhandle_struct.IDE = IDE; /* 设置标识符类型:标准帧 (0) 或扩展帧 (1) */
can_txhandle_struct.RTR = RTR; /* 设置帧类型:数据帧 (0) 或远程帧 (1) */
can_txhandle_struct.DLC = DLC; /* 设置帧数据长度,有效值为 0-8 */
/* 将待发送的数据和帧信息添加到 CAN 控制器的发送邮箱 */
if(HAL_CAN_AddTxMessage(&can_handle_struct, &can_txhandle_struct, msg, &TxMailBox) != HAL_OK)
{
return 1; /* 如果添加到发送邮箱失败,返回 1 表示发送失败 */
}
/* 等待发送完成,循环检测发送邮箱是否已空闲。
CAN 控制器的邮箱有 3 个,每个邮箱的空闲状态由寄存器标志位控制。 */
while(HAL_CAN_GetTxMailboxesFreeLevel(&can_handle_struct) != 3)
{
t++; /* 每次循环递增超时计数器 */
if(t > 0xFFF) /* 如果超时计数器超过预设阈值 (0xFFF),则认为发送超时 */
{
HAL_CAN_AbortTxRequest(&can_handle_struct, TxMailBox); /* 超时,直接中止邮箱的发送请求,释放邮箱 */
printf("HAL_CAN_AbortTxRequest Time out\r\n"); /* 打印提示信息,通知超时 */
return 1; /* 返回 1,表示发送失败 */
}
}
return 0; /* 返回 0,表示发送成功 */
}
/**
* @brief CAN 接收数据查询
* @param Id : ID(帧标识符)
* @param IDE :标识符类型 (标准帧/扩展帧)
* @param RTR :帧类型 (数据帧/远程帧)
* @param buf : 数据缓存区,用于存储接收到的数据
* @retval 接收结果
* @arg 0 , 无数据被接收到;
* @arg 其他, 接收的数据长度。
*/
uint8_t CAN_Receive_Msg(uint32_t Id, uint32_t IDE, uint32_t RTR, uint8_t *buf)
{
/* 检查 CAN 接收 FIFO0 是否为空,即是否有数据可接收。
HAL_CAN_GetRxFifoFillLevel 函数返回 FIFO 中的帧数,如果为 0,表示没有数据可接收。 */
if(HAL_CAN_GetRxFifoFillLevel(&can_handle_struct, CAN_RX_FIFO0) == 0) /* 没有接收到数据 */
{
return 0; /* 如果接收FIFO为空,则直接返回0,表示无数据 */
}
/* 从 CAN 的接收 FIFO0 中读取一帧数据。
HAL_CAN_GetRxMessage 用于接收一帧 CAN 消息。如果返回值不是 HAL_OK,表示读取失败。 */
if (HAL_CAN_GetRxMessage(&can_handle_struct, CAN_RX_FIFO0, &can_rxhandle_struct, buf) != HAL_OK) /* 读取数据 */
{
return 0; /* 如果读取数据失败,返回0,表示接收失败 */
}
/* 检查接收到的帧是否符合预期的 ID、标识符类型和帧类型 */
if (can_rxhandle_struct.StdId!= Id || can_rxhandle_struct.IDE != IDE || can_rxhandle_struct.RTR != RTR) /* 接收到的ID不对 / 不是标准帧 / 不是数据帧 */
{
return 0; /* 如果帧不符合条件,返回0,表示不符合预期的接收条件 */
}
return can_rxhandle_struct.DLC; /* DLC 表示接收到的数据长度,返回值大于0时表示接收成功 */
}