1、簡介
STM32F302的核心為Cortex-M4。
bxCAN是基本擴充CAN(Basic Extended CAN)的縮寫,它支援CAN協定2.0A和2.0B。它的設計目标是,以最小的CPU負荷來高效處理大量收到的封包。它也支援封包發送的優先級要求(優先級特性可軟體配置)。對于安全緊要的應用, bxCAN提供所有支援時間觸發通信模式所需的硬體功能。
2、準備
2.1、軟體
1、HAL 庫版本:STM32Cube FW_F3 V1.11.2
2.2、開發工具
1、STM32CubeMX 6.2.0
2、Keil uVision5 V5.34.0.0
2.3、代碼測試時間
2021年7月25日
2.4、參考文檔
STM32F302參考手冊:RM0365
STM32F3 HAL庫使用者手冊:UM1786
3、CubeMX 工程配置
3.1、參數配置
在該項配置中的波特率配置如下圖所示。其中Tpclk為經過分頻的時鐘,其中bxCAN在外設時鐘APB1上為36MHz,經過分頻為6MHz。
其中TS1配置為2、TS2配置為3。波特率為1MHz。此處配置可參考下面圖檔進行了解。

3.2、中斷配置
3.3、中斷優先級配置
4、代碼配置
4.1、配置辨別符過濾
硬體過濾的做法節省了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。
對照下表我們就知道如何進行配置。
4.2、濾波器配置初始化
根據上述講解我們将相應的 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();
}
4.3、添加中斷使能及CAN的啟動
在 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 */
}
4.4、中斷函數的配置
在以上配置中我們使能的中斷是發送郵箱空中斷&接收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、測試