STMF4系列HAL庫使用雙CAN配置及注意事項
主控芯片:STM32F406ZGT6
軟件版本:STM32CUBEMX 5.4
HAL庫版本:STM32Cube FW_F4 V1.24.2
對於本實驗芯片,該單片機具有兩個CAN接口,CAN1爲主機CAN,CAN2爲從機CAN;CAN2是和CAN1有關聯的,要想使用CAN2必須先使能CAN1的時鐘!
時鐘配置
CAN配置
CAN外設是掛載在APB1_PCLK1時鐘上的,APB1_PCLK1是42M。CAN的最大速率是1MHZ,本實驗配置爲500KHZ。波特率配置計算方法:
CAN波特率 = APB1_PCLK1/分頻/(tq1 + tq2 + rjw)
本實驗波特率 = 42MHZ/4分頻/(14 + 6 + 1) = 0.5MHZ = 500KHZ
使能CAN接收中斷
配置CAN的IO,此步驟必須!!要不然HAL庫函數MX_CAN_Init初始化會失敗!!
CAN2和CAN1是同樣的配置,這裏不再貼CAN2的配置圖了。
STM32外設CAN過濾器說明
STM32CUBEMX生成的代碼默認是沒有設置ID篩選器的,所以需要手動添加過濾器代碼。下面一張圖,STM32的過濾器組:
STM32F407ZG有28組篩選器,一組篩選器有兩個32位的寄存器,篩選器組可配置爲四種模式:
1個32位篩選器-標識符掩碼模式
,這時候篩選器組的兩個32位寄存器一個用來存放ID,另一個用來存放ID的掩碼兩個32位篩選器-標識符列表模式
,這時候篩選器組的兩個32位寄存器都用來存放ID兩個16位篩選器-標識符掩碼模式
,這時候篩選器組被分成了4個16位的寄存器,分別存放ID高16位+掩碼高16位,ID低16位+掩碼低16位四個16位篩選-標識符列表模式
,這時候篩選器組被分成了4個16位的寄存器,都存放ID的高16位和低16位
貼上我的配置代碼,我的配置代碼目前使用屏蔽位模式沒有問題,但是使用列表模式CAN2只能發送無法接收數據,目前問題未找到:
#define CAN1_FILTER_MODE_MASK_ENABLE 1 ///< CAN1過濾器模式選擇:=1:屏蔽位模式 =0:屏蔽列表模式
#define CAN2_FILTER_MODE_MASK_ENABLE 1 ///< CAN2過濾器模式選擇:=1:屏蔽位模式 =0:屏蔽列表模式
#define CAN1_BASE_ID 0x10F00266 ///< 主CAN過濾ID
#define CAN2_BASE_ID 0x10F0F126 ///< 從CAN過濾ID
#define CAN1_FILTER_BANK 0 ///< 主CAN過濾器組編號
#define CAN2_FILTER_BANK 14 ///< 從CAN過濾器組編號
/// CAN過濾器寄存器位寬類型定義
typedef union
{
__IO uint32_t value;
struct
{
uint8_t REV : 1; ///< [0] :未使用
uint8_t RTR : 1; ///< [1] : RTR(數據幀或遠程幀標誌位)
uint8_t IDE : 1; ///< [2] : IDE(標準幀或擴展幀標誌位)
uint32_t EXID : 18; ///< [21:3] : 存放擴展幀ID
uint16_t STID : 11; ///< [31:22]: 存放標準幀ID
} Sub;
} CAN_FilterRegTypeDef;
// CAN_FMR寄存器位寬類型定義
typedef union
{
__IO uint32_t value;
struct
{
uint8_t FINIT : 1;
uint8_t RESERVER_0 : 7;
uint8_t CAN2SB : 6;
uint32_t RESERVER_1 : 18;
}Sub;
}FMR_TypeDef;
/// 設置CAN1的過濾器(主CAN)
static void CAN1_Filter_Config(void)
{
CAN_FilterTypeDef sFilterConfig;
CAN_FilterRegTypeDef IDH = {0};
CAN_FilterRegTypeDef IDL = {0};
IDH.Sub.IDE = 0; // 標準幀
IDH.Sub.STID = 0; // 標準幀ID值
IDH.Sub.EXID = (CAN1_BASE_ID >> 16) & 0xFFFF; // 擴展幀高16位ID值
IDL.Sub.IDE = 1; // 擴展幀
IDL.Sub.STID = 0; // 標準幀ID值
IDL.Sub.EXID = (CAN1_BASE_ID & 0xFFFF); // 擴展幀低16位ID值
sFilterConfig.FilterBank = CAN1_FILTER_BANK; // 設置過濾器組編號
#if CAN1_FILTER_MODE_MASK_ENABLE
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 屏蔽位模式
#else
sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST; // 列表模式
#endif
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 32位寬
sFilterConfig.FilterIdHigh = IDH.value; // 標識符寄存器一ID高十六位,放入擴展幀位
sFilterConfig.FilterIdLow = IDL.value; // 標識符寄存器一ID低十六位,放入擴展幀位
sFilterConfig.FilterMaskIdHigh = IDH.value; // 標識符寄存器二ID高十六位,放入擴展幀位
sFilterConfig.FilterMaskIdLow = IDL.value; // 標識符寄存器二ID低十六位,放入擴展幀位
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; // 過濾器組關聯到FIFO0
sFilterConfig.FilterActivation = ENABLE; // 激活過濾器
sFilterConfig.SlaveStartFilterBank = CAN2_FILTER_BANK; // 設置CAN2的起始過濾器組(對於單CAN的CPU或從CAN此參數無效;對於雙CAN的CPU此參數爲從CAN的起始過濾器組編號)
if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
{
Error_Handler();
}
{
FMR_TypeDef regval = {0};
regval.value = hcan1.Instance->FMR;
printf("------ CAN1:> FMR:0x%0X CAN2SB:0x%X \r\n", regval.value, regval.Sub.CAN2SB);
}
}
/// 設置CAN2的過濾器(從CAN)
static void CAN2_Filter_Config(void)
{
CAN_FilterTypeDef sFilterConfig;
CAN_FilterRegTypeDef IDH = {0};
CAN_FilterRegTypeDef IDL = {0};
IDH.Sub.IDE = 0;
IDH.Sub.STID = 0;
IDH.Sub.EXID = (CAN2_BASE_ID >> 16) & 0xFFFF;
IDL.Sub.IDE = 1;
IDL.Sub.STID = 0;
IDL.Sub.EXID = (CAN2_BASE_ID & 0xFFFF);
sFilterConfig.FilterBank = CAN2_FILTER_BANK; // 設置過濾器組編號
#if CAN2_FILTER_MODE_MASK_ENABLE
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 屏蔽位模式
#else
sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST; // 列表模式
#endif
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 32位寬
sFilterConfig.FilterIdHigh = IDH.value; // 標識符寄存器一ID高十六位,放入擴展幀位
sFilterConfig.FilterIdLow = IDL.value; // 標識符寄存器一ID低十六位,放入擴展幀位
sFilterConfig.FilterMaskIdHigh = IDH.value; // 標識符寄存器二ID高十六位,放入擴展幀位
sFilterConfig.FilterMaskIdLow = IDL.value; // 標識符寄存器二ID低十六位,放入擴展幀位
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; // 過濾器組關聯到FIFO0
sFilterConfig.FilterActivation = ENABLE; // 激活過濾器
sFilterConfig.SlaveStartFilterBank = 28; // 無效
if (HAL_CAN_ConfigFilter(&hcan2, &sFilterConfig) != HAL_OK)
{
Error_Handler();
}
{
FMR_TypeDef regval = {0};
regval.value = hcan2.Instance->FMR;
printf("------ CAN2:> FMR:0x%0X CAN2SB:0x%X \r\n", regval.value, regval.Sub.CAN2SB);
}
}
/// CAN初始化
void CAN_Init(void)
{
MX_CAN1_Init(); // 初始化CNA1
CAN1_Filter_Config(); // 初始化CNA1過濾器
HAL_CAN_Start(&hcan1); // 啓動CAN1
HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING); // 激活CAN1 FIFO0
MX_CAN2_Init();
CAN2_Filter_Config();
HAL_CAN_Start(&hcan2);
HAL_CAN_ActivateNotification(&hcan2, CAN_IT_RX_FIFO0_MSG_PENDING);
}
/**
* CAN數據傳輸
* @param buf 待發送的數據
* @param len 數據長度
* @param number CAN編號,=0:CAN1,=1:CAN2
* @return 0:成功 other:失敗
*/
uint8_t CAN_Transmit(const void* buf, uint32_t len, uint8_t number)
{
uint32_t txmailbox = 0;
uint32_t offset = 0;
CAN_TxHeaderTypeDef hdr;
hdr.IDE = CAN_ID_EXT; // ID類型:擴展幀
hdr.RTR = CAN_RTR_DATA; // 幀類型:數據幀
hdr.StdId = 0; // 標準幀ID,最大11位,也就是0x7FF
hdr.ExtId = number == 0 ? CAN1_BASE_ID : CAN2_BASE_ID; // 擴展幀ID,最大29位,也就是0x1FFFFFFF
hdr.TransmitGlobalTime = DISABLE;
while (len != 0)
{
hdr.DLC = len > 8 ? 8 : len; // 數據長度
if (HAL_CAN_AddTxMessage(number == 0 ? &hcan1 : &hcan2, &hdr, ((uint8_t *)buf) + offset, &txmailbox) != HAL_OK)
return 1;
offset += hdr.DLC;
len -= hdr.DLC;
}
return 0;
}
uint8_t CAN1_RX_STA = 0; ///< CAN1數據接收標誌:[7]:數據 [6:0]:未使用
uint8_t CAN2_RX_STA = 0; ///< CAN2數據接收標誌:[7]:數據 [6:0]:未使用
uint8_t CAN1_RX_BUF[8]; ///< CAN1數據接收緩存
uint8_t CAN2_RX_BUF[8]; ///< CAN2數據接收緩存
uint8_t CAN1_TX_BUF[8]; ///< CAN1數據發送緩存
uint8_t CAN2_TX_BUF[8]; ///< CAN2數據發送緩存
/**
* CAN FIFO0 數據接收中斷回調函數
* @param hcan CAN句柄
*/
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
static CAN_RxHeaderTypeDef CAN_RX_HDR;
// CAN1數據接收
if (hcan->Instance == hcan1.Instance)
{
// 數據已經處理
if ((CAN1_RX_STA & 0x80) == 0)
{
// 清空緩存
memset(CAN1_RX_BUF, 0, 8);
// 接收數據
if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &CAN_RX_HDR, CAN1_RX_BUF) == HAL_OK) // 獲得接收到的數據頭和數據
{
CAN1_RX_STA |= 0x80; // 標記接收到數據
HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING); // 再次使能FIFO0接收中斷
}
}
}
// CAN2數據接收
else if (hcan->Instance == hcan2.Instance)
{
// 數據已經處理
if ((CAN2_RX_STA & 0x80) == 0)
{
// 清空緩存
memset(CAN2_RX_BUF, 0, 8);
// 接收數據
if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &CAN_RX_HDR, CAN2_RX_BUF) == HAL_OK) // 獲得接收到的數據頭和數據
{
CAN2_RX_STA |= 0x80; // 標記接收到數據
HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING); // 再次使能FIFO0接收中斷
}
}
}
}
///< CAN數據處理函數
inline void CAN_RecvHandler(void)
{
// CAN1有數據收到
if (CAN1_RX_STA & 0x80)
{
int i = 0;
memcpy(CAN1_TX_BUF, CAN1_RX_BUF, sizeof(CAN1_RX_BUF)); // 拷貝出數據
CAN1_RX_STA = 0; // 重置CAN1接收狀態
for(i = 0; i != 8; i++)
{
printf("CAN1_TX_BUF[%d]:0x%X\r\n", i, CAN1_TX_BUF[i]);
}
printf("\r\n\r\n");
}
// CAN2有數據收到
if (CAN2_RX_STA & 0x80)
{
int i = 0;
memcpy(CAN2_TX_BUF, CAN2_RX_BUF, sizeof(CAN2_RX_BUF)); // 拷貝出數據
CAN2_RX_STA = 0; // 重置CAN1接收狀態
for(i = 0; i != 8; i++)
{
printf("CAN2_TX_BUF[%d]:0x%X\r\n", i, CAN2_TX_BUF[i]);
}
printf("\r\n\r\n");
}
}