【STM32】STMF4系列HAL庫使用雙CAN配置及注意事項

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");
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章