基於STM32F429和HAL庫的CAN收發例程

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口。oop

記得改一下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發送消息句柄),而後ui

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

 
 

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 */
}

 

 

 

 

 測試結果以下:

發送:

 

接收:

 

 

相關文章
相關標籤/搜索