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[