STM32F302的内核为Cortex-M4。
bxCAN是基本扩展CAN(Basic Extended CAN)的缩写,它支持CAN协议2.0A和2.0B。它的设计目标是,以最小的CPU负荷来高效处理大量收到的报文。它也支持报文发送的优先级要求(优先级特性可软件配置)。对于安全紧要的应用, bxCAN提供所有支持时间触发通信模式所需的硬件功能。
1、HAL 库版本:STM32Cube FW_F3 V1.11.2
1、STM32CubeMX 6.2.0
2、Keil uVision5 V5.34.0.0
2021年7月25日
STM32F302参考手册:RM0365
STM32F3 HAL库用户手册:UM1786
在该项配置中的波特率配置如下图所示。其中Tpclk为经过分频的时钟,其中bxCAN在外设时钟APB1上为36MHz,经过分频为6MHz。
其中TS1配置为2、TS2配置为3。波特率为1MHz。此处配置可参考下面图片进行理解。
硬件过滤的做法节省了CPU开销, 每个过滤器组x由2个32位寄存器, CAN_FxR0 和 CAN_FxR1 组成。
主要的配置寄存器为:CAN_FM1R、CAN_FS1R、CAN_FA1R。参考下图,此处我们配置为:1 个 32 位过滤器-标识符屏蔽模式
看图可以了解到过滤器中的低三位是 IDE、RTR、0 三个,在扩展帧中ID 的位数为29位,所以我们定义的 ID 低29位是有效位,高三位我们通过移位操作去除掉。
我们跳转到 CAN 通信滤波器配置函数中就可以看出来,
参数 FilterIdHigh、 FilterIdLow 对应寄存器 CAN_FxR1、
参数 FilterMaskIdHigh、 FilterMaskIdLow 对应寄存器 CAN_FxR2。
对照下表我们就知道如何进行配置。
根据上述讲解我们将相应的 ID 左移三位,对应到 ID 的区域,配置 IDE 为1(我们用的扩展帧格式),代码如下图所示
记得初始化过滤器的结构体,此处代码截图未截到:CAN_FilterTypeDef My_CanFilter0,My_CanFilter1,My_CanFilter2,My_CanFilter3;
如果要过滤多个 ID 可以增加对滤波器(在STM32F302CBT6中共有13个滤波器),对应的参数为 FilterBank(0-12)
代码:
My_CanFilter0.FilterActivation = ENABLE;
My_CanFilter0.FilterBank = 0;
My_CanFilter0.FilterFIFOAssignment = CAN_FilterFIFO0;
My_CanFilter0.FilterScale = CAN_FILTERSCALE_32BIT;
My_CanFilter0.FilterMode = CAN_FILTERMODE_IDMASK;
My_CanFilter0.FilterIdHigh = ((CAN_IMU_ID<<3)>>16)&0xffff;
My_CanFilter0.FilterIdLow = ((CAN_IMU_ID<<3)&0xffff)|CAN_ID_EXT;
My_CanFilter0.FilterMaskIdHigh = ((DeviceMask<<3)>>16)&0xffff;
My_CanFilter0.FilterMaskIdLow = ((DeviceMask<<3)&0xffff)|CAN_ID_EXT;
if(HAL_CAN_ConfigFilter(&hcan,&My_CanFilter0) != HAL_OK)
{
Error_Handler();
}
My_CanFilter1.FilterActivation = ENABLE;
My_CanFilter1.FilterBank = 1;
My_CanFilter1.FilterFIFOAssignment = CAN_FilterFIFO0;
My_CanFilter1.FilterScale = CAN_FILTERSCALE_32BIT;
My_CanFilter1.FilterMode = CAN_FILTERMODE_IDMASK;
My_CanFilter1.FilterIdHigh = ((CAN_ROLL_ID<<3)>>16)&0xffff;
My_CanFilter1.FilterIdLow = ((CAN_ROLL_ID<<3)&0xffff)|CAN_ID_EXT;
My_CanFilter1.FilterMaskIdHigh = ((DeviceMask<<3)>>16)&0xffff;
My_CanFilter1.FilterMaskIdLow = ((DeviceMask<<3)&0xffff)|CAN_ID_EXT;
if(HAL_CAN_ConfigFilter(&hcan,&My_CanFilter1) != HAL_OK)
{
Error_Handler();
}
My_CanFilter2.FilterActivation = ENABLE;
My_CanFilter2.FilterBank = 2;
My_CanFilter2.FilterFIFOAssignment = CAN_FilterFIFO0;
My_CanFilter2.FilterScale = CAN_FILTERSCALE_32BIT;
My_CanFilter2.FilterMode = CAN_FILTERMODE_IDMASK;
My_CanFilter2.FilterIdHigh = ((CAN_PITCH_ID<<3)>>16)&0xffff;
My_CanFilter2.FilterIdLow = ((CAN_PITCH_ID<<3)&0xffff)|CAN_ID_EXT;
My_CanFilter2.FilterMaskIdHigh = ((DeviceMask<<3)>>16)&0xffff;
My_CanFilter2.FilterMaskIdLow = ((DeviceMask<<3)&0xffff)|CAN_ID_EXT;
if(HAL_CAN_ConfigFilter(&hcan,&My_CanFilter2) != HAL_OK)
{
Error_Handler();
}
My_CanFilter3.FilterActivation = ENABLE;
My_CanFilter3.FilterBank = 3;
My_CanFilter3.FilterFIFOAssignment = CAN_FilterFIFO0;
My_CanFilter3.FilterScale = CAN_FILTERSCALE_32BIT;
My_CanFilter3.FilterMode = CAN_FILTERMODE_IDMASK;
My_CanFilter3.FilterIdHigh = ((CAN_YAW_ID<<3)>>16)&0xffff;
My_CanFilter3.FilterIdLow = ((CAN_YAW_ID<<3)&0xffff)|CAN_ID_EXT;
My_CanFilter3.FilterMaskIdHigh = ((DeviceMask<<3)>>16)&0xffff;
My_CanFilter3.FilterMaskIdLow = ((DeviceMask<<3)&0xffff)|CAN_ID_EXT;
if(HAL_CAN_ConfigFilter(&hcan,&My_CanFilter3) != HAL_OK)
{
Error_Handler();
}
在 HAL 库中 CAN 有两种 API 来使能或关闭相应的中断,我测试的是两者的效果相同。
大家可以测试一下留言讨论一下
API:
HAL_CAN_ActivateNotification 开启相应的中断
HAL_CAN_DeactivateNotification 禁用相应的中断
__HAL_CAN_ENABLE_IT 开启相应的中断
__HAL_CAN_DISABLE_IT 禁用相应的中断
代码:
// if(HAL_CAN_ActivateNotification(&hcan,CAN_IT_TX_MAILBOX_EMPTY|CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
// {
// Error_Handler();
// }
__HAL_CAN_ENABLE_IT(&hcan,CAN_IT_TX_MAILBOX_EMPTY|CAN_IT_RX_FIFO0_MSG_PENDING);
if(HAL_CAN_Start(&hcan) != HAL_OK)
{
Error_Handler();
}
/* USER CODE END CAN_Init 2 */
}
在以上配置中我们使能的中断是发送邮箱空中断&接收FIFO0挂起中断
在中断处理文件中 stm32f3xx_it.c 的相应中断函数 USB_HP_CAN_TX_IRQHandler\USB_LP_CAN_RX0_IRQHandler 找到CAN的中断处理 HAL_CAN_IRQHandler
在 HAL_CAN_IRQHandler 中找到配置使能的中断处理代码(我们只使用了接收挂起中断),找到对应的处理,使用回调函数
HAL_CAN_RxFifo0MsgPendingCallback 进行相应中断的处理
中断回调处理代码:
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
if(hcan->Instance == CAN)
{
HAL_CAN_GetRxMessage(hcan,CAN_FilterFIFO0,&My_RxHeader,Rx_DataBuff);
}
if(My_RxHeader.IDE == CAN_ID_STD)
{
if(My_RxHeader.StdId == 0x00500000)
{
}
}
if(My_RxHeader.IDE == CAN_ID_EXT)
{
if(My_RxHeader.ExtId == 0x00500000)
{
led(1);
}
if(My_RxHeader.ExtId == 0x00510000)
{
led(2);
}
if(My_RxHeader.ExtId == 0x00520000)
{
led(3);
}
if(My_RxHeader.ExtId == 0x00530000)
{
led(4);
}
}
}
4.4、主函数的配置
添加测试代码
代码:
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
printf("CAN通信测试 帧ID = %x \r\n",CAN_YAW_ID);
CAN_SendData(&hcan,0x00510000,Rx_DataBuff,8);
}
/* USER CODE END 3 */
5、测试