­

基於STM32F429和HAL庫的CAN收發常式

  • 2019 年 10 月 17 日
  • 筆記

1.CAN協議介紹

  CAN Controller Area Network 的縮寫(以下稱為 CAN),是 ISO 國際標準化的串列通訊
協議。在當前的汽車產業中,出於對安全性、舒適性、方便性、低公害、低成本的要求,各種
各樣的電子控制系統被開發了出來。由於這些系統之間通訊所用的數據類型及對可靠性的要求
不盡相同,由多條匯流排構成的情況很多,線束的數量也隨之增加。為適應“減少線束的數量”、
“通過多個 LAN,進行大量數據的高速通訊”的需要, 1986 年德國電氣商博世公司開發出面
向汽車的 CAN 通訊協議。此後, CAN 通過 ISO11898 ISO11519 進行了標準化,現在在歐
洲已是汽車網路的標準協議。
現在, CAN 的高性能和可靠性已被認同,並被廣泛地應用於工業自動化、船舶、醫療設
備、工業設備等方面。現場匯流排是當今自動化領域技術發展的熱點之一,被譽為自動化領域的
電腦區域網。它的出現為分散式控制系統實現各節點之間實時、可靠的數據通訊提供了強有
力的技術支援。
CAN 控制器根據兩根線上的電位差來判斷匯流排電平。匯流排電平分為顯性電平和隱性電平,
二者必居其一。發送方通過使匯流排電平發生變化,將消息發送給接收方。


2.Cube配置

  基本配置跳過,直接講CAN的配置,只是收發的話,配好CAN的時鐘加上開個接收中斷就行了。

CAN的時鐘配置是掛載在APB1的時鐘上的,可根據這一點來計算CAN匯流排的波特率,比如本常式的APB1

是45MHz,則CAN匯流排的波特率為 :45M/((9+5+1)*6)M=500KMz。

 

 

 

要注意的是阿波羅F429的CAN數據線的GPIO口是在PA11和PA12,但Cube自動生成的不是這兩個GPIO口。

記得改一下GPIO口復用,軟體要和硬體要匹配。

 

 

Cube生成的程式碼如下:

/* Includes ------------------------------------------------------------------*/  #include "can.h"    /* USER CODE BEGIN 0 */    /* USER CODE END 0 */    CAN_HandleTypeDef hcan1;    /* CAN1 init function */  void MX_CAN1_Init(void)  {      hcan1.Instance = CAN1;    hcan1.Init.Prescaler = 6;    hcan1.Init.Mode = CAN_MODE_NORMAL;    hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;    hcan1.Init.TimeSeg1 = CAN_BS1_9TQ;    hcan1.Init.TimeSeg2 = CAN_BS2_5TQ;    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();    }    }    void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle)  {      GPIO_InitTypeDef GPIO_InitStruct = {0};    if(canHandle->Instance==CAN1)    {    /* USER CODE BEGIN CAN1_MspInit 0 */      /* USER CODE END CAN1_MspInit 0 */      /* CAN1 clock enable */      __HAL_RCC_CAN1_CLK_ENABLE();        __HAL_RCC_GPIOA_CLK_ENABLE();      /**CAN1 GPIO Configuration      PA11     ------> CAN1_RX      PA12     ------> CAN1_TX      */      GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;      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(GPIOA, &GPIO_InitStruct);        /* CAN1 interrupt Init */      HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 0, 0);      HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn);    /* USER CODE BEGIN CAN1_MspInit 1 */      /* USER CODE END CAN1_MspInit 1 */    }  }    void HAL_CAN_MspDeInit(CAN_HandleTypeDef* canHandle)  {      if(canHandle->Instance==CAN1)    {    /* USER CODE BEGIN CAN1_MspDeInit 0 */      /* USER CODE END CAN1_MspDeInit 0 */      /* Peripheral clock disable */      __HAL_RCC_CAN1_CLK_DISABLE();        /**CAN1 GPIO Configuration      PA11     ------> CAN1_RX      PA12     ------> CAN1_TX      */      HAL_GPIO_DeInit(GPIOA, GPIO_PIN_11|GPIO_PIN_12);        /* CAN1 interrupt Deinit */      HAL_NVIC_DisableIRQ(CAN1_RX0_IRQn);    /* USER CODE BEGIN CAN1_MspDeInit 1 */      /* USER CODE END CAN1_MspDeInit 1 */    }  }    /* USER CODE BEGIN 1 */    /* USER CODE END 1 */

 

 

 3.Cube收發常式

  除了Cube的配置外,我們還要配置一個CAN_FilterTypeDef(CAN1濾波器)和一個CAN_TxHeaderTypeDef(CAN1發送消息句柄),然後

就可以啟動CAN,使能CAN中斷,至於CAN_RxHeaderTypeDef(CAN1接收消息句柄)只用定義不用配置。


CAN_TxHeaderTypeDef hCAN1_TxHeader; //CAN1發送消息
CAN_RxHeaderTypeDef hCAN1_RxHeader; //CAN1接收消息
CAN_FilterTypeDef hCAN1_Filter; //CAN1濾波器



/*
****************************************************************************** * Function Name : vApp_CAN_TxHeader_Init * Description : 初始化發送幀頭句柄 * Input : pHeader 發送幀頭指針 StdId 標識符 ExtId 擴展標識符 IDE 0:標準幀 1:拓展幀 RTR 0:數據幀 1:遠程幀 DLC 數據長度 * Output : None * Return : None ****************************************************************************** */ void vApp_CAN_TxHeader_Init(CAN_TxHeaderTypeDef * pHeader, uint32_t StdId, uint32_t ExtId, uint32_t IDE, uint32_t RTR, uint32_t DLC) { pHeader->StdId = StdId; //11位 標準標識符 pHeader->ExtId = ExtId; //29位 擴展標識符 pHeader->IDE = IDE; //1位 0:標準幀 1:拓展幀 pHeader->RTR = RTR; //1位 0:數據幀 1:遠程幀 pHeader->DLC = DLC; //4位 發送的數據的長度 pHeader->TransmitGlobalTime = ENABLE; } /******************************************************************************* * Function Name : vApp_CAN_Filter_Init * Description : 初始化濾波器 * Input : pFilter 濾波器句柄,初始化全部值 IdHigh, IdLow, MaskIdHigh, MaskIdLow, FIFOAssignment, Bank, Mode, Scale, Activation, SlaveStartFilterBank * Output : None * Return : None ****************************************************************************** */ void vApp_CAN_Filter_Init(CAN_FilterTypeDef * pFilter, uint32_t IdHigh, uint32_t IdLow, uint32_t MaskIdHigh, uint32_t MaskIdLow, uint32_t FIFOAssignment, uint32_t Bank, uint32_t Mode, uint32_t Scale, uint32_t Activation, uint32_t SlaveStartFilterBank) { pFilter->FilterIdHigh = 0; pFilter->FilterIdLow = 0; pFilter->FilterMaskIdHigh = 0; pFilter->FilterMaskIdLow = 0; pFilter->FilterFIFOAssignment = CAN_FILTER_FIFO0; pFilter->FilterBank = 0; pFilter->FilterMode = CAN_FILTERMODE_IDMASK; pFilter->FilterScale = CAN_FILTERSCALE_32BIT; pFilter->FilterActivation = ENABLE; pFilter->SlaveStartFilterBank = 0; }

/*******************************************************************************
* Function Name : vApp_User_CAN_Configuration
* Description : 初始化CAN(用戶修改)
* Input : None
* Output : None
* Return : None
****************************************************************************** */
void vApp_User_CAN_Configuration(void)
{
/*—————– CAN初始化配置 ————————–*/
vApp_CAN_Configuration(&hCAN1_TxHeader, &hCAN1_Filter,
/* TxHeader 句柄配置 */
/* StdId ExtId IDE RTR DLC */
0x12, 0, CAN_ID_STD, CAN_RTR_DATA, 8,
/* Filter 句柄配置 */
/* IdHigh IdLow MaskIdHigh MaskIdLow FIFOAssignment Bank Mode Scale Activation SlaveStartFilterBank */
0, 0, 0, 0, CAN_FILTER_FIFO0, 0, CAN_FILTERMODE_IDMASK, CAN_FILTERSCALE_32BIT, ENABLE, 0);
}

/*******************************************************************************  * Function Name  : vApp_CAN_Configuration  * Description    : CAN初始化配置,配置發送幀頭,配置濾波器  * Input          : (...)  * Output         : None  * Return         : None  ****************************************************************************** */  void vApp_CAN_Configuration(CAN_TxHeaderTypeDef    * pTxHeader,                                                          CAN_FilterTypeDef     * pFilter,                                                          uint32_t                             StdId,                                                          uint32_t                             ExtId,                                                          uint32_t                             IDE,                                                          uint32_t                             RTR,                                                          uint32_t                             DLC,                                                          uint32_t                             IdHigh,                                                          uint32_t                             IdLow,                                                          uint32_t                             MaskIdHigh,                                                          uint32_t                             MaskIdLow,                                                          uint32_t                             FIFOAssignment,                                                          uint32_t                             Bank,                                                          uint32_t                             Mode,                                                          uint32_t                             Scale,                                                          uint32_t                             Activation,                                                          uint32_t                             SlaveStartFilterBank)  {      /*-1- 初始化TxHeader句柄 ----------------------------------------*/      vApp_CAN_TxHeader_Init(pTxHeader, StdId, ExtId, IDE, RTR, DLC);        /*-2- 初始化濾波器句柄 ------------------------------------------*/      vApp_CAN_Filter_Init(pFilter, IdHigh, IdLow, MaskIdHigh, MaskIdLow, FIFOAssignment, Bank, Mode, Scale, Activation, SlaveStartFilterBank);      HAL_CAN_ConfigFilter(&hcan1, pFilter);        /*-3- 啟動CAN ---------------------------------------------------*/      while(HAL_CAN_Start(&hcan1) != HAL_OK )      {          printf("nCAN_Start Failed!!");          HAL_Delay(100);      }      printf("nCAN_Start Success!!");        /*-4- 使能中斷通知 ----------------------------------------------*/      HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);  }

發送的只用調用HAL庫的HAL_CAN_AddTxMessage函數就行了


/*******************************************************************************
* Function Name : vApp_User_CAN1_TxMessage
* Description : 使用CAN1發送數據
* Input : None
* Output : None
* Return : None
****************************************************************************** */
void vApp_User_CAN1_TxMessage(uint8_t aTxData[], uint8_t DLC)
{
vApp_CAN_TxMessage(&hcan1, &hCAN1_TxHeader, aTxData, DLC);
}


/*
****************************************************************************** * Function Name : vApp_CAN_TxMessage * Description : 郵箱發送數據 * Input : hcan pTxHeader 發送幀頭 aData 數據段 DLC 數據段長度 * Output : None * Return : None ****************************************************************************** */ void vApp_CAN_TxMessage(CAN_HandleTypeDef *hcan, CAN_TxHeaderTypeDef * pTxHeader, uint8_t aData[], uint8_t DLC) { uint32_t Tx_MailBox; /*-1- 配置數據段長度 ----------------------------------------*/ pTxHeader->DLC = DLC; /*-2- 發送aData ---------------------------------------------*/ while(HAL_CAN_AddTxMessage(hcan, pTxHeader, aData, &Tx_MailBox) != HAL_OK) { printf("TxMsg Failed!!"); HAL_Delay(100); } printf("nSend Tx Message Success!!Tx_Mail:%d", Tx_MailBox); }

接收是用中斷回調函數void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)來接收資訊,這個是個HAL庫里的弱函數,要自己重寫。

 

/*******************************************************************************  * Function Name  : HAL_CAN_RxFifo0MsgPendingCallback  * Description    : 消息接收回調函數  * Input          : hcan  * Output         : None  * Return         : None  ****************************************************************************** */  void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)  {      uint8_t aRxData[8], i;        if(HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &hCAN1_RxHeader, aRxData) == HAL_OK)      {          printf("nGet Rx Message Success!!nData:");          for(i=0; i<8; i++)              printf("%d", aRxData[i]);      }  }

 

 

 

4.測試

  本次測試用的CAN分析儀是周立功的CANalyst-II,上位機是CANTest,下面介紹一下怎麼用。根據上面計算的波特率,設置為500K,然後點最後的一欄

 

 

 

  單片機主函數程式如下:

 

 

int main(void)  {    /* USER CODE BEGIN 1 */      uint8_t key;      uint8_t TxData[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};    /* USER CODE END 1 */        /* MCU Configuration--------------------------------------------------------*/      /* Reset of all peripherals, Initializes the Flash interface and the Systick. */    HAL_Init();      /* USER CODE BEGIN Init */      /* USER CODE END Init */      /* Configure the system clock */    SystemClock_Config();      /* USER CODE BEGIN SysInit */      /* USER CODE END SysInit */      /* Initialize all configured peripherals */    MX_GPIO_Init();    MX_CAN1_Init();    MX_USART1_UART_Init();    /* USER CODE BEGIN 2 */      vApp_User_CAN_Configuration();  //    KEY_Init();                     //初始化按鍵    /* USER CODE END 2 */      /* Infinite loop */    /* USER CODE BEGIN WHILE */    while (1)    {       vApp_User_CAN1_TxMessage(TxData, 8);      /* USER CODE END WHILE */        /* USER CODE BEGIN 3 */    }    /* USER CODE END 3 */  }

 

 

 

 

 測試結果如下:

發送:

 

接收: