CAN编程实践指南:从入门到实战

1、CAN基本驱动步骤

(1)CAN参数初始化

  • 工作模式、波特率等
  • 函数:HAL_CAN_Init
  • (2)使能CAN时钟和初始化相关引脚

  • GPIO模式设为复用功能模式
  • 函数:HAL_CAN_MspInit(CAN的初始化回调函数)
  • (3)设置过滤器

  • 过滤器的配置
  • 函数:HAL_CAN_ConfigFilter
  • (4)CAN数据的接收和发送

  • HAL_CAN_AddTxMessage  发送消息
  • HAL_CAN_GetRxMessage  接收数据
  • (5)使能CAN相关中断/设置NVIC/编写中断服务函数

  • NVIC:中断控制器
  • __HAL_CAN_ENABLE_IT (可选)
  • 2、开发环境

    (1)KeilMDK:V5.38.0.0

    (2)STM32CubeMX:V6.8.1

    (3)MCU:STM32F407ZGT6

    3、实验目的

    (1)使用回环模式实现自发自收。

    (2)CAN发送数据,然后接收数据,将接收到的数据通过串口发送出去。

    4、原理图

    (1)CAN芯片选择TJA1040。

    (2)CAN_TX接PA12,CAN_RX接PA11。

    5、STM32CubeMX创建工程及配置

    5.1、补充内容

    (1)查阅数据手册,CAN外设接在总线APB1上,时钟频率此处配置为36MHz。

    (2)CubeMX创建工程、配置时钟、串口不做详细介绍。

    5.2、CubeMX中CAN的配置

    (1)使能CAN外设。

    (2)配置CAN的参数

  • Bit Timings Parameters:位时序参数
  • Prescaler:分频系数
  • Time Quanta in Bit Segment 1:时间段1(配置为9标识9个时间单元)
  • Time Quanta in Bit Segment 2:时间段2
  • ReSynchronization Jump Width:重新同步跳跃宽度
  • TS1=8、TS2=7、BRP=3,波特率 = 36000 / [( 9 + 8 + 1 ) * 4] = 500Kbps
  • Basic Parameters:基本参数
  • Time Triggered Communication Mode:时间触发通信方式
  • Automatic Bus-Off Management:总线自动离线管理
  • Automatic Wake-Up Mode:自动唤醒模式
  • Automatic Retransmission:自动重发
  • Receive Fifo Locked Mode:接收Fifo锁定模式
  • Transmit Fifo Priority:发送Fifo优先级
  • Advanced Parameters:先进的参数
  • Operating Mode:操作模式
  • 6、KeilMDK软件编写

    6.1、CAN相关函数

    CAN_TxHeaderTypeDef g_can1_txheader;    /* CAN发送结构体 */
    CAN_RxHeaderTypeDef g_can1_rxheader;    /* CAN接收结构体 */
    CAN_HandleTypeDef hcan1;     // CAN控制句柄
    
    /* CAN1 init function */
    void MX_CAN1_Init(void)
    {
      hcan1.Instance = CAN1;
      hcan1.Init.Prescaler = 4;                  /* 分频系数 */            
      hcan1.Init.Mode = CAN_MODE_LOOPBACK;       /* 工作模式设置 环回模式:自发自收 */
      hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;    /* 重新同步跳跃宽度 */
      hcan1.Init.TimeSeg1 = CAN_BS1_9TQ;         /* 时间段1 */
      hcan1.Init.TimeSeg2 = CAN_BS2_8TQ;         /* 时间段2 */
      hcan1.Init.TimeTriggeredMode = DISABLE;    /* 禁止时间触发通信模式 */
      hcan1.Init.AutoBusOff = DISABLE;           /* 禁止自动离线管理 */
      hcan1.Init.AutoWakeUp = DISABLE;           /* 禁止自动唤醒 */
      hcan1.Init.AutoRetransmission = DISABLE;   /* 禁止自动重发 */
      hcan1.Init.ReceiveFifoLocked = DISABLE;    /* 禁止接收FIFO锁定 */
      hcan1.Init.TransmitFifoPriority = DISABLE; /* 禁止发送FIFO优先级 */
      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);
    
      /* USER CODE BEGIN CAN1_MspInit 1 */
    
      /* USER CODE END CAN1_MspInit 1 */
      }
    }
    
    
    /*
    **功能:CAN过滤器配置
    **参数:无
    **返回值:无
     */
    void can_filter_config(void)
    {
    	CAN_FilterTypeDef can_filterconfig;
        /* 过滤器是接收所有报文,不筛选 */
        can_filterconfig.FilterMode = CAN_FILTERMODE_IDMASK;   /* 过滤器模式:标识符掩码模式(屏蔽位模式)*/
        can_filterconfig.FilterScale = CAN_FILTERSCALE_32BIT;  /* 过滤器位宽:32位位宽 */
       
    	//STID[10:3] STID[2:0] EXID[17:13]         EXID[12:5] EXID[4:0] IDE RTR 0
    	can_filterconfig.FilterIdHigh = 0;        /* ID高字节 */               
        can_filterconfig.FilterIdLow  = 0;        /* ID低字节 */
        can_filterconfig.FilterMaskIdHigh = 0;    /* 掩码高字节 */
        can_filterconfig.FilterMaskIdLow  = 0;    /* 掩码低字节 */
    	
        can_filterconfig.FilterBank = 0;                         /* 选择过滤器组 */
        can_filterconfig.FilterFIFOAssignment = CAN_FilterFIFO0; /* 过滤器关联FIFO */
        can_filterconfig.FilterActivation = CAN_FILTER_ENABLE;   /* 过滤器使能 */
        can_filterconfig.SlaveStartFilterBank = 14;
        HAL_CAN_ConfigFilter(&hcan1, &can_filterconfig);
        
    }
    
    /* 发送消息数据函数 */
    void can_send_message(uint32_t id, uint8_t *buf, uint8_t len)
    {
    
        uint32_t tx_mail = CAN_TX_MAILBOX0;  /* 发送邮箱 */
        
        g_can1_txheader.ExtId = id;          /* 扩展标识符 */
        g_can1_txheader.DLC = len;           /* 数据长度 */
        g_can1_txheader.IDE = CAN_ID_EXT;    /* 帧格式(标准帧或扩展帧) */
        g_can1_txheader.RTR = CAN_RTR_DATA;  /* 帧类型(数据帧或远程帧) */
        
        HAL_CAN_AddTxMessage(&hcan1, &g_can1_txheader, buf, &tx_mail);
        // 等待发送完成
        while(HAL_CAN_GetTxMailboxesFreeLevel(&hcan1) != 3);
    }
    
    /* 接收数据函数 */
    uint8_t can_receive_message(uint8_t *buf)
    {
        if (HAL_CAN_GetRxFifoFillLevel(&hcan1, CAN_RX_FIFO0) == 0)
        {
            return 0;
        }
        
        HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &g_can1_rxheader, buf);
        
    		// 返回接收数据长度
        return g_can1_rxheader.DLC;
    }

    6.2、main.c部分代码

    #include "can.h"
    
    
    int main(void)
    {
        /* USER CODE BEGIN 1 */
        uint8_t can_sen_buf[8] = {0, 1, 2, 3, 4, 5, 6, 7};  // can发送数据
        uint8_t can_rec_len = 0;                            // can接收数据长度
        uint8_t can_rec_buf[8] = {0};                       // can接收数据缓冲区
       
    
        MX_CAN1_Init();
        /* USER CODE BEGIN 2 */
    	// 1、已经使能CAN时钟和初始化CAN
    	// 2、配置CAN接收过滤器
    	can_filter_config();
    	// 3、启动CAN设备
    	HAL_CAN_Start(&hcan1);
        while (1)
        {
    	    printf("hello world\r\n");
    	    can_send_message(0xF0000000, can_sen_buf, 8);
    	    can_rec_len = can_receive_message(can_rec_buf);
            
            if (can_rec_len)
    	    {
    		    for (uint8_t i = 0; i < can_rec_len; i++)
    		    {
    			    printf("%x ",can_rec_buf[i]);
    		    }
    		    printf("\r\n");
    	    }
        }
    }

    6.3、完整工程下载地址

    (1)完整工程存储再码云。

    (2)STM32_CSDN: CSDN中STM32专栏的所有示例代码

    物联沃分享整理
    物联沃-IOTWORD物联网 » CAN编程实践指南:从入门到实战

    发表评论