基于STM32F429和HAL库的CAN收发例程

ⅰ亾dé卋堺 提交于 2019-12-01 15:37:16

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

 

 

 

 

 测试结果如下:

发送:

 

接收:

 

 

 

  

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!