学习笔记1~2:CAN通讯配置与接收报文详解

硬件设备

STM32F103C8T6工控板、USB转TLL接口、TLL转CAN串口小板,继电器,降压模块,警示灯

软件工具

CANPro协议分析平台、CubeMX、MDK-ARM

实现过程及功能

基础功能:PC端通过CANPro设置报文,发出串口信号经过USB和串口小板转成CAN信号,在单片机上接收特定ID的报文,通过判断需要接收的首byte数据执行相应操作(LED亮)

进阶功能:通过修改CAN_Filter完成对两种报文的筛选,并设计一个特定报文,选择执行两种报文命令,通过连接继电器和警示灯,更直观的表现出来选择效果。

CubeMX的配置

定义引脚 根据板子的原理图来定义,这里CAN是PB8、PB9,LED是PB14、PB15

CAN的配置,最重要的是使得接收的波特率和发送的信号一致,至于怎么配置,可以查查别人的文章,我记得之前有看到过,我这里是因为后续使用在汽车上测试的,所以设置为500K

时钟的初始化,输入频率看板子上的晶振,在范围内选择。单独使用CAN的话,数值不太重要,高速时钟为72MHz,APB1外围时钟为最大频率即可。

配置完回去检查一下,随后工程管理设置一下,生成代码即可

代码

main.c


/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "can.h"
#include "gpio.h"

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void SystemClock_Config(void);
CAN_HandleTypeDef hcan;
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{ 
  /* MCU Configuration--------------------------------------------------------*/
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
  /* Configure the system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
    MX_GPIO_Init();//引脚初始化函数
    
    MX_CAN_Init();//CAN初始化函数
  /* USER CODE BEGIN 2 *//
    MY_CAN_Filter();//自定义滤波器设置函数
    HAL_CAN_Start(&hcan);//调用HAL启动CAN
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
  }
  /* USER CODE END 3 */
}

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {       
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT

void assert_failed(uint8_t *file, uint32_t line)
{

}
#endif /* USE_FULL_ASSERT */

can.c


/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "can.h"

void MX_CAN_Init(void)
{
  hcan.Instance = CAN1;
  hcan.Init.Prescaler = 12;
  hcan.Init.Mode = CAN_MODE_NORMAL;
  hcan.Init.SyncJumpWidth = CAN_SJW_1TQ;
  hcan.Init.TimeSeg1 = CAN_BS1_3TQ;
  hcan.Init.TimeSeg2 = CAN_BS2_2TQ;
  hcan.Init.TimeTriggeredMode = DISABLE;
  hcan.Init.AutoBusOff = DISABLE;
  hcan.Init.AutoWakeUp = DISABLE;
  hcan.Init.AutoRetransmission = DISABLE;
  hcan.Init.ReceiveFifoLocked = DISABLE;
  hcan.Init.TransmitFifoPriority = DISABLE;
  if (HAL_CAN_Init(&hcan) != HAL_OK)
  {
    Error_Handler();
  }
}

void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(canHandle->Instance==CAN1)
  {
    /* CAN1 clock enable */
    __HAL_RCC_CAN1_CLK_ENABLE();

    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**CAN GPIO Configuration
    PB8     ------> CAN_RX
    PB9     ------> CAN_TX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_8;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    __HAL_AFIO_REMAP_CAN1_2();

    /* CAN1 interrupt Init *///这里也可以设置一下,在cubemx里NVIC
    HAL_NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, 0, 0);//没设置就直接添加
    HAL_NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
  }
}

void HAL_CAN_MspDeInit(CAN_HandleTypeDef* canHandle)
{
  if(canHandle->Instance==CAN1)
  {
    /* Peripheral clock disable */
    __HAL_RCC_CAN1_CLK_DISABLE();
    /**CAN GPIO Configuration
    PB8     ------> CAN_RX
    PB9     ------> CAN_TX
    */
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_8|GPIO_PIN_9);
    /* CAN1 interrupt Deinit */
    HAL_NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn);
  }
}

/* USER CODE BEGIN 1 */
    CAN_FilterTypeDef  CAN_FilterStructure;//过滤器结构体
static    CAN_RxHeaderTypeDef  RxMessage;//接收的结构体
void MY_CAN_Filter(void)
{
    uint32_t mask;

    CAN_FilterStructure.FilterBank=0;
    CAN_FilterStructure.FilterMode=CAN_FILTERMODE_IDMASK;
    CAN_FilterStructure.FilterScale=CAN_FILTERSCALE_32BIT;
    CAN_FilterStructure.FilterIdHigh=0x0018<<5;//CAN报文标准帧识别是11位,向左移5位,变为16位
    CAN_FilterStructure.FilterIdLow=CAN_ID_STD;//我的需求是标准帧,扩展帧,远程帧就没设置了
    
    mask = 0x0018;//用于筛选ID
    mask =~mask;
    mask<<=3;
    mask |=0x02;
    CAN_FilterStructure.FilterMaskIdHigh=(mask>>16)&0xffff;
    CAN_FilterStructure.FilterMaskIdLow=mask&0xffff;//筛选模式可以设置,例如mask&0x0000
    CAN_FilterStructure.FilterFIFOAssignment=CAN_FILTER_FIFO0;
    CAN_FilterStructure.FilterActivation=CAN_FILTER_ENABLE;
    CAN_FilterStructure.SlaveStartFilterBank=0;
    HAL_CAN_ConfigFilter(&hcan,&CAN_FilterStructure);
    HAL_CAN_ActivateNotification(&hcan,CAN_IT_RX_FIFO0_MSG_PENDING);
}
//接收回调函数
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{  
    uint8_t data[8];//根据所需要发送的报文byte决定
    RxMessage.DLC=8;
    RxMessage.StdId=0x18;//接收报文的ID
    RxMessage.RTR=CAN_ID_STD;//ID类型如上,标准帧,扩展帧等等可选
    RxMessage.IDE=CAN_RTR_DATA;
    
     if (HAL_CAN_GetState(hcan)!= RESET)
     {
            HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxMessage, data);        
         
         if(data[0] == 0x00)
         {
             HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14,GPIO_PIN_RESET);
             HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15,GPIO_PIN_SET);
         }                 
   }
}

/* USER CODE END 1 */

gpio.c

/* Includes ------------------------------------------------------------------*/
#include "gpio.h"
/*----------------------------------------------------------------------------*/
/* Configure GPIO                                                             */
/*----------------------------------------------------------------------------*/
void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_RESET);

  /*Configure GPIO pins : PB14 PB15 */
  GPIO_InitStruct.Pin = GPIO_PIN_14|GPIO_PIN_15;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
/* USER CODE BEGIN 2 */
void GPIO_Init()
{
    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_14,GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_SET);
}
/* USER CODE END 2 */

当然,也可以实现单片机之间的相互发送报文,再定义一下 CAN_RxHeaderTypeDef结构体即可。这里由于我们是PC端发报文,不多此一举了。在实际的应用中可以加入CAN_Debug,Receive_Debug等等

下一章:记录一下我对CAN总线/CAN协议/CAN报文的理解。

与君共勉!!!

进阶功能

记得配置输出引脚

修改的can.c部分一样的系统代码就不放了

/*Define filter function*/
void MY_CAN_Filter(void)
{
  uint16_t mask,num,tmp,i;
    uint16_t STDIDArray[3]={0x18,0x559,0x666};//
//    /*0x599 Filter setting*/
    CAN_FilterStructure.FilterBank=0;//Set filter 0,ranges from 0 to 13
    CAN_FilterStructure.FilterMode=CAN_FILTERMODE_IDMASK;//Mask mode
    CAN_FilterStructure.FilterScale=CAN_FILTERSCALE_32BIT;//The bit width is set to 32
    CAN_FilterStructure.FilterIdHigh=STDIDArray[1]<<5;//Sets the identifier register height byte
    CAN_FilterStructure.FilterIdLow=0;//The low byte is set to 0
    
    mask=0xfff;//Start calculating the mask code,
    num=sizeof(STDIDArray)/sizeof(STDIDArray[1]);
    
    for(i=0;i<num;i++)//Mask the same or result of all members of the StdIdArray[] array
    {
        tmp = STDIDArray[i]^(~STDIDArray[1]);//All array members operate with the 0th member
        mask &=tmp;
    }
    CAN_FilterStructure.FilterMaskIdHigh=(mask<<5);
    CAN_FilterStructure.FilterMaskIdLow=0|0x02;//Only data frames are received
    CAN_FilterStructure.FilterFIFOAssignment=CAN_FILTER_FIFO0;//Set the data frame that passes into FIFO0
    CAN_FilterStructure.FilterActivation=CAN_FILTER_ENABLE;
    HAL_CAN_ConfigFilter(&hcan,&CAN_FilterStructure);
    HAL_CAN_ActivateNotification(&hcan,CAN_IT_RX_FIFO0_MSG_PENDING);
//    
}


int flag=0;//Set flag bits to select execution
CAN_Receive can_receive;//Custom structure,Check in stm32f1xx_hal_can_h.//Note that modifying it in CUNBEMX causes the struct to be lost
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)//Receive the callback function
{         
     if (HAL_CAN_GetState(hcan)!= RESET)
     {
            HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &can_receive.RxMessage,can_receive.data);    //Receive data in FIFO0

         if(can_receive.RxMessage.StdId==0x666)//Determines whether to control the packet ID
         {    
             if(can_receive.data[0]==0x10)//The first byte is 0x10, and 0x18 is assigned to flag
             {flag =0x18;}
             if(can_receive.data[0]==0x20)//The first byte is 0x20, and 0x18 is assigned to flag
             {flag =0x599;}
         }
        /*The flag is 0x18 and the ID of the received packet is 0x18,Switch to single-node mode*/
         if(flag==0x18)
         {
            if(can_receive.RxMessage.StdId==0x18)
             {Single_node_model();}
         }
         /*The flag is 0x559 and the ID of the received packet is 0x559,Switch to BX1E mode*/
         if(flag==0x599)
         {
            if(can_receive.RxMessage.StdId==0x559)
             {BX1E();}
         }

     }
     HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING);
}
/*Single node mode execution function */
void Single_node_model(void)
{
    uint8_t TMP_Twobit;//Set the variable to take the first two bits of the second byte
    TMP_Twobit=can_receive.data[1];
    TMP_Twobit>>=6;
         if(can_receive.data[0] == 0x19)//Currently ten bits are 100 in decimal
         {
             if(TMP_Twobit==0x00)
             {//PB-LED  Used for warning lamp faults
                HAL_GPIO_WritePin(GPIOB,GPIO_PIN_14,GPIO_PIN_RESET);//PB14-LEDl on
              HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_SET);//PB15-LED2 off
              HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2 | GPIO_PIN_3,GPIO_PIN_RESET);//Yellow light 
              HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4 | GPIO_PIN_1,GPIO_PIN_SET);
          }
      }
         if(can_receive.data[0] == 0x19)//Currently ten bits are 101 in decimal
         {
             if(TMP_Twobit==0x01)
            {
             HAL_GPIO_WritePin(GPIOB,GPIO_PIN_14,GPIO_PIN_SET);//PB14-LEDl off
             HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_RESET);//PB15-LED2 on
           HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);//green light
             HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1 | GPIO_PIN_3| GPIO_PIN_4 ,GPIO_PIN_SET);
            }
        }
                  
         if(can_receive.data[0] == 0xe1)//Currently ten bits are 900 in decimal
         {
             if(TMP_Twobit==0x00)
             {
                HAL_GPIO_WritePin(GPIOB,GPIO_PIN_14,GPIO_PIN_RESET);//PB14-LED1 on
            HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_RESET);//PB15-LED2 on
              HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3|GPIO_PIN_4,GPIO_PIN_RESET);// The buzzer sounded and red light
              HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1 | GPIO_PIN_2  ,GPIO_PIN_SET);
         }
           }    
}
/*BX1E mode execution function*/
void BX1E(void)
{
            uint8_t TMP_FB;//Define a variable to take the first four bits of the first byte
            TMP_FB=can_receive.data[0];
            TMP_FB>>=4;
            
             if(TMP_FB==0x0)//the first four bits are 0 in decimal
         {
            HAL_GPIO_WritePin(GPIOB,GPIO_PIN_14,GPIO_PIN_RESET);    //PB14-LED1 on
            HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_SET);    //PB15-LED2 off
            HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2 | GPIO_PIN_3,GPIO_PIN_RESET);//Yellow light 
            HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4 | GPIO_PIN_1,GPIO_PIN_SET);
         }
         if(TMP_FB==0x1)//the first four bits are 1 in decimal
         {
            HAL_GPIO_WritePin(GPIOB,GPIO_PIN_14,GPIO_PIN_SET);//PB14-LED1 off
            HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_RESET);//PB15-LED2 on
            HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);//Green light
            HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1 | GPIO_PIN_3| GPIO_PIN_4 ,GPIO_PIN_SET);
         }
         if(TMP_FB==0x2)//the first four bits are 2 in decimal
         {
            HAL_GPIO_WritePin(GPIOB,GPIO_PIN_14,GPIO_PIN_RESET);//PB14-LED1 on
          HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_RESET);//PB15-LED2 on
            HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3|GPIO_PIN_4,GPIO_PIN_RESET);//The buzzer sounded and red light
            HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1 | GPIO_PIN_2  ,GPIO_PIN_SET);
         }
}
/* USER CODE END 1 */

下面的结构体不定义也可以,定义了CAN_Receive,方便查找

typedef struct CAN_Receive
{
  CAN_RxHeaderTypeDef RxMessage;
    
    uint8_t data[8];//Received message
}CAN_Receive;

实际效果

0 0x00000666 1s 橙色

1 0x00000018 1s 黄色

2 0x00000018 1s 绿色

3 0x00000018 1s 红色+蜂鸣器

4 0x00000559 1s 红色+蜂鸣器

5 0x00000666 1s 红色+蜂鸣器

6 0x00000559 1s 红色+蜂鸣器

7 0x00000559 1s 绿色

8 0x00000559 1s 黄色

不知道怎么上传视频,但是实际效果符合预期。

物联沃分享整理
物联沃-IOTWORD物联网 » 学习笔记1~2:CAN通讯配置与接收报文详解

发表评论