给 CAN 卡加上 Wi-Fi:STM32G474 + ESP32-S3 扩展方案
本文最后更新于12 天前,其中的信息可能已经过时,如有错误请留言评论。

2026-09-08 · 设计记录。Wi-Fi 扩展尚未实现,文中的接口频率和性能预算有待原型验证。

这张 DluCanFD CAN 卡使用 STM32G474CBT6,目前支持双路 CAN / CAN FD,通过 USB 与电脑通信。这次想再加上 Wi-Fi,让上位机也能通过无线网络访问设备。

考虑到现有 CAN 驱动和 USB 协议都已经有了基础,初步打算保留 STM32,在旁边增加一颗 ESP32-S3,通过 SPI + DMA 连接。STM32 继续处理 CAN 收发、时间戳和通道控制,ESP32-S3 承担配网、TCP 连接和缓存。第一版先跑通 DluCanFD over TCP,下面记录这套方案的取舍和接下来的实现安排。

现有工程的基础

项目当前事实对扩展的影响
主控STM32G474CBT6,App 初始化到 160 MHz保留主控承担实时 CAN 工作
CAN双路 FDCAN,统一使用 bsp_can_frame_t沿用现有驱动和帧语义
Flash128 KiB,其中 Bootloader 32 KiB、App 92 KiB、两页配置合计 4 KiBWi-Fi / TCP/IP 栈放在 ESP32,STM32 只增加传输和控制逻辑
RAM普通 SRAM 96 KiB、CCM 32 KiB;USB 协议池固定预留 48 KiB按实际链接布局分配无线缓冲
调度中断搬运 + 非阻塞主循环,没有现成网络任务框架新增 SPI 异步泵,保持主循环有界
DluCanFD16 B 包头、最大 1024 B payload、CRC16、命令响应、CAN batch 格式复用现有应用协议
连接状态DluCanFD 连接和通道状态依赖 usb_core_is_configured()无线会话必须独立于 USB 枚举
CAN 上传当前每次上传生成只含一帧的 RX_CAN_BATCH需要实现真正的多帧聚合
时间戳record 中的 timestamp_us 写入 0,内部帧没有时间戳字段在 STM32 接收入口增加时间戳
状态能力stats / event 等存在协议预留,尚未全部启用实际实现丢帧、队列和断线状态后再声明能力

回头看现有工程,内存布局是扩展前需要先弄清楚的一件事。2026-09-08 的一份构建 map 显示:LR_APP 使用约 56.6 KiB / 92 KiB,普通 App RW/ZI 区约 6.8 KiB / 25.5 KiB,CCM 数据约 23.9 KiB / 28 KiB。暂时可以据此估算新增代码和缓冲区的空间,等帧结构、队列和驱动改完,还要重新核对实际占用。

引脚、电源和天线位置还需要对照实际原理图确认。这篇先把整体架构定下来,具体模组参数、最高 SPI 频率和无线覆盖范围,留到选型和原型验证时补齐。

STM32 负责 CAN,ESP32 负责网络

USB 路径:上位机 / SDK ↔ USB ↔ STM32G474 ↔ 双路 CAN / CAN FD。

Wi-Fi 路径:上位机 / SDK ↔ Wi-Fi / TCP ↔ ESP32-S3 ↔ SPI + DMA ↔ STM32G474 ↔ 双路 CAN / CAN FD。

第一版先让 USB 和 Wi-Fi 互斥控制 CAN。如果两端同时修改波特率、启停通道或发送报文,控制权和断线恢复都会变得复杂。先增加 USB / WIFI 连接模式,只在通道停止时允许切换,通过重启应用生效,比较容易沿用现有的单实例逻辑。

  • USB 模式沿用现有 USB 协议选择。
  • Wi-Fi 模式使用 DluCanFD 网络会话;USB 仅承担允许的设备管理与维护,不启动厂商 CAN 协议数据面。
  • Wi-Fi 会话不能因为 USB 未插入而处于离线状态。
  • 维持“一次只有一个协议实例占用共享池”的前提。Wi-Fi 模式的 USB 管理端点使用小块独立存储,不能启动第二套协议后调用 usbcan_pool_reset()

双端同时收发、多客户端只读监听和热切换接管,可以等单链路稳定后再逐步加入。到那时,各端需要独立的接收队列,也要重新分配控制权和内存,不能继续照搬当前的单实例前提。

模块与硬件接口

为什么选 ESP32-S3

选择适用情况建议
ESP32-S3 + SPI,自编 ESP-IDF 固件双路 CAN FD、网络缓存、后续配网页面首选,优先评估带 PSRAM 的模组
ESP32-C3 + SPI,自编固件成本更敏感、缓存与吞吐需求较低后续降本候选,先验证资源与吞吐
UART + AT / 串口透传模块低负载演示、只引出串口的旧板受限原型,不作为双路 CAN FD 默认数据通道
外接 USB 网络共享盒完全不改 CAN 卡且能接受额外盒子需匹配 USB 主机、PC 虚拟 USB 驱动和兼容性,单独评估

模块上,我更倾向于带 PSRAM 的 ESP32-S3 WROOM 系列,主要是想给无线抖动留出一些排队空间。具体料号还要结合手册、温度等级、天线和供应情况确定。大缓存与 DMA 活跃缓冲需要分开考虑:SPI DMA 先使用 ESP-IDF 支持的内部 DMA 内存,再评估哪些排队数据适合放到 PSRAM。

两颗芯片之间的连接

板间连接暂定 STM32 做 SPI 主机、ESP32 做从机,由 STM32 安排事务时机。除了 SCK / MOSI / MISO / CS,还需要 READY、IRQ 两根握手线,并预留 ESP32 EN、下载口和调试串口。

信号方向用途
SCK / CSSTM32 → ESP32STM32 控制事务时机
MOSISTM32 → ESP32CAN 上传及控制响应
MISOESP32 → STM32PC 下发命令和发送请求
READYESP32 → STM32ESP32 已准备好一个 SPI DMA 事务
IRQESP32 → STM32待下发数据或链路事件通知
EN,可选STM32 → ESP32独立复位网络模块

READY 和 credit 表达的是两件事:前者表示 ESP32 已准备好一个 SPI 事务,后者表示接收端还剩多少缓存空间。还有一个容易漏掉的场景:CAN 总线很安静时,ESP32 仍可能收到上位机命令。STM32 需要响应 IRQ,并保留有界轮询,让这些命令不必等到下一次 CAN 上传才有机会下行。

联调可以从低频开始,再逐步验证 10 / 20 / 40 MHz。40 MHz 目前只是待评估的目标,最终取值要看模组时序、PCB 和实际传输结果。

现有代码已经占用了 PB8/PB9(CAN0)、PB5/PB6(CAN1)、PA11/PA12(USB)和 PA15(LED)。SPI1 的 PA5/PA6/PA7 可以先列为候选,CS、READY、IRQ 另选空闲 GPIO。PB5 已经是 CAN1 RX,选复用功能时尤其要避开这处冲突。正式画板前,再把封装复用表、PCB 连线和启动脚约束一起过一遍。

电源与天线

除了数据接口,增加 Wi-Fi 模组还会带来供电瞬态和射频布局的问题。下一版硬件需要一起考虑这些内容:

  • 为 ESP32 模组配置有足够瞬态余量的 3.3 V 电源,可先按 1 A 等级稳压方案评估。这是供电设计余量,不表示模组持续消耗 1 A;最终按模组规格、整卡负载和示波器实测定额。
  • 当前 DluCanFD USB 配置声明 100 mA。无线版需要重新核算整卡功耗、USB 供电与枚举约束;仅修改描述符不会自动增加上游供电能力。
  • 没有 PC USB 数据连接时仍需供电,可用 USB 接充电器/适配器;车载独立供电需匹配输入保护和稳压设计。
  • 模组与 STM32 位于同一数字电源域;若 CAN 口已有隔离,维持原隔离边界,不用新增连线跨接隔离地。
  • 天线布置在板边并遵守净空要求;金属外壳评估外置天线模组。核查 Wi-Fi 发射对 CAN 收发器、电源纹波和 EMC 的影响。

固件需要调整的地方

复用 DluCanFD 协议

网络端先用一条长 TCP 连接,承载命令、ACK/NACK、RX/TX batch 和事件。这样现有 DluCanFD 的长度字段、CRC、版本和 CAN record 格式都能继续使用。解析器仍然要处理拆包和粘包,一次 recv() 可能只拿到半个包,也可能同时拿到几个包。

连接顺序暂定为:网络连接 → 设备身份与客户端认证 → 获取控制会话 → HELLOGET_INFO → 配置通道 → 启动 → 收发。

认证和会话信息通过明确的网络版本或独立握手增加,保留 v1 中必须为零的字段。第一版先面向局域网,只开放一个控制客户端,公网访问不在这次扩展范围内。

选择 TCP,主要是因为它有序交付,容易与现有协议和 SDK 衔接。不过,重传会造成队头等待,延迟并不固定,设备缓存耗尽也仍然需要处理。实现时可以让命令和心跳优先排队,限制 socket 中的积压数据,再评估 TCP_NODELAY 对小命令延迟的影响。CAN 上传则继续做应用层聚合。

后续若增加更强调实时性、允许少量丢包的监视模式,再考虑 UDP 数据面,并配套序号和丢失统计。CAN 发送和通道配置会改变设备行为,仍然需要明确的交付语义。

SPI 链路与异常处理

SPI 内部链路需要单独定义长度、消息类别、会话标识、序号、确认号、credit 和完整性校验,用来承载 DluCanFD 包或模块状态消息。两颗 MCU 之间也采用逐字段编码,避免依赖 C 结构体的内存布局。具体实现需要覆盖下面几种情况:

  • 校验长度上限、版本和 CRC 后再交给上层,异常包不能改变 CAN 配置或发送队列。
  • 两个方向独立维护序号与确认;重传包识别为重复,不再次执行 CAN 发送。
  • 任一 MCU 复位后建立新链路会话,清理旧会话未执行的网络发送请求,禁止重连后重放旧帧。
  • 采用有界 DMA 双缓冲、超时、错误计数和 credit 流控;事务长度与对齐要求随选定的 ESP-IDF SPI 从机实现冻结。
  • DMA ISR 只更新完成状态与索引;解析、重试、配网和 socket 操作不进入 STM32 CAN ISR。

对应到现有工程的改动

按这个分工,需要调整的模块集中在传输接口、桥接调度和状态记录上。对应到现有工程,可以先按下表推进:

位置建议改动
BSP/spi/,新增SPI / DMA / GPIO / 超时与恢复;DMA 缓冲区使用明确可访问的普通 SRAM 区域
Middleware/增加 ESP 板间链路,将 DluCanFD 字节输入输出和连接状态从 USB 端点调用中解耦
现有 DluCanFD 模块复用命令、校验和编码;增加真正的 RX 多帧聚合、时间戳和网络统计
App/services/bridge_service.*按当前控制会话选择帧来源与去向,保持单个 CAN TX 生产者
App/main.c编排非阻塞 SPI 泵、协议泵、CAN 收发与恢复,不等待 Wi-Fi
App/services/fault_*区分 CAN 错误、无线离线和队列溢出,将事件供协议和 LED 使用
BSP/can/ 与内部帧接收入口增加时间戳,复核结构体尺寸、CCM 使用和所有适配器
Keil / MemMap / scatter新增源文件同时登记到 .uvprojx / .uvoptx,更新段布局并检查 map
ESP32 独立工程ESP-IDF 下实现 AP/STA、配网、TCP 会话、SPI slave、缓存、认证和模块恢复
上位机 / C SDK 独立工程网络发现、按地址连接、TCP transport、状态显示、断线与未知发送结果处理

这次真正需要分开的,是“字节传输与连接生命周期”和“DluCanFD 命令及 CAN 帧语义”。围绕这条边界解耦,就能让同一套应用协议接到 USB 或 SPI 上。其他职责内聚的实现继续保留,ZLG、PCAN、创新等适配器也继续使用各自的私有格式。

时间戳和发送结果不能含糊

现有上传时间戳固定填 0,加上无线链路后,这一项需要补齐。TIM2 已经提供 32 位、1 MHz 的自由运行时基,可以先在 CAN RX ISR 搬运硬件 FIFO 时读取,让接收时间随帧进入队列。这是软件采集时间,会包含中断延迟和 FIFO 排队;若后续精度不够,再评估 FDCAN 硬件接收时间戳及扩展方式。

如果等报文到达 ESP32 或 PC 后再补时间戳,无线链路的抖动就会混进 CAN 时序里。时间应在 STM32 侧记录,并随原帧传递。32 位微秒计数约 71.6 分钟回绕,SDK 需要结合连续数据和设备会话处理;重启后建立新的时间基准,不同设备间也不能直接比较未同步的时间戳。

发送结果继续沿用现有约定:TX_CAN_BATCH 的 ACK 表示整批已被 STM32 接受入队,并不表示已经完成总线发送。断线前提交但未得到最终结果的请求,在 SDK 中标为“结果未知”,不自动重发。SPI 去重只解决局部链路的重复包,无法保证 MCU 重启后仍然做到应用级恰好一次执行。

先算带宽,再决定缓存

带宽是否够用,要从实际 CAN 帧率和封装字节数算起。Wi-Fi 标称空口速率不能直接当成 TCP 吞吐,两路 CAN 的数据段波特率相加,也不等于每秒需要上传的数据量。

单帧 record 长度 = align4(12 + CAN 数据长度)
一个 batch 长度 = 16 + 4 + 所有 record 长度之和
所需应用吞吐 = 每秒所有 batch 字节数

拿 64 B CAN FD 做一笔预算:每个 record 是 76 B,现有 1024 B payload 上限能放下 13 帧,整个满 batch 是 1008 B。假设双通道合计每秒采集 10,000 帧,批量上传约需要 0.775 MB/s,还没计入 SPI 外层、TCP/IP 和 Wi-Fi 开销。当前一帧一个包的方式则是 96 B/帧,约 0.960 MB/s。这是容量计算示例,实际帧率和吞吐还要测试。

板间接口理论数据上限评估
UART 115200,8N111.52 kB/s仅适合低负载演示
UART 3 Mbaud,8N1300 kB/s低于双路 FD 示例,且尚未扣除协议开销
SPI 20 MHz每方向 2.5 MB/s 时钟上限扣除握手、间隔、处理和控制开销后实测
SPI 40 MHz每方向 5 MB/s 时钟上限扩展目标,受 MCU、模组时序和 PCB 条件约束

3 Mbaud 串口的理论吞吐已经低于这组业务示例,这是目前选择 SPI 的主要原因。后续测试分别记录两个方向的有效吞吐,不能把全双工两方向相加后当作单方向带宽。无线端则按实际 AP/STA 拓扑、信号强度、干扰和双向负载来验证。

网络侧暂按 256 KiB CAN 上传环形缓存做预算,STM32 保留小块 DMA 缓冲,尽快把数据搬给 ESP32。按前面的 0.775 MB/s 计算,256 KiB 理论上只能吸收约 0.34 秒停顿,实际还要扣除其他内存开销。可见增加缓存主要是为了缓冲短暂抖动,持续干扰或断网时,仍然要处理溢出和丢帧。

上传聚合同时设置大小上限和等待时限:流量大时满包就发,流量小时到期就发。初始等待预算先取 0.5~1 ms,再结合 CPU 占用和延迟测试调整。两个 CAN 通道保持公平调度,控制请求另留容量。

连接、掉线与恢复

使用方式准备支持热点直连和加入现有局域网。与连接功能一起确定的,还有断线、队列满和重连后的行为;这些情况会直接影响上位机看到的数据和实际发往总线的报文。第一版先按以下规则处理:

  • AP 直连:CAN 卡建立热点,电脑连接后使用自研上位机;每台设备使用独立初始密码。
  • STA 联网:加入局域网,按 IP 或设备发现连接;发现失败时保留手动地址连接。
  • 配网:优先支持 USB 设置 SSID/密码,配置保存在 ESP32;可增加受保护的首次配网页面和实体键恢复入口。
  • 认证:STA 数据连接规划 TLS 和设备身份校验,或使用经明确威胁模型验证的认证加密方案,流程和性能一起验收。CRC 不替代认证。
  • 掉线:有效协议活动和心跳维护控制租约,可从 500 ms 心跳、连续 3 次丢失释放控制权开始验证。USB 正常不代表无线会话正常。
  • 控制权失效:停止接受新 CAN TX,清理尚未执行的网络 TX,并定义硬件已提交帧的完成/取消结果;已上总线的帧无法撤回。
  • RX 缓冲耗尽:首版丢弃新到帧并累计 drop/gap,保留已排队数据顺序。计数独立存储,不能仅把丢帧事件写进已满的数据队列。
  • 慢消费:credit 不足时停止继续入队,但外部 CAN 节点不会因此停发;最终仍可能溢出,必须显示准确计数和数据不完整状态。
  • 重连:新建会话,重新确认配置与启动,不重放旧 CAN 发送队列。积压采集数据按会话策略清理并累计丢弃数。
  • 升级:首版保留 STM32 USB Bootloader,ESP32 升级单独管理。无线升级 STM32 需重新设计掉电恢复与完整性策略,不能仅转发 USB 擦写请求。

这次扩展仍然定位为 CAN 原始帧适配器。Wi-Fi 不提供硬实时保证,上位机 ISO-TP/UDS 的超时和 STmin 调度也需要单独验证。涉及 ECU 刷写或严格时序用例时,再根据实测结果评估设备端 ISO-TP 或定时发送。

上位机接入与兼容范围

上位机端准备在现有 C SDK 中增加 TCP transport,尽量复用 CAN 配置、发送和接收接口。连接入口需要能区分 USB 和网络,并接受设备 ID、IP 和端口;网络设备通过单独的发现和连接流程接入。

还有一处兼容范围需要分清:现有 ZLG、PCAN 等适配对应的是 USB 协议,增加 Wi-Fi 后,原厂软件不会自动发现这张卡。如果后续有这类需求,还要分别研究原厂网络协议、SDK 适配入口或 USB 网络虚拟化方式。

因此,第一版先让自研上位机支持 USB 和 Wi-Fi 两种连接。已有原厂软件兼容继续通过 USB 使用。

从原型到 PCB

接下来先用开发板验证,再进入 PCB 改版。实现过程分成四步,每一步都有可以单独检查的结果:

阶段交付验收重点
1. 硬件与 SPI 原型ESP32 开发板连接 STM32,独立供电,SPI DMA + 握手引脚、电源、双向伪随机数据、序号、CRC、复位恢复、净吞吐和最长事务间隔
2. 最小无线 CAN 链路TCP 握手、配置、双路收发、AP/STA、USB/WIFI 互斥外部 CAN 分析仪验证帧一致、实际发送、通道独立及无 USB 数据线工作
3. 性能与失败语义聚合、时间戳、流控、丢失统计、会话恢复双路 Classic/FD 长短帧、混合 ID、双向负载、bus-off、拥塞、无线中断和两 MCU 分别重启
4. 产品集成SDK/上位机、认证配网、PCB 模组、天线和说明书用户连接流程、供电瞬态、RF/EMC、长时间运行及 USB 模式回归

压测前先固定 CAN 配置、帧长分布、目标帧率、AP/STA 拓扑、距离和信号条件。持续净吞吐暂以目标业务峰值的 1.5 倍作为设计目标,给调度和无线波动留出余量,实测后再确定能够承诺的性能范围。

正常无线条件下,用外部节点的产帧计数和 PC 完整接收计数对账。故意断网或堵满缓存时,则检查丢失统计是否准确、设备是否死锁、重连后是否重复发送。测试记录保留延迟 P50/P95/P99/最大值、队列高水位、drop、SPI retry 和 CAN overrun。

单向延迟测量采用同步时间基准或共同观测点,避免直接相减两台机器未同步的时钟。稳定性测试先跑约定负载下的 1 小时,再做 24 小时运行,同时复查 Flash、SRAM、CCM 和栈余量。

硬件定版前的待确认项

进入硬件定版前,还需要补齐这些条件:

  1. PCB 原理图与可引出的 GPIO,确认 SPI、握手和下载引脚。
  2. 目标工况:双通道仲裁/数据波特率、最高持续负载、帧长分布、允许延迟与丢失。
  3. USB 供电还是独立供电、是否金属外壳、CAN 是否隔离。
  4. 无线是否只供自研上位机使用,是否另有原厂软件兼容要求。

目前的方向是 STM32G474 + ESP32-S3 + SPI DMA。下一步先跑通开发板之间的供电、握手、数据搬运和复位恢复,再接入双路 CAN。拿到实际数据后,再确定 SPI 时钟、缓存配置和下一版 PCB。

相关工程文件

相关工程文件一并记录在这里,后续实现、排查和复查资源时方便对照。

  • README.md:MCU、双路 FDCAN、当前协议与 App 运行方式。
  • MDK-ARM/Demo.sctMDK-ARM/Listings/Demo.map:空间划分与已有构建使用情况。
  • BSP/can/bsp_can.hBSP/can/bsp_can_private.h:帧结构、队列和 CAN 引脚。
  • BSP/led/bsp_led_private.hBSP/usb/usb_core_private.h:现有引脚占用。
  • BSP/timer/bsp_timer.c:TIM2 微秒时基。
  • App/main.cApp/services/bridge_service.c:调度和桥接依赖。
  • Middleware/usbcan/usbcan_pool.h:48 KiB 互斥协议池。
  • Middleware/usbcan/dlucanfd/dlucanfd_proto.c:USB 状态耦合、单帧上传和零时间戳。
  • Doc/DLUCANFD_USB_WIRE_SPEC.md:线上格式、ACK 语义、已实现与预留功能边界。
文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇