1. 程式人生 > >基於STM32F429和HAL庫的CAN收發例程

基於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口。

記得改一下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 */
}

 

 

 

 

 測試結果如下:

傳送:

 

接收:

 

 

 

  

&n