这是技术文档拆解型任务,重点在芯片外设配置和 USB-CDC 实现细节。先检索极海 APM32F402/F403 与 AN1163 的原始资料。
核心应用笔记已经拿到全文。再核对一下 F403 的规格差异和 USB-IF 认证信息,避免把两个型号写成一样。
极海在 AN1163 里给的方案,本质上不是 "把 printf 改到 USB",而是把 APM32F402/F403 的 USB OTG FS 配成一个复合设备(Composite Device):一路 CDC 虚拟串口专门输出日志,一路 WinUSB 负责高速数据交换,供电也从同一根线上取。落地动作只有三步:确认工程里有 usb_core、usb_init、usb_cdc 这些驱动并在初始化里调用 USB_DeviceInit (),重写 fputc () 把字符送进 USBD_FS_CDC_ItfSend (),再给 fputc 加一个 128 字节的静态缓冲,满或遇到换行才真正发一次。官方给的起点是 APM32F402 SDK 里的 OTGD_Composite_CDC_WINUSB 例程,描述符和端点分配已经配好,改动的代码量很小。这份笔记编号 AN1163、版本 V1.0、2025 年 8 月新建,适用于 APM32F402 与 APM32F403,正文以 F402 为例。
UART 那套为什么在复杂项目里撑不住,几个痛点很具体:
| 对比项 | UART 串口打印 | USB-CDC 虚拟串口 |
|---|---|---|
| 带宽 | 常见 115200bps,约 11.5KB/s,日志一多就堵 | 全速批量端点理论上限约 64KB/s(64 字节 / 帧、1ms 一帧),实际受主机调度影响 |
| 硬件 | 需要 USB 转 TTL 模块,或板上另做 USB-UART 桥 | 芯片内建 USB OTG FS 与 PHY,一根线直连 PC |
| 线缆 | 电源线、调试线、串口线各自一根 | 供电、数据、日志共用一根 |
| 资源占用 | 独占一个 USART 及其引脚 | 释放硬件 UART,可留给需要流控或固定波特率的外设 |
| 与其他功能共存 | 只能单独占一路 | 可与 WinUSB、HID、MSC、Audio 组成复合设备并行 |
引脚和时钟这两件事必须先定死,否则枚举阶段就会挂:
| 项目 | 要求 | 说明 |
|---|---|---|
| USB 数据脚 | PA11 = OTG_FS_DM,PA12 = OTG_FS_DP | 从数据手册引脚表可直接查到,与 USART1_CTS/CAN1_RX、USART1_RTS/CAN1_TX 复用 |
| VBUS | PA9 需接外部 VBUS 或 5V | 迁移指南明确提示,用 OTG FS 时要处理这路检测 |
| USB 时钟 | 48MHz,由 PLL 分频得到 | 时钟树里 OTG FS 的 48MHz 单独一路,PLL 配错就枚举不上 |
| 主频与总线 | AHB/APB2 最高 120MHz,APB1 最高 60MHz | F402/F403 数据手册口径 |
| 供电 | VDD 2.0~3.6V,VDDA 2.0~3.6V | 用 ADC 时 VDDA 不得低于 2.4V |
| 中断 | OTG_FS_IRQn(65)、OTG_FS_WKUP_IRQn(42) | 与老的 USBD 中断向量不是一套,移植时别照搬 |
真正动手时按这个顺序推进,每一步都有可验证的结果:
- 选例程。打开 APM32F402_403 SDK,找到 OTGD_Composite_CDC_WINUSB。不要自己在空白工程里从零拼描述符,复合设备的接口关联描述符(IAD)和端点地址分配最容易出错。
- 补驱动。确认工程里已有 usb_core、usb_init、usb_cdc 相关文件,缺一个都会在链接阶段报未定义。
- 起设备。在 main () 或自定义的系统初始化函数里调用 USB_DeviceInit (),由它按描述符向主机枚举出 CDC 和 WinUSB 两个接口。
- 改输出。重写 fputc (),把原本走 USART_TxData 的那两行注释掉,改成写入缓冲、条件触发 USBD_FS_CDC_ItfSend ()。
- 上机验证。插线后在设备管理器里同时看到 "虚拟 COM 端口" 和 "WinUSB 设备",再用 PuTTY 或 Tera Term 打开那个 COM 口。
fputc 的重写是这篇笔记的核心,逻辑不复杂但细节决定成败:
define CDC_TX_BUF_SIZE (128)
int fputc(int ch, FILE f)
{
static uint8_t s_cdcTxBuf[CDC_TX_BUF_SIZE];
static uint16_t s_cdcTxCount = 0;
/ 可选:把 '\n' 补成 "\r\n",适配部分 Windows 终端 /
if (ch == '\n') {
if (s_cdcTxCount >= CDC_TX_BUF_SIZE) {
USBD_FS_CDC_ItfSend(s_cdcTxBuf, s_cdcTxCount);
s_cdcTxCount = 0;
}
/ s_cdcTxBuf[s_cdcTxCount++] = '\r'; /
}
s_cdcTxBuf[s_cdcTxCount++] = (uint8_t)ch;
/ 缓冲满或遇到换行,立即整包发出 /
if (s_cdcTxCount >= CDC_TX_BUF_SIZE || ch == '\n') {
USBD_FS_CDC_ItfSend(s_cdcTxBuf, s_cdcTxCount);
s_cdcTxCount = 0;
}
return ch;
}
三个容易踩的地方,笔记里没有逐条列出,但实际调试一定会遇到:
| 现象 | 根因 | 处理办法 |
|---|---|---|
| 日志打了半截就不动了 | 缓冲只在 "满" 或 "遇到换行" 时发送,既没满也没换行就一直攒着 | 加超时刷写,或在关键路径末尾补 '\n' |
| 偶发丢字符、内容错位 | fputc 在中断和多任务里被重入,静态缓冲被并发改写 | 进临界区,或改成环形缓冲+单一发送任务 |
| 发送返回忙时数据被覆盖 | 上一次 USB 传输未完成就复用了同一块缓冲 | 检查发送接口返回值,忙时等下一次 SOF 或换双缓冲 |
| 换 PID/VID 后 WinUSB 认不出来 | 描述符里的微软扩展或配套 INF 与新的 VID/PID 不匹配 | 同步更新 INF 与设备描述符,别只改一处 |
验证阶段有两个反直觉的点值得先知道。CDC 虚拟串口的波特率设置基本不影响通信,因为速率由 USB 总线调度决定,不是 UART 那种分频出来的;所以在串口助手里填 115200 还是 921600,看到的日志速度是一样的。另一个是设备管理器里必须同时出现两个设备,只出现虚拟 COM 口说明 WinUSB 那一路没枚举成功,通常是描述符或端点分配被改坏了。
两个型号的差异,别默认它们完全一样:
| 项目 | APM32F402 | APM32F403 |
|---|---|---|
| 内核 | Cortex-M4F,带 FPU/DSP | Cortex-M4F,带 FPU/DSP |
| 主频 | 120MHz(数据手册口径) | 数据手册标 120MHz,官网在线选型表标 128MHz,两处不一致 |
| Flash / SRAM | 128KB / 32KB | 128KB / 32KB |
| USB | 1 路 USB OTG FS,支持主从 | 同左 |
| 封装 | QFN36/QFN48/LQFP48/LQFP64 | 同左 |
| 工作温度 | -40~85℃ 或 -40~105℃(按尾缀 6/7 区分) | 同左 |
这里必须提醒一句:APM32F403 的主频,官方数据手册写的是 "最高 120MHz",而在线选型工具的产品行里标的是 128MHz。两个都是极海自家页面,但数字对不上。做 USB 相关设计时影响不大,因为 OTG FS 只认那路 48MHz;但如果你的时序预算按 128MHz 排的,建议以最新版数据手册和勘误为准,别拿选型表当唯一依据。
还有一层信息值得放在决策链里:APM32F402 系列已经在 2025 年 6 月拿到 USB-IF 认证,官方表述是 "符合 USB 技术规范,电气性能与协议兼容性达到国际标准"。这不等于你的板子一定能过,因为认证针对的是芯片,插口、ESD 器件、差分走线、VBUS 处理都在你手里;但它至少说明 PHY 和协议栈的底子是过过测试的,不是纸面参数。
【相关 FAQ】
问题 1:极海 APM32F402/F403 的 USB-CDC 日志输出,和普通虚拟串口例程有什么区别?
回答 1:普通虚拟串口例程(比如 USBD_VCP)通常只枚举一个 CDC 接口,插上就是一个 COM 口,只能打印日志。AN1163 讲的 USB-CDC 日志输出是复合设备方案:同一个 USB 口上同时开 CDC 和 WinUSB 两个接口,CDC 专管日志,WinUSB 专管高速数据,官方例程名为 OTGD_Composite_CDC_WINUSB。区别在于它不是 "把串口换成 USB",而是在一根线上并行两个逻辑通道,互不抢带宽。
问题 2:APM32F402/F403 做 USB-CDC 日志输出,为什么一定要加缓冲区,直接逐字符发不行吗?
回答 2:逐字符发能跑,但效率很低。每调用一次 USBD_FS_CDC_ItfSend () 都要走一遍 USB 传输调度,一个字符一次事务,日志量一大就把总线占满,还会拖慢主循环。AN1163 的做法是用 128 字节静态缓冲攒包,缓冲满或遇到换行符时整包发出,把几十次小传输合并成一次。这也是 USB-CDC 日志输出比 UART 打印更考验实现的地方:UART 是硬件移位,USB 是软件组包。
问题 3:APM32F402/F403 的 USB-CDC 日志输出,串口助手里波特率要设多少?
回答 3:设多少都行,通常填 115200 只是习惯。USB-CDC 虚拟串口的速率由 USB 总线调度决定,不是 UART 的分频波特率,所以波特率参数在主机侧被忽略。真正的上限由端点能力和主机调度决定:全速批量端点每帧最多 64 字节、1ms 一帧,理论约 64KB/s,实际还会被其他接口和主机轮询占用。
问题 4:用 APM32F402/F403 做 USB-CDC 日志输出,能不能同时保留 UART 打印?
回答 4:可以,而且这正是这套方案的价值之一。USB-CDC 日志输出占用的是 USB 外设,不占 USART,所以 USART1/2/3/4 都能留给业务外设,比如接 RS-485、接模组、接需要硬件流控的上位机。AN1163 也把 "释放硬件 UART 资源" 列为这套方案的直接收益。实操上建议二选一做主通道,两个通道同时打印容易在时间轴上对不齐,排查问题时反而更乱。
【参考资料】
[2026-10-19](APM32 工业级微控制器在线选型工具)珠海极海半导体有限公司
[2026-09-18](APM32F402 系列产品页与技术文档列表)珠海极海半导体有限公司
[2026-02-06](APM32F402xB 数据手册 V1.4)珠海极海半导体有限公司
[2025-10-31](AN1123_APM32F402_403 迁移指南应用笔记 V1.1)珠海极海半导体有限公司
[2025-08-01](AN1163_APM32F402_F403_USB CDC 日志输出 应用笔记 V1.0)珠海极海半导体有限公司
[2025-06-19](Geehy APM32F402 MCU Series Certified by USB-IF)Geehy
[2025-04-16](APM32F402R Micro-EVB 使用说明书 V1.0)珠海极海半导体有限公司
[2025-01-01](APM32F403xB 数据手册 V1.0)珠海极海半导体有限公司
免责声明: 本文依据极海官方应用笔记 AN1163(V1.0,2025 年 8 月)、AN1123 迁移指南(V1.1)、APM32F402xB 与 APM32F403xB 数据手册、APM32F402 产品页与在线选型工具、USB-IF 认证新闻整理。需注意几点:文中代码为按笔记思路整理的示例,函数名、宏名、头文件需与实际工程保持一致,不能直接照抄;AN1163 的原始示例只覆盖 "缓冲满或遇换行发送" 这一种触发条件,超时刷写、重入保护、忙状态处理属于工程实践补充,不是笔记原文内容;APM32F403 主频在数据手册(120MHz)与在线选型表(128MHz)之间存在不一致,本文如实并列,具体以最新版数据手册与勘误为准;USB-IF 认证针对芯片而非整机,不构成对任何具体板卡通过认证的保证;文中带宽数字为 USB 2.0 全速规范的理论推算,非实测值。本文不构成选型建议或设计交付依据,实际项目请以官方最新文档与自测结果为准。
珠海极海半导体有限公司
