最近项目中用到CAN,想直接搬原子哥的代码,发现中断好像有些问题,另外网上的资源版本都很老,版本不同,本文采用最新版本(▽)
注意事项:
CAN在配置接收得时候要配置滤波器,否则无法接收
频率配置:
由手册可知CAN挂载在APB1总线上,最高频率为42MHZ;
由此:
- 预分频器值(
hcan1.Init.Prescaler
)设置为12
。 - 时间段1(
hcan1.Init.TimeSeg1
)设置为7
个时间单元。 - 时间段2(
hcan1.Init.TimeSeg2
)设置为6
个时间单元。
我们可以使用以下公式来计算CAN总线的波特率:
波特率 = CAN时钟频率 / ((时间段1 + 时间段2 + 1) × 预分频器)
假设STM32F407的CAN时钟频率为42 MHz,则波特率计算如下:
波特率 = 42 MHz / ((7 + 6 + 1) × 12
) = 42 MHz / (14 × 12) = 42 MHz / 168 ≈ 250 kHz
因此,根据提供的参数,CAN总线的波特率约为250 kHz。请确保使用正确的CAN时钟频率进行准确的波特率计算。
以CAN1为例:
初始化并启动CAN:
void CAN1_Init(void)
{
__HAL_RCC_CAN1_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
CAN_FilterTypeDef sFilterConfig1;
GPIO_InitTypeDef GPIO_InitStruct = {0};
hcan1.Instance = CAN1;
hcan1.Init.Prescaler = 12;
hcan1.Init.Mode = CAN_MODE_NORMAL;
hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
hcan1.Init.TimeSeg1 = CAN_BS1_7TQ;
hcan1.Init.TimeSeg2 = CAN_BS2_6TQ;
hcan1.Init.TimeTriggeredMode = DISABLE;
hcan1.Init.AutoBusOff = DISABLE;
hcan1.Init.AutoWakeUp = DISABLE;
hcan1.Init.AutoRetransmission = DISABLE;
hcan1.Init.ReceiveFifoLocked = DISABLE;
hcan1.Init.TransmitFifoPriority = DISABLE;
if (HAL_CAN_Init(&hcan1) != HAL_OK)
{
Error_Handler();
}
/* 配置CAN过滤器 */
sFilterConfig1.FilterBank = 0; /* 过滤器0 */
sFilterConfig1.FilterMode = CAN_FILTERMODE_IDMASK;
sFilterConfig1.FilterScale = CAN_FILTERSCALE_32BIT;
sFilterConfig1.FilterIdHigh = 0x0000; /* 32位ID */
sFilterConfig1.FilterIdLow = 0x0000;
sFilterConfig1.FilterMaskIdHigh = 0x0000; /* 32位MASK */
sFilterConfig1.FilterMaskIdLow = 0x0000;
sFilterConfig1.FilterFIFOAssignment = CAN_FILTER_FIFO0; /* 过滤器0关联到FIFO0 */
sFilterConfig1.FilterActivation = CAN_FILTER_ENABLE; /* 激活滤波器0 */
sFilterConfig1.SlaveStartFilterBank = 14;
/* 过滤器配置 */
if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig1) != HAL_OK)
{
return ;
}
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1;
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(GPIOD, &GPIO_InitStruct);
/* USER CODE BEGIN CAN1_MspInit 1 */
HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn);
if(HAL_CAN_Start(&hcan1)!=HAL_OK)
{
Error_Handler();
}
if(HAL_CAN_ActivateNotification(&hcan1,CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)//开启接受邮箱0挂起中断
{
Error_Handler();
}
}
中断函数:
extern uint8_t data[8];
extern CAN_RxHeaderTypeDef RXHeader; // 接收消息的头部结构体
void CAN1_RX0_IRQHandler(void)
{
HAL_CAN_GetRxMessage(&hcan1,CAN_FILTER_FIFO0,&RXHeader,data);//获取数据
HAL_GPIO_TogglePin( GPIOG , GPIO_PIN_1);
}
封装发送函数:
// 发送CAN1标准数据帧
uint8_t CAN1_Send_Standard_Frame(uint32_t id, uint8_t *msg, uint8_t len)
{
uint16_t t = 0;
uint32_t TxMailbox;
CAN_TxHeaderTypeDef can_tx_header;
can_tx_header.StdId = id; // 标准标识符
can_tx_header.ExtId = 0; // 扩展标识符(此处为标准帧,扩展标识符为0)
can_tx_header.IDE = CAN_ID_STD; // 使用标准帧
can_tx_header.RTR = CAN_RTR_DATA; // 数据帧
can_tx_header.DLC = len; // 数据长度
if (HAL_CAN_AddTxMessage(&hcan1, &can_tx_header, msg, &TxMailbox) != HAL_OK)
{
// 发送失败,打印错误消息或采取其他适当的措施
return 1;
}
// 等待发送完成,所有邮箱为空
while (HAL_CAN_GetTxMailboxesFreeLevel(&hcan1) != 3)
{
t++;
if (t > 0xFFF)
{
// 超时,直接中止邮箱的发送请求,采取适当的措施
HAL_CAN_AbortTxRequest(&hcan1, TxMailbox);
return 1;
}
}
return 0; // 发送成功
}
// 发送CAN1扩展数据帧
uint8_t CAN1_Send_Extended_Frame(uint32_t id, uint8_t *msg, uint8_t len)
{
uint16_t t = 0;
uint32_t TxMailbox;
CAN_TxHeaderTypeDef can_tx_header;
can_tx_header.StdId = 0; // 标准标识符(此处为扩展帧,标准标识符为0)
can_tx_header.ExtId = id; // 扩展标识符
can_tx_header.IDE = CAN_ID_EXT; // 使用扩展帧
can_tx_header.RTR = CAN_RTR_DATA; // 数据帧
can_tx_header.DLC = len; // 数据长度
if (HAL_CAN_AddTxMessage(&hcan1, &can_tx_header, msg, &TxMailbox) != HAL_OK)
{
// 发送失败,打印错误消息或采取其他适当的措施
return 1;
}
// 等待发送完成,所有邮箱为空
while (HAL_CAN_GetTxMailboxesFreeLevel(&hcan1) != 3)
{
t++;
if (t > 0xFFF)
{
// 超时,直接中止邮箱的发送请求,采取适当的措施
HAL_CAN_AbortTxRequest(&hcan1, TxMailbox);
return 1;
}
}
return 0; // 发送成功
}
"如果你不去追逐自己的梦想,有一天你会被雇来帮助别人实现他们的梦想。" - 扖夫·乔布斯(Steve Jobs)