简介:本资源是面向嵌入式开发者的 STM32F4 系列 CAN 通信固件实现方案,专为 XCAN PRO/PRO FD/FD USB2CAN 硬件适配设计,适用于基于 STM32F407/405/417/415 的自定义电路板,解决 Linux/Windows 下 CAN 设备即插即用与协议兼容难题。压缩包含 1061 个文件,主体为 600 个 C 源码与 296 个头文件(构建底层驱动与 SocketCAN 接口),辅以 78 个汇编启动文件、46 个 IAR 链接脚本及数学库静态库(如 libarm_cortexM4lf_math.a)等,完整覆盖硬件初始化、双 CAN 控制器配置、USB CDC 通信与跨平台驱动适配,包体大小为 31.24MB。已有 1076 人学习下载,提供开箱即用的 Linux SocketCAN 支持、PEAK PCAN-View 及 BUSMASTER 兼容能力,并明确标注各 LED 与 CAN/USB 引脚映射关系,便于快速移植与调试。
1. 项目概述:从零构建一个工业级USB-CAN适配器
最近在做一个工控项目,需要把几台老旧的设备通过CAN总线连到上位机做数据采集和诊断。市面上现成的USB转CAN适配器要么太贵,要么驱动兼容性差,在Linux下尤其折腾。于是琢磨着自己动手,基于手头富余的STM32F4开发板,实现一个功能完整、性能可靠的USB2CAN适配器固件,并且要原生支持Linux下的SocketCAN框架。这样一来,上位机软件几乎不用改,直接用标准的SocketCAN API就能收发CAN报文,开发和部署都省心不少。
这个项目,我称之为“XCAN PRO”固件实现,名字听着唬人,其实核心目标就一个:让一块普通的STM32F4开发板,通过USB接口,变身成一个稳定、高速、即插即用的CAN网络分析仪或网关。它需要兼容经典CAN(CAN 2.0 A/B)和CAN FD(灵活数据速率),以应对从汽车诊断到工业控制的不同场景。最终,你得到的不仅仅是一段能跑的代码,而是一个从硬件连接、固件架构、驱动适配到应用测试的完整解决方案。无论你是嵌入式软件工程师想深入理解USB和CAN协议栈,还是自动化工程师需要定制数据采集工具,这个项目都能给你提供清晰的路径和可复现的细节。
2. 核心需求与方案选型背后的逻辑
为什么选择STM32F4和SocketCAN这个组合?这背后是一系列权衡和实际工程需求的考量。
2.1 硬件基石:为什么是STM32F4?
STM32F4系列,尤其是像F407、F429这些型号,几乎是这类桥接应用的“标准答案”。首先,它拥有全速USB OTG(On-The-Go)控制器,这对于实现一个稳定的、免驱(或使用标准类驱动)的USB设备至关重要。我们不需要依赖额外的USB芯片,直接用MCU内置的USB外设,能最大程度简化硬件设计和固件复杂度。其次,STM32F4通常配备至少一个,甚至多个CAN控制器(bxCAN),原生支持CAN 2.0B协议,部分型号通过固件升级也能支持CAN FD的基本收发。它的主频高达168MHz,带有FPU,处理USB数据包和CAN报文帧的打包、解析、缓冲绰绰有余,为实现低延迟、高吞吐量提供了硬件保障。最后,其丰富的生态(HAL库、CubeMX工具)和广泛的应用,意味着你在开发中遇到的绝大多数问题,都能在社区找到答案或参考。
2.2 软件架构:拥抱SocketCAN生态
上位机接口的选择决定了工具的易用性和通用性。SocketCAN是Linux内核中将CAN设备网络套接字化的一个子系统。它最大的好处是标准化。一旦你的设备在Linux下被识别为一个SocketCAN接口(比如can0),所有标准的Linux网络工具(如ip link,cansend,candump)和编程接口(如C的socket()调用,Python的python-can库)都能直接使用,无需任何特定驱动或SDK。这极大地降低了上位机软件的开发门槛和跨平台移植的难度。我们的固件目标,就是让STM32F4通过USB模拟成一个符合SocketCAN规范的网络设备。
2.3 协议桥梁:USB-CAN的通信模型设计
USB和CAN是两种截然不同的通信模型。USB是主从式、基于事务的、高带宽的;CAN是多主式、基于报文ID、带宽相对较低但可靠性高的。固件的核心任务就是在这两者之间建立一个高效、不丢数据的桥梁。我采用的模型是“双缓冲队列+事件驱动”。
- CAN到USB方向:CAN控制器收到一帧报文,触发中断。中断服务程序(ISR)只做最少的操作——将CAN邮箱中的报文数据(ID、DLC、数据场)快速拷贝到一个RAM中的环形缓冲区(Rx Ring Buffer),并更新写指针。然后,在主循环或USB发送回调函数中,从这个环形缓冲区读取数据,按照我们自定义的、紧凑的协议格式打包,通过USB端点(Bulk IN Endpoint)发送给主机。这样做避免了在ISR中进行耗时的USB通信,保证了CAN中断的响应速度,不会因为USB暂时阻塞而丢失CAN报文。
- USB到CAN方向:主机通过USB端点(Bulk OUT Endpoint)下发指令或CAN发送请求。USB接收中断触发后,将数据包存入另一个接收缓冲区。主循环解析这些数据包,如果是发送CAN报文的指令,则配置CAN控制器的发送邮箱,启动发送。这里同样需要队列管理,因为CAN总线可能有仲裁,发送不一定能立即完成。
自定义的USB通信协议帧格式必须精心设计。一个典型的帧头可以包含:帧类型(如:发送CAN帧、设置波特率、读取状态)、CAN帧信息(标准/扩展帧、CAN FD标志、波特率切换标志等)、时间戳(用于精确分析)。数据部分就是CAN报文本身。协议要足够简洁以减少开销,又要足够完备以支持所有必要功能。
3. 固件实现深度解析与关键模块构建
有了顶层设计,我们深入各个模块,看看具体怎么实现,以及其中有哪些容易踩坑的细节。
3.1 开发环境与工程初始化
我使用的是STM32CubeIDE,它集成了CubeMX配置工具和基于Eclipse的IDE,管理STM32项目非常方便。第一步就是用CubeMX初始化工程。
- 时钟树配置:这是STM32性能的根基。对于F407,我们需要将系统时钟(SYSCLK)配置到168MHz。特别注意USB时钟(48MHz)必须由专用的PLL(通常PLL48CK)精确提供,误差必须控制在0.25%以内,否则USB通信会失败。CubeMX的时钟配置图会直观地显示是否满足要求。
- 外设配置:
- CAN1/CAN2:模式设为“Normal”,这允许它收发数据。根据硬件连接,配置正确的引脚(通常是PA11/PA12或PB8/PB9)。关键参数是“Bit Timing Parameters”,这决定了CAN总线的波特率。对于500kbps的标准波特率,在168MHz系统时钟下,典型的配置可能是:Prescaler=6, Time Segment 1=13, Time Segment 2=2, Synchronization Jump Width=1。计算和验证这个参数需要借助工具或仔细查阅数据手册。
- USB_OTG_FS:模式设为“Device Only”。在“Middleware”部分,选择“USB_DEVICE”,Class选择“Communication Device Class (CDC)” 或者更底层的“Custom Class”。这里有个重要选择:CDC还是Custom Class?CDC(虚拟串口)实现简单,在主机端表现为一个串口,但你需要额外在应用层解析串口数据来实现SocketCAN功能,效率较低,且延迟不稳定。为了实现真正的SocketCAN(
can0网络接口),我们需要让Linux内核识别为一个网络设备,这通常需要实现一个特定的USB网络设备类,或者使用gs_usb(Gadget Serial USB) 内核驱动兼容的协议。更直接的方法是,我们的固件模拟一个类似“CANable”或“PCAN-USB”设备的协议,这些协议已有成熟的开源Linux内核驱动(如canable_fd,gs_usb)。本项目采用模拟gs_usb协议的方式,因此USB设备类选择“Custom Class”,并手动配置描述符。
- 中断配置:使能CAN RX0中断(用于接收报文)、USB全局中断和对应的端点中断。合理设置中断优先级,通常CAN接收中断的优先级应高于USB中断,因为CAN报文有实时性要求,不能被USB数据处理阻塞太久。
- 生成代码:点击生成代码,CubeIDE会创建包含HAL库初始化的完整工程框架。我们的主要工作将在
usbd_custom_hid.c(如果选Custom HID)或自定义的USB类文件,以及主循环和CAN回调函数中展开。
3.2 USB设备自定义与gs_usb协议模拟
要让Linux自动识别为SocketCAN设备,我们需要让USB设备报告特定的 Vendor ID 和 Product ID,并且遵循gs_usb驱动的通信协议。这不是一个标准的USB类,所以我们需要深度定制USB描述符。
修改USB描述符:在
usbd_custom.c(或类似的自定义设备文件)中,找到设备描述符、配置描述符、接口描述符和端点描述符。- 设备描述符:将
idVendor和idProduct改为某个已获内核支持的值。例如,开源项目CANable常用的VID/PID是0x1d50/0x606f。使用这些ID,Linux在插入设备时会尝试匹配驱动。 - 接口描述符:
bInterfaceClass通常设为0xFF(Vendor Specific),表明是厂商自定义类。 - 端点描述符:至少需要两个Bulk端点,一个IN(设备到主机),一个OUT(主机到设备)。端点地址和包大小(MPS)要合理设置,全速USB最大包长是64字节。对于CAN FD(最大64字节数据),一个报文可能需要多个USB包传输,固件需要处理分包与组包逻辑。
- 设备描述符:将
实现
gs_usb数据帧格式:该协议定义了主机与设备之间交换的控制命令和数据帧结构。例如,一个用于发送CAN数据的命令帧可能包含:struct gs_host_frame { uint32_t echo_id; // 用于请求-响应匹配 uint32_t can_id; // CAN标识符(包含EFF/RTR标志位) uint8_t can_dlc; // 数据长度码(对于CAN FD,有特殊含义) uint8_t channel; // 通道号(多通道适配器时使用) uint8_t flags; // 标志位(如CAN FD、BRS、ESI位) uint8_t reserved; uint8_t data[64];// CAN数据场 };固件需要解析主机发来的这种结构体,并根据
echo_id和channel执行相应操作(发送CAN报文、设置滤波器、改变波特率等)。同样,当CAN控制器收到报文时,固件需要将报文封装成相同的格式,通过USB IN端点发送给主机。
注意:直接使用他人的VID/PID用于商业产品可能涉及法律问题。个人学习和原型开发无妨,但产品化时需要申请自己的USB VID。
3.3 CAN控制器驱动与高效双缓冲机制
STM32的bxCAN控制器功能强大但配置稍复杂。HAL库提供了HAL_CAN_*系列函数,但为了追求极致性能和可控性,我建议在理解HAL的基础上,直接操作寄存器或编写更轻量级的驱动。
CAN初始化:除了波特率,关键配置是过滤器(Filter)。bxCAN提供了数量可观的过滤器组,用于硬件过滤无关的CAN ID,减轻CPU负担。在我们的桥接应用中,通常需要接收总线上所有报文,因此可以将过滤器配置为“屏蔽模式”,将屏蔽码和标识码都设为0,即接收所有报文。
中断驱动接收:配置CAN的FIFO0关联到接收中断。当FIFO中有新报文时,触发中断。在中断服务函数
CAN1_RX0_IRQHandler()中:- 读取
CAN_RF0R寄存器确认FIFO非空。 - 从
CAN_RF0R指定的邮箱读取CAN_RIxR(ID),CAN_RDTxR(DLC和时间戳),CAN_RDLxR,CAN_RDHxR(数据)。 - 将这些原始数据快速组合成一个结构体,放入一个预先定义好的环形缓冲区。
- 释放该邮箱(设置
CAN_RF0R的RFOM0位)。 - 切记,中断函数里不要做复杂运算或调用可能阻塞的函数(如
printf)。我们的原则是“快进快出”。
- 读取
环形缓冲区实现:这是保证不丢帧的核心。定义一个结构体数组作为缓冲区,并维护读/写索引和大小计数。
typedef struct { uint32_t id; uint8_t dlc; uint8_t data[8]; uint32_t timestamp; } CanRxFrame_t; #define CAN_RX_BUF_SIZE 256 CanRxFrame_t can_rx_buffer[CAN_RX_BUF_SIZE]; volatile uint32_t can_rx_write_idx = 0; volatile uint32_t can_rx_read_idx = 0; volatile uint32_t can_rx_count = 0;在CAN接收中断中,将帧写入
can_rx_buffer[can_rx_write_idx],然后更新写索引和计数。在主循环中,检查can_rx_count,如果大于0,则读取并处理,然后更新读索引。读写索引都需要考虑缓冲区回绕(idx = (idx + 1) % CAN_RX_BUF_SIZE)。由于中断和主循环可能同时访问这些索引,它们应声明为volatile,并且对于can_rx_count的增减操作最好是原子操作,或者在操作时暂时关闭中断。
3.4 主循环调度与协议解析
主循环while(1)是固件的大脑,负责协调所有任务。
- 检查并处理CAN接收缓冲区:如果
can_rx_count > 0,则取出一个帧,按照gs_usb格式封装,然后调用CDC_Transmit_FS()或底层的USBD_LL_Transmit()函数,通过USB IN端点发送出去。这里需要注意USB的传输状态,如果上一次传输还未完成(hcdc->TxState不为0),需要等待或缓冲,不能覆盖。 - 检查并处理USB接收:USB OUT端点的数据接收通常是中断驱动的,数据会被存放到一个缓冲区。主循环需要定期检查这个缓冲区是否有新数据包到达。如果有,则解析数据包。根据帧头中的“命令字”,执行不同操作:
CMD_CAN_SEND: 解析出CAN ID、DLC、数据,调用HAL_CAN_AddTxMessage()将报文放入CAN发送邮箱。CMD_SET_BITRATE: 解析出目标波特率参数,动态重新初始化CAN控制器(这需要先让CAN进入睡眠模式,修改位定时寄存器,再退出初始化模式)。动态修改波特率是一个高风险操作,必须确保总线上无活动时进行,并且要做好错误恢复。CMD_GET_STATUS: 返回设备状态,如错误计数器、总线状态(正常、警告、被动、离线)等。
- 处理CAN发送完成中断:CAN报文成功发送或发送失败也会触发中断。可以在中断中设置标志位,在主循环中查询并处理,例如重试发送或上报错误。
- 看门狗与异常处理:加入独立看门狗(IWDG)防止程序跑飞。对于USB通信超时、CAN总线持续错误等异常情况,要有复位或恢复机制。
4. 上位机驱动与SocketCAN接口配置
固件烧录到STM32后,插上Linux电脑,奇迹不会自动发生。我们需要确保内核有正确的驱动,并进行配置。
驱动加载:现代Linux内核(如5.x版本)通常已经内置了
gs_usb驱动。当插入设备,内核识别到特定的VID/PID后,会自动加载该驱动。你可以通过dmesg | tail命令查看内核日志,应该能看到类似下面的信息:[ 123.456789] usb 1-1.2: new full-speed USB device number 5 using xhci_hcd [ 123.567890] usb 1-1.2: New USB device found, idVendor=1d50, idProduct=606f [ 123.567892] usb 1-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3 [ 123.567894] usb 1-1.2: Product: XCAN FD Adapter [ 123.567895] usb 1-1.2: Manufacturer: YourName [ 123.569123] gs_usb: GS USB devices now attached如果驱动没有自动加载,可能需要手动加载
modprobe gs_usb。网络接口出现:驱动加载成功后,会创建一个网络接口,通常是
can0。使用ip link show命令可以查看:1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: enp3s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000 link/ether aa:bb:cc:dd:ee:ff brd ff:ff:ff:ff:ff:ff 3: can0: <NOARP,ECHO> mtu 16 qdisc noop state DOWN mode DEFAULT group default qlen 10 link/can注意
can0的状态是DOWN,并且MTU是16(经典CAN)或72(CAN FD),这与以太网接口完全不同。配置CAN接口:在使用前,需要配置比特率并启动接口。
- 设置500kbps比特率:
sudo ip link set can0 type can bitrate 500000 - 启动接口:
sudo ip link set up can0 - 可以使用
ip -details link show can0查看详细的配置信息,包括状态、比特率、错误计数器等。
- 设置500kbps比特率:
测试通信:
- 发送一帧报文:
cansend can0 123#1122334455667788(发送标准帧ID 0x123,数据为8字节) - 监听总线:
candump can0(会打印出所有收到的CAN帧) - 如果固件和驱动工作正常,你应该能看到自发自收的报文(如果总线只有你一个节点),或者与其他CAN设备的正常通信。
- 发送一帧报文:
5. 进阶功能实现与性能优化
基础功能跑通后,可以考虑添加更多实用功能来提升工具的竞争力。
5.1 CAN FD支持实现
CAN FD帧格式与经典CAN不同,数据场最长可达64字节,并且存在两个比特率(仲裁段波特率和数据段波特率)。STM32F4的bxCAN硬件本身不支持CAN FD,但我们可以通过“位填充”和“软件定时”的方式在支持CAN 2.0的控制器上实现FD帧的接收和发送,这被称为“固件FD”或“软件FD”。这需要精确的定时器(如TIM)来模拟FD的数据段高速传输。
- 接收CAN FD:CAN控制器会以经典CAN格式(最大8字节)接收到FD帧,并可能报告“格式错误”。我们需要在固件中,通过分析接收到的原始数据(包括可能被破坏的帧),结合时间戳和预知的FD帧格式(通过DLC编码判断),在软件中重组出完整的FD帧。这非常复杂且容易出错,通常需要特定的硬件支持或外置CAN FD控制器。
- 发送CAN FD:更困难。需要禁用CAN控制器的自动重传,在发送完仲裁段后,立即切换GPIO模式(从CAN TX切换到普通推挽输出),然后使用高精度定时器(如TIM+DMA)严格按照FD数据段的位时序,手动“比特填充”输出64字节的数据位和CRC场,最后再切换回CAN控制器发送ACK和EOF场。这极其依赖CPU性能和精确的定时,在168MHz的F4上实现稳定的高速(如5Mbps数据段)FD发送挑战巨大。
因此,对于真正的CAN FD应用,更推荐使用内置CAN FD控制器的MCU,如STM32G4、H7系列,或者外置的MCP2517/8FD、TLE925x等芯片。我们的“XCAN FD”固件如果基于F4,可能更准确地应称为“XCAN Pro”,主要优化经典CAN,FD作为实验性或有限支持的功能。
5.2 时间戳同步与精确分析
网络分析仪的一个重要功能是精确的时间戳。STM32内部有一个32位的微秒级定时器(如SysTick或TIM2)。可以在每次CAN报文接收中断发生时,读取该定时器的值,作为时间戳存入环形缓冲区。通过USB上传给主机时,一并发送。主机软件可以利用这个时间戳分析报文间隔、网络负载等。为了更精确,可以考虑使用CAN控制器本身的接收时间戳(bxCAN的CAN_RDTxR寄存器低16位),它的分辨率是一个时间份额(Time Quantum, Tq),比系统定时器更贴近总线事件。
5.3 大容量存储与离线记录
有时需要设备脱离PC独立工作,记录总线数据。可以添加一个MicroSD卡槽(通过SPI或SDIO接口)。当设备处于“记录模式”时,接收到的CAN报文连同时间戳被写入SD卡的文件中。文件格式可以是简单的二进制格式,也可以是通用的如.asc(Vector ASC) 或.log格式,方便用主流分析软件(如CANalyzer, SavvyCAN)打开。这需要实现FATFS文件系统,并处理好SD卡的写速度(CAN总线500kbps时,数据量不小),确保不会因为写卡延迟而丢帧。
6. 调试技巧、常见问题与解决方案实录
开发过程中,我踩过不少坑,这里把关键问题和解决方法记录下来。
6.1 USB枚举失败
- 现象:设备插入电脑,设备管理器(Windows)或
lsusb(Linux) 识别不到,或者识别为“未知设备”。 - 排查:
- 检查硬件:USB线是否完好?DP/DM线是否接反?STM32的USB引脚(PA11/PA12)是否已正确配置?VBUS是否有5V供电?(开发板通常通过USB口直接供电)。
- 检查时钟:这是最常见的原因。用调试器单步运行,检查
SystemCoreClock变量值是否正确(168MHz)。重中之重:检查USB时钟(48MHz)是否由PLL精确产生。在CubeMX的时钟图中确认“USB OTG FS clock”源是“PLLQ”,且分频系数计算后正好是48MHz。误差过大会导致USB PHY无法正常工作。 - 检查描述符:使用USB分析仪(如Wireshark+USBPcap)是终极武器。没有的话,可以在固件中,在USB初始化完成后的回调函数(如
USBD_SetupStage)里设置断点,看主机是否发来了获取描述符的请求。如果没收到,说明底层USB通信就没建立。如果收到了但主机不认可,可能是描述符格式错误、长度不对或内容非法。
6.2 CAN通信异常
- 现象:USB设备已识别,
can0接口也能up,但candump收不到任何报文,或者cansend发送失败。 - 排查:
- 物理层检查:CAN总线两端是否有120欧姆终端电阻?这是必须的。用示波器测量CAN_H和CAN_L之间的差分信号,在发送时应该能看到明显的波形。如果没有波形,检查CAN收发器(如TJA1050)的供电和使能引脚。
- 波特率匹配:确保固件中CAN控制器的波特率设置与总线上其他设备、以及你用
ip link set命令设置的比特率完全一致。一个比特的误差都可能导致无法同步。使用示波器测量位时间进行验证是最可靠的。 - 过滤器配置:确认CAN过滤器是否配置正确。如果只想监听所有流量,最简单的办法是启用一个过滤器,并设置掩码模式,掩码码和ID码都为0。
- 中断与缓冲区:在CAN接收中断服务函数里设置一个GPIO引脚翻转,用逻辑分析仪或示波器看中断是否被触发。如果触发频繁但上位机收不到数据,问题可能出在环形缓冲区管理或USB发送环节。可以在主循环中,将缓冲区中的数据通过串口打印出来,先绕过USB,验证CAN接收逻辑是否正确。
6.3 SocketCAN接口无法启动或发送失败
- 现象:
sudo ip link set up can0失败,提示“No buffer space available”或其他错误。 - 排查:
- 驱动协议不匹配:这是最可能的原因。内核的
gs_usb驱动期望设备遵循特定的控制请求和数据格式。我们的固件必须100%兼容。仔细对照开源项目(如candleLight_fw)的固件源码,检查每一个控制请求(如设置波特率、设置模式)的响应格式。一个常见的错误是,驱动发送了设置波特率的控制请求,但固件没有回复,或者回复的数据长度不对,导致驱动认为设备无响应而失败。 - 端点配置:确认USB端点类型(Bulk)、方向、包大小与驱动期望的一致。驱动可能固定从某个端点号(如0x81, 0x01)读写数据。
- 查看内核日志:
dmesg会提供最直接的错误信息。例如,“gs_usb: Could not set bittiming (err=-71)” 这样的错误码能指引你到固件中查找对应的处理函数。
- 驱动协议不匹配:这是最可能的原因。内核的
6.4 性能瓶颈与丢帧问题
- 现象:在总线负载较高时,出现丢帧,或者USB传输延迟大。
- 优化:
- 增大缓冲区:这是最直接的方法。将CAN接收环形缓冲区从256扩大到512甚至1024。确保缓冲区操作是线程安全的(中断与主循环)。
- 优化USB传输:使用USB Bulk传输的最大包长(64字节)。将多个CAN帧打包成一个USB数据包一次性发送,可以减少USB协议开销。但要注意,这增加了上位机解析的复杂度,需要定义更复杂的帧结构来区分帧边界。
- 提升处理优先级:适当提高CAN接收中断的优先级,确保它能及时响应。但注意不要高于系统滴答定时器(SysTick)中断,否则会影响HAL库的延时函数。
- 减少调试输出:移除所有在中断或主循环中的
printf语句,它们会严重拖慢程序。 - 使用DMA:对于USB和CAN的数据搬运,可以考虑使用DMA。STM32的USB OTG和CAN都支持DMA。使用DMA可以将CPU从数据拷贝中解放出来,专注于协议处理。但DMA的配置和调试更为复杂。
这个项目从概念到实现,涉及了嵌入式开发的多个层面:USB设备协议、CAN总线通信、实时系统设计、驱动交互。它不仅仅是一个固件,更是一个理解现代嵌入式系统如何与复杂外部世界交互的绝佳案例。当你亲手让candump can0窗口里开始滚动来自真实设备的数据流时,那种成就感是无可替代的。最后一个小建议,在开始编码前,先用逻辑分析仪抓一下一个成熟的商业USB-CAN适配器(如PCAN-USB)的USB通信数据流,你会对整个协议交互过程有恍然大悟的理解,这比看任何文档都来得直接。
本文还有配套的精品资源,点击获取