STM32G431 开发USB2CANFD调试工具盒子

1、前言

开发CAN的时候,一直使用Canable的开源硬件,尤其是刷了PCAN的固件后,性价比真是太高了。后来需要开发CANFD的产品,就买了Canable2.0的开源软硬件,刷了slcanfd固件 (非常可惜没有PCAN FD固件的开源),体验使用下来实在是差强人意,就萌生了用Canable2.0的硬件自己开发下USB2CANFD的调试工具。

2、硬件资源

STM32G431CBT6 + TJA1050

  • 一路全速USB: 一次最多传输64字节,这就导致在传输DLC=64的CANFD的数据时就需要分包处理才行 (PA12/PA11)
  • 一路CANFD控制器:PB8/PB9
  • 2个LED灯:PA0/PA15
    在这里插入图片描述

3、软件工具

开发环境:STM32CubeIDE 1.14.1 HAL库版本: FW_G4_V1.5.2
使用到组件:

  • USB : 使用USB CDC 虚拟串口功能
  • FDCAN1
  • FreeRTOS

4、固件实现的功能目标

  • 1、支持上位机打开/关闭USB2CANFD功能
  • 2、支持设置CAN/CANFD模式,支持设置常用波特率配置,
  • 3、支持接收/发送CAN/CANFD帧,标准帧/拓展帧,支持开启BRS
  • 4、支持配置1组标准帧的硬件ID过滤范围、1组拓展帧的硬件ID过滤范围
  • 5、支持接收/发送指示灯LED的闪烁显示

5、软件设计思路

在这里插入图片描述

接收到CAN数据转换成协议数据后,专门设计了1个通过USB虚拟串口发送数据给上位机的Task, 主要是控制USB向上位机发送协议数据的频率,最多1ms发送一次协议数据包,这样做主要是为了降低上位机处理串口数据的难度,避免因为上位机因为处理接收数据异常,导致显示CAN的报文的丢帧。

6、USB2CANFD 自定义传输控制协议

6.1 设备打开/关闭-串口数据协议

在这里插入图片描述

6.2 设备传输CAN(FD)报文串口协议

在这里插入图片描述

其中CAN类型的规定如下:
在这里插入图片描述

7、USB虚拟串口CDC配置

STM32中USB中间件配置成虚拟串口的使用还是比较简单的,前提先配置好USB的时钟源为48MHZ
在这里插入图片描述

使能USB外设,配置PIN脚,中断
在这里插入图片描述

然后中间件这里选择CDC的类即可
在这里插入图片描述

完成以上配置,main函数中调用初始化后,我们的设备就能在电脑上被正确的枚举成串口。

MX_USB_Device_Init();

在这里插入图片描述

我们只要关心以下2个API的使用即可,在usbd_cdc_if.c文件中
USB数据接收中断回调函数CDC_Receive_FS中,我们自己在这里面实现协议数据的接收处理逻辑

static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{

  /* USER CODE BEGIN 6 */
  
  // 这里写自己的数据接收处理逻辑

  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);

  USBD_CDC_ReceivePacket(&hUsbDeviceFS);

  return (USBD_OK);

  /* USER CODE END 6 */

}

USB发送接口函数:需要发送数据时,直接调用即可

uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len)

8、FDCAN1配置

同样是配置好时钟源,然后引脚、中断配置,至于CANFD其他参数如波特率/采样率的配置,随意配置即可,因为我们后面会重新写接口来通过上位机来控制这些参数的配置
在这里插入图片描述

这里附上CANFD外设初始化代码配置,上位机发送打开CAN的功能指令后,下位机调用CanFd_if_Start的接口函数接口,这里面主要是配置了CAN的工作模式,波特率,采样率,然后依次配置硬件过滤、注册FIFO0/FIFO1的中断回调函数、BUSOFF的中断回调函数。

// --------------------------------------------------------------------------------------------------------------------

/// \brief CanFd_if_Start

// --------------------------------------------------------------------------------------------------------------------

void CanFd_if_Start(CANFD_PARA_SET_U CanFd_Para)

{

    //设置波特率

  

    const uint8_t CanBaud = CanFd_Para.PARA.CanBaud;

    const uint8_t CanMode = CanFd_Para.PARA.CanMode;

    const uint8_t CanDataBaud = CanFd_Para.PARA.CanDataBaud;

  

    const uint32_t StdFileterId1 = LDR32_BIG(&CanFd_Para.PARA.StdFileterId1[0]);

    const uint32_t StdFileterId2 = LDR32_BIG(&CanFd_Para.PARA.StdFileterId2[0]);

    const uint32_t ExtFileterId1 = LDR32_BIG(&CanFd_Para.PARA.ExtFileterId1[0]);

    const uint32_t ExtFileterId2 = LDR32_BIG(&CanFd_Para.PARA.ExtFileterId2[0]);

  
  

    uint32_t NominalPrescaler = 0;

    uint32_t DataPrescaler = 0;

  

    HAL_FDCAN_DeInit(&hfdcan1);

    // 81.25%

    switch (CanBaud)

    {

        case 0: //125k

            NominalPrescaler = 48;

            break;

        case 1: //250K

            NominalPrescaler = 24;

            break;

        case 2: //500K

            NominalPrescaler = 12;

            break;

        case 3: //1M

            NominalPrescaler = 6;

            break;

        default:

            NominalPrescaler = 12;

            break;

    }

    // 75%

    switch (CanDataBaud)

    {

        case 0: // 1M

            DataPrescaler = 12;

        break;

  

        case 1: // 2M

            DataPrescaler = 6;

        break;

  

        case 2: // 4M

            DataPrescaler = 3;

        break;

        default:

            break;

    }

  
  

    switch (CanMode)

    {

        case 0: //classic can

            hfdcan1.Init.FrameFormat = FDCAN_FRAME_CLASSIC;

            break;

        case 1: //canfd

            hfdcan1.Init.FrameFormat = FDCAN_FRAME_FD_NO_BRS;

            break;

        case 2: //canfd brs

            hfdcan1.Init.FrameFormat = FDCAN_FRAME_FD_BRS;

            break;

        default:

            hfdcan1.Init.FrameFormat = FDCAN_FRAME_FD_BRS;

            break;

    }

  
  

    hfdcan1.Instance = FDCAN1;

    hfdcan1.Init.ClockDivider = FDCAN_CLOCK_DIV1;

    hfdcan1.Init.Mode = FDCAN_MODE_NORMAL;

    hfdcan1.Init.AutoRetransmission = ENABLE;

    hfdcan1.Init.TransmitPause = DISABLE;

    hfdcan1.Init.ProtocolException = DISABLE;

    // 波特率和采样率计算

    // FDCAN 时钟频率 FDCLK 96MHZ

    // baud = FDCLK / Prescaler / (1 + TimeSeg1 + TimeSeg2) eg. 96 / 12 / (1+12+3) = 0.5

    // sample = ( 1 +  TimeSeg1) /  (1 +  TimeSeg1 +  TimeSeg2) eg. (1+12) / (1+12+3) = 81.25%

    // SyncJumpWidth:

    // 81.25%

    hfdcan1.Init.NominalPrescaler = NominalPrescaler;  // 96 / 12 = 8

    hfdcan1.Init.NominalSyncJumpWidth = 3;

    hfdcan1.Init.NominalTimeSeg1 = 12;

    hfdcan1.Init.NominalTimeSeg2 = 3;  // 8 / (1 + 12 + 3) = 0.5

    // 75%

    hfdcan1.Init.DataPrescaler = DataPrescaler;   // 96 / 6 = 16

    hfdcan1.Init.DataSyncJumpWidth = 2;

    hfdcan1.Init.DataTimeSeg1 = 5;

    hfdcan1.Init.DataTimeSeg2 = 2;  // 16 / (1 + 5 + 2)  = 2

  

    hfdcan1.Init.StdFiltersNbr = 28;

    hfdcan1.Init.ExtFiltersNbr = 8;

  

    hfdcan1.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION;

    if (HAL_FDCAN_Init(&hfdcan1) != HAL_OK)

    {

        Error_Handler();

    }

  

    CanFd_if_Canfd1Config(StdFileterId1,StdFileterId2,ExtFileterId1,ExtFileterId2);

}

// --------------------------------------------------------------------------------------------------------------------

/// \brief CanFd_if_Canfd1Config

// --------------------------------------------------------------------------------------------------------------------

void CanFd_if_Canfd1Config(uint32_t std_filterid1, uint32_t std_filterid2, uint32_t ext_filterid1, uint32_t ext_filterid2)

{

    sFilterConfig1.IdType = FDCAN_STANDARD_ID;

    sFilterConfig1.FilterIndex = 0;

    sFilterConfig1.FilterType = FDCAN_FILTER_RANGE;

    sFilterConfig1.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;

    sFilterConfig1.FilterID1 = std_filterid1;

    sFilterConfig1.FilterID2 = std_filterid2;

    if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig1) != HAL_OK)

    {

        Error_Handler();

    }

  

   sFilterConfig1.IdType = FDCAN_EXTENDED_ID;

   sFilterConfig1.FilterIndex = 0;

   sFilterConfig1.FilterType = FDCAN_FILTER_RANGE;

   sFilterConfig1.FilterConfig = FDCAN_FILTER_TO_RXFIFO1;

   sFilterConfig1.FilterID1 = ext_filterid1;

   sFilterConfig1.FilterID2 = ext_filterid2;

   if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig1) != HAL_OK)

   {

       Error_Handler();

   }

  

    /* Configure global filter on both FDCAN instances:

    Filter all remote frames with STD and EXT ID

    Reject non matching frames with STD ID and EXT ID */

    if (HAL_FDCAN_ConfigGlobalFilter(&hfdcan1, FDCAN_REJECT, FDCAN_REJECT, FDCAN_FILTER_REMOTE, FDCAN_FILTER_REMOTE) != HAL_OK)

    {

        Error_Handler();

    }

  

    /* Activate Rx FIFO 0 new message notification on both FDCAN instances */

    if (HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0) != HAL_OK)

    {

        Error_Handler();

    }

  

    if (HAL_FDCAN_ConfigRxFifoOverwrite(&hfdcan1, FDCAN_RX_FIFO0, FDCAN_RX_FIFO_OVERWRITE) != HAL_OK)

    {

        Error_Handler();

    }

  

    /* Activate Rx FIFO 1 new message notification on both FDCAN instances */

    if (HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO1_NEW_MESSAGE, 0) != HAL_OK)

    {

        Error_Handler();

    }

  

    if (HAL_FDCAN_ConfigRxFifoOverwrite(&hfdcan1, FDCAN_RX_FIFO1, FDCAN_RX_FIFO_OVERWRITE) != HAL_OK)

    {

        Error_Handler();

    }

    /* Activate busoff notification on both FDCAN instances */

    if (HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_BUS_OFF, 0) != HAL_OK)

    {

        Error_Handler();

    }

  

    /* Configure and enable Tx Delay Compensation, required for BRS mode.

            TdcOffset default recommended value: DataTimeSeg1 * DataPrescaler

            TdcFilter default recommended value: 0 */

    // HAL_FDCAN_ConfigTxDelayCompensation(&hfdcan1, hfdcan1.Init.DataPrescaler * hfdcan1.Init.DataTimeSeg1, 0);

    // HAL_FDCAN_EnableTxDelayCompensation(&hfdcan1);

  

    HAL_FDCAN_Start(&hfdcan1);

}

9、FREERTOS的配置

打开FreeRtos的中间件,选择API的版本为V2
在这里插入图片描述

创建任务,队列,给每个任务配置堆栈大小,修改Task的优先级
在这里插入图片描述

创建Event, 因为我们会在USB/CAN的中断回调中设置Event事件, 我们要注意配置FreeRTOS的系统中断优先级不能低于USB/CAN的系统中断优先级(值越小系统中断优先级越高),否则在USB/CAN中断中调用事件置位的API会导致FreeRTOS挂了。
在这里插入图片描述
在这里插入图片描述

配置完这些,main.c里初始化时是自动生成创建这些Task,Event,Queue的代码。

/* creation of Can2UsbCmdQueue */

  Can2UsbCmdQueueHandle = osMessageQueueNew (35, sizeof(CAN2USB_CMD_T), &Can2UsbCmdQueue_attributes);

  

  /* creation of CanIfQueue */

  CanIfQueueHandle = osMessageQueueNew (30, sizeof(S_CanIf_RxMsg_t), &CanIfQueue_attributes);

  

  /* creation of Usb2CanCmdQueue */

  Usb2CanCmdQueueHandle = osMessageQueueNew (70, sizeof(USB2CANFD_CMD_U), &Usb2CanCmdQueue_attributes);

  

  /* USER CODE BEGIN RTOS_QUEUES */

  /* add queues, ... */

  /* USER CODE END RTOS_QUEUES */

  

  /* Create the thread(s) */

  /* creation of defaultTask */

  defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);

  

  /* creation of ReciveMsgTask */

  ReciveMsgTaskHandle = osThreadNew(StartReciveMsgTask, NULL, &ReciveMsgTask_attributes);

  

  /* creation of SendMsgTask */

  SendMsgTaskHandle = osThreadNew(StartSendMsgTask, NULL, &SendMsgTask_attributes);

  

  /* creation of LedFlashTask */

  LedFlashTaskHandle = osThreadNew(StartLedFlashTask, NULL, &LedFlashTask_attributes);

  

  /* USER CODE BEGIN RTOS_THREADS */

  /* add threads, ... */

  /* USER CODE END RTOS_THREADS */

  

  /* Create the event(s) */

  /* creation of LedEvent */

  LedEventHandle = osEventFlagsNew(&LedEvent_attributes);

  

  /* creation of ReceiveEvent */

  ReceiveEventHandle = osEventFlagsNew(&ReceiveEvent_attributes);

  

  /* creation of SendEvent */

  SendEventHandle = osEventFlagsNew(&SendEvent_attributes);

我们自己写下Task的实体就行,例如事件等待型的Task

// --------------------------------------------------------------------------------------------------------------------

/// brief: StartReciveMsgTask

/// param:

/// note:

// --------------------------------------------------------------------------------------------------------------------

void StartReciveMsgTask(void *argument)

{

    uint32_t event;

  

    for(;;)

    {

        event = osEventFlagsWait(ReceiveEventHandle,            

                                 RECE_EVENT_ALL,                    

                                 osFlagsWaitAny,                

                                 osWaitForever);                

        if((event & RECE_INI_EVENT) == RECE_INI_EVENT)

        {

        }

  

        if((event & RECE_USB_EVENT) == RECE_USB_EVENT)

        {

            ReciveMsgTaskUsbHandle();

        }

  

        if((event & RECE_CAN_EVENT) == RECE_CAN_EVENT)

        {

            ReciveMsgTaskCanHandle();

            SendMsgTaskSetCanEvent();

            LedFlashTaskSetRxLedEvent();

            vTaskDelay(1);

        }

  

        //osEventFlagsClear(ReceiveEventHandle,RECE_EVENT_ALL);

    }

}

10、逻辑功能代码编写

剩下的就是逻辑功能代码编写了,源码比较多,不好贴,我上传了源代码,感兴趣的可以下载学习参考。
https://download.csdn.net/download/yinzimu/89497901

11、功能验证

使用python开发上位机验证了USB2CANFD盒子的功能,使用 CANFD 500K/2M 配置,进行512K大小的固件进行FOTA测试,36服务每次传输4K数据,整个刷机过程还是非常稳定的。
在这里插入图片描述

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飞翔的汽车人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值