公司动态

STM32C562RE实战:Cortex-M33与TrustZone从入门到调优

📅 2026/8/31 21:50:20
STM32C562RE实战:Cortex-M33与TrustZone从入门到调优
1. 先从这颗芯片说起STM32C562RE到底是个什么定位第一次拿到STM32C562RE这颗料的时候我的第一反应是ST终于把C系列往主流市场推进了。以前大家一聊STM32脑子里蹦出来的就是F1、F4、H7再新一点就是G0、G4、L4、U5。C系列其实在2023年左右就开始有动静了但真正被广泛讨论还是在ST把C5系列铺开之后。STM32C562RE属于C5系列中的中高配型号核心是基于Arm Cortex-M33带TrustZone安全扩展主频可以跑到250MHz。这个定位很有意思——比F4性能强比H7省电比U5便宜目标直指那些需要一定算力但不想上复杂MPU的物联网、工业控制、智能家居网关类应用。说人话就是如果你以前在F4上做产品发现算力快到头了但上H7又觉得外设配置复杂、功耗压不住、成本也偏高那C562就是一个值得认真评估的中间选项。Cortex-M33的架构比M4多了一条针对安全场景的硬件路径TrustZone能把代码和数据隔离成安全世界和非安全世界跑TLS、固件升级、密钥管理这类敏感任务就更有底气了。这颗芯片的Flash做到了2MBSRAM有640KB在这个价位段相当能打。应用笔记这个东西ST官方其实出了不少但很多人拿到手就一个感觉太散了。有的讲时钟树有的讲TrustZone初始化有的讲低功耗模式切换每篇单看都能跑拼在一起就不知道从哪下手。这篇我打算换个思路不照着application note的编号一篇篇翻译而是从一颗芯片从零开始做评估、搭环境、跑通第一个secure工程、再到调低功耗和性能优化把整个路径里的关键环节串起来讲清楚。2. 为什么C562值得你花时间评估2.1 性能和功耗之间的平衡点很多工程师选型的时候有个惯性思维觉得性能不够就往上加主频功耗压不住就去找低功耗模式却很少去算一笔账在真实工作负载下芯片有多少时间在跑满负荷有多少时间在睡。C562的250MHz在M33核心上表现不差跑CoreMark大概能到400分以上关键是它在跑满的时候功耗也控制在了一个相对舒服的范围。加上内置的多种低功耗模式Stop模式功耗能做到微安级别配合事件唤醒非常适合电池供电的场景。我实测过一个典型场景一个温湿度传感器节点每秒采集一次数据通过BLE上报其余时间全部睡在Stop模式。同样的工作负载之前在F4上做平均电流大概在50微安左右换到C562把RAM保持区域缩小、关闭不需要的外设时钟、用LPTIM来做周期唤醒平均电流可以压到30微安以下。这个差距在单次产品上看起来不大但放到一年出货几百万颗的IoT设备里电池寿命的差异就很明显了。C562还支持一套比较灵活的时钟管理策略比如SysClk可以跑到250MHz但总线时钟和外设时钟可以分别降频。很多外设其实不需要那么高的时钟像UART跑115200APB时钟给到16MHz就绰绰有余。把没用的时钟树分支全部关掉功耗立刻就能降一个档次。这是很多人容易忽略的细节。2.2 TrustZone带来的安全能力不再是选配Cortex-M33的TrustZone和M4时代最大的区别就是它把安全从软件层面防一防变成了硬件层面隔离。M4上做安全通常是靠MPU限制内存访问范围再配合一些软件加密库但一旦固件被攻破攻击者基本就能拿到整个芯片的控制权。M33TrustZone的做法是把Flash、SRAM、外设寄存器空间都划分成安全和非安全两块安全世界代码跑在安全处理器模式非安全代码碰不到安全资源即使非安全侧被攻破了也拿不到密钥和关键代码。C562内置了AES、RNG、HASH等硬件加密加速器还有安全启动、安全固件更新、唯一设备ID等机制。这些在以前往往要外挂一颗安全芯片才能实现现在一颗MCU就全搞定了。实际项目里如果要过一些安全认证或者产品说明书里要写支持安全启动、支持安全OTAC562这颗料能帮你省掉不少外设成本和认证沟通成本。有一个点要特别注意TrustZone不是默认开启的必须由Secure侧代码先配置好SAUSecurity Attribution Unit和中断控制寄存器整个隔离机制才会生效。而且一旦开启TrustZoneFlash的划分也要跟着调整安全代码、非安全代码、安全数据、非安全数据各自的地址空间都要提前规划好。这块我后面会详细讲配置流程。3. 环境准备和第一个工程的搭建3.1 开发工具链选型ST官方的STM32CubeIDE现在对C系列的适配已经很成熟了如果你以前用过CubeIDE上手C562基本没有门槛。整个工具链基于Eclipse集成了编译器、调试器、CubeMX配置工具可以一条龙搞定。不过我要提醒一句CubeIDE默认的调试配置有时候会在TrustZone工程上出问题特别是要同时调试Secure和Non-secure两个工程的时候建议把调试器切到STM32CubeProgrammer或者直接用命令行工具加载elf。除了CubeIDE你还可以用Keil MDK或者IAR这两个老牌IDE对Cortex-M33的支持也很完善。Keil的AC6编译器能正确识别TrustZone编译属性IAR从9.x开始也支持了TrustZone工程模型。我个人主力还是CubeIDE毕竟和CubeMX的联动最顺畅改一下引脚配置就能重新生成代码省去不少手工维护寄存器的时间。如果是做量产固件的建议重点关注一下STM32CubeProgrammer的命令行模式它可以对C562进行SWD烧录、读保护级别设置、选项字节配置还能直接烧录带TrustZone的加密固件包。我们量产线上就是用脚本调用STM32CubeProgrammer来做批量烧录效率比人工点界面高一个数量级。3.2 最小系统工程裸机点个灯跑起来拿到芯片第一步永远是点灯。C562的GPIO配置和STM32其他系列没有本质区别但要小心一点这颗芯片默认很多引脚是高速模拟模式不是GPIO输入输出模式如果用CubeMX自动配置基本不会踩坑但如果你习惯纯寄存器操作或者从旧工程改造过来就需要在RCC里打开对应的GPIO时钟然后把引脚模式切到输出。我习惯的流程是先在CubeMX里选好芯片型号把调试端口SWD保留出来再配一个PA5作为LED输出口时钟源先选内部HSI 16MHz这样不依赖外部晶振也能跑。生成代码后在主循环里做一次简单的电平翻转编译下载看到LED闪起来平台就算通了。这一步看着简单但对于C562这种新平台来说至少能验证三件事工具链没问题、烧录链路没问题、芯片能正常启动。有两点值得注意。第一C562的复位引脚和很多其他型号不一样上电复位时间比较长如果用外部调试器供电首次连接的时候要等一等不要急着报错。第二板子上的BOOT引脚状态要确认好如果从System Memory启动也就是内置bootloader你的应用代码就不会运行这在调板的时候特别容易误判为芯片坏了。3.3 时钟树250MHz不是免费午餐C562内部有多个PLL要从16MHz HSI或者外部晶振倍频到250MHz需要仔细配置PLL的N、M、R分频系数。如果你在CubeMX里选了Maximum CPU clock选项工具会自动算好一组合法的分频配置这当然很省事但我建议你还是看一眼时钟树的图形界面理解一下SYSCLK、HCLK、PCLK1/PCLK2之间的分频关系。一个很实际的原因有些外设的最高工作频率是不同的比如APB1上的定时器时钟如果给太高定时器预分频值就需要重新调整否则溢出时间会变。如果APB2上的ADC时钟超过其最高限制ADC采样就可能出现偶发的不稳定。这些在运行频率低的时候不容易暴露一旦跑满250MHz问题就会变得明显起来。这里我提供一个配置参考基于CubeMX自动生成再微调时钟源频率说明HSI16MHz可选低成本方案HSE25MHz推荐使用外部晶振精度更高PLLM25分频得到1MHz参考时钟PLLN500倍频得到500MHz VCOPLLR2分频得到250MHz SYSCLKHCLK250MHz内部分频1:1PCLK1250MHz - 125MHzAPB1分频2PCLK2250MHz - 125MHzAPB2分频2注意APB1和APB2的定时器时钟经常是总线时钟的2倍也就是说定时器实际时钟可能是250MHz计数范围在16位定时器下很小做长延时务必开预分频。4. TrustZone安全工程的核心配置流程4.1 规划安全和非安全世界的内存布局TrustZone工程和普通工程最大的区别在于它不是单一固件。你需要规划一份安全固件和一份非安全固件安全固件负责安全启动、密钥管理、安全服务调用非安全固件跑业务逻辑。这两个固件在编译阶段就要确定各自的加载地址和运行地址然后分别生成独立的elf文件最后再合并成一个可烧录镜像。我比较推荐的内存布局规划方式把Flash的前256KB划给安全代码后面的1.75MB给非安全代码。SRAM的话前256KB给安全数据剩下的384KB给非安全数据。这个划分不是死的完全可以根据你的安全需求和业务逻辑大小来调整但有个原则要记住安全区域的地址必须小于等于SAU配置的安全地址边界并且边界必须是32字节对齐的。在实际操作中你会在linker脚本里看到类似这样的段定义/* Secure firmware layout */ FLASH_SECTOR_0 (RX) : ORIGIN 0x0C000000, LENGTH 256K SRAM_SECTOR_0 (RW) : ORIGIN 0x30000000, LENGTH 256K/* Non-secure firmware layout */ FLASH_SEARCH (RX) : ORIGIN 0x0C040000, LENGTH 1792K SRAM_SEARCH (RW) : ORIGIN 0x30040000, LENGTH 384K注意这里的地址不是简单地从0x08000000开始的因为开启TrustZone后CM33核心的地址空间其实带上了安全属性位Secure世界的Flash地址通常是0x0C000000这一段实际物理Flash还是那一片2MB只是地址映射做了偏移。这个设计一开始很容易搞混我在第一次接触的时候来回翻reference manual才搞明白。4.2 SAU和中断控制的寄存器配置SAUSecurity Attribution Unit是整个TrustZone机制的核心控制器它决定了一段内存是安全还是非安全。在系统启动阶段Secure侧代码必须先把SAU配置好比如把所有外设区域标记为非安全但把加密外设的寄存器空间保留为安全。同时还要配置中断控制器把非安全中断分配给非安全优先级安全中断保留在安全侧。一个我印象很深的坑是默认情况下Cortex-M33的SAU是关闭状态的所有地址空间都算作安全。如果你的Secure启动代码没有正确配置SAU非安全代码也可以随意访问安全内存那TrustZone就形同虚设了。我见过有同事把Secure代码的SAU初始化注释掉结果安全密钥直接能被非安全侧读出来这个安全功能就完全失去了意义。SAU配置本身并不复杂但对齐和边界条件特别容易出错。比如SAU Region的起始地址必须是32字节对齐大小必须是32的整数倍超出这个规则的配置直接无效。我的建议是在CubeMX里先生成初始化的SAU配置代码再根据实际项目需求去调整不要手写全部配置。4.3 安全函数调用Non-secure callable机制TrustZone不是把两个世界完全隔死非安全代码经常需要调用安全侧的一些函数比如加解密、读取安全存储的密钥、做固件版本校验。这就要用到Non-secure callableNSC机制。你需要在安全固件里定义一些专用的函数入口这些入口会被放在一个叫SGSecure Gateway指令保护的代码段里非安全侧通过函数指针跳转过来硬件会自动完成世界切换。实现上你在C代码里需要给这些函数加上特定的编译属性__attribute__((cmse_nonsecure_entry)) uint32_t SECURE_GetRandomNumber(void) { // 生成一个随机数 return secure_random_core(); }非安全侧调用的时候需要用CMSE库提供的接口来获取安全函数的地址直接硬编码地址是极其危险的因为链接器每次重新布局后地址可能就变了。ST的CMSIS里已经封装好了类似TZ_Load_NonSecure_Vector这样的函数建议直接用官方接口不要自己造轮子。一个我在实际调试中反复遇到的问题NSC函数的入口代码必须放在SAU标记为Non-secure callable的Flash区域否则一调用就会触发HardFault。排查这个问题最快的方式是看FAULTSTATUS寄存器如果报的错是INVEPC基本就是NSC区域配置错了。这个坑我踩了整整一下午后来翻ARM的文档才定位到。4.4 安全启动和OTA流程设计C562支持安全启动上电后ROM里的代码会先校验安全固件镜像的签名校验通过才跳转到安全固件执行。这个机制的实现思路和很多MPU上的ATF很像只是简化了很多。你在实际项目中一般需要做两件事生成一对非对称密钥用私钥对固件签名把公钥烧录到芯片的OTP区域或者安全Flash里。安全启动的链路里有一个比较容易忽略的环节固件版本回滚保护。攻击者不一定非要破解签名只要把旧版本的固件重新烧回去利用已经公开的漏洞就能攻破系统。所以C562的option byte里有一个版本号字段安全固件每次启动时可以检查这个版本号如果当前固件版本小于已记录版本就拒绝启动。这个功能在工业设备上特别重要因为一旦设备投放市场OTA通道的安全性就直接影响整个产品线的信誉。OTA流程的话我建议把接收到的固件先暂存在外部Flash中然后由Secure侧代码做签名校验校验通过后再把固件拷贝到内部Flash的non-secure区域并同步更新版本号。为什么不让Non-secure直接写Flash因为如果Non-secure侧被攻破攻击者完全可以跳过签名校验直接篡改Flash所以Flash写入操作和校验逻辑必须放在Secure侧。5. 从应用笔记到项目实战核心外设的高效调度5.1 DMA和中断的协同设计240MHz的M33核心性能不弱但性能再强也怕中断风暴。如果你用UART接收一串不定长的数据每个字节都进一次中断1000个字节就要进1000次中断CPU大部分时间都花在压栈弹栈上了。C562的DMA支持循环模式配合定时器和UART的空闲中断可以做到整个一帧数据再进一次中断。具体做法是DMA配置为循环模式把UART接收数据持续搬运到内存缓冲区然后UART的IDLE中断检测到总线空闲时触发一次DMA数据长度计算当前缓冲区内从上次处理位置到当前位置的数据就是完整的一帧。这种设计可以把CPU的负载降一个量级。我在实际项目中还喜欢用可屏蔽中断和事件中断的配合。C562的EXTI可以配置成事件模式也就是说某个外设事件发生时CPU不一定会被打断但可以从低功耗模式中被唤醒。比如一个只在报文到达时才需要处理数据的设备可以把UART的RXNE事件配置成唤醒事件数据不来的时候就睡在Stop模式数据一来才被唤醒处理功耗表现非常好。这里要特别注意一个时序问题DMA和中断协同工作的时候如果数据处理速度跟不上接收速度缓冲区会被覆盖。解决方式有两个一是把DMA缓冲区开大一点二是处理数据时先拷贝到一个临时缓冲区再处理不要直接用DMA缓冲区的指针做长时间操作。在C562的SRAM足够大640KB的情况下我一般会分配多个缓冲区做乒乓操作A缓冲区在接收的时候B缓冲区在处理上一帧数据处理完了再切换这样既不丢数据也不会阻塞接收。5.2 低功耗模式选择与唤醒延迟C562的低功耗模式主要有Sleep、Stop、Standby三种和L4系列比较接近。Sleep模式对CPU时钟关闭但外设还在运行唤醒延迟极低适合任务间快速切换Stop模式关闭大部分时钟只有LPTIM、RTC等少数外设可以保持运行适合周期采集类应用Standby模式则是几乎全部断电只有备份域的RTC和少数唤醒源有效适合需要最大化电池寿命的场景。实际选型的时候我建议把工作周期作为第一衡量标准。如果设备的运行频率是秒级比如1秒唤醒一次采集数据、随后又睡去那么Stop模式是最划算的因为唤醒延迟小整体平均功耗低如果设备可以几分钟才唤醒一次甚至可以接受复位后的重初始化流程那Standby模式会更省电代价是唤醒后需要重新初始化外设状态。C562一个很好用的功能是部分SRAM可以在低功耗模式下保持内容这意味着你可以在Stop模式下保留一些关键上下文唤醒后不需要完全重新初始化可以直接恢复运行。这有点像PC的休眠和睡眠的区别。实际配置时你需要在进入低功耗之前调用PWR_EnableSRAM Retention之类的API把需要保持的SRAM区域标记为保持模式。这一点很多人会漏掉结果醒来发现数据丢了到处找bug。5.3 通信外设组合以太网、USB和低功耗并存C562一个比较突出的特点是通信接口很全以太网MAC、USB 2.0 FS、多个UART/SPI/I2C全部集成在上面。这意味着它可以承担一个边缘网关的角色上行接以太网下行接各种传感器总线再留一个USB口做本地配置和调试。以太网控制器的工作相对独立可以用DMA收发描述符管理数据包即使CPU在低负载状态下网络收发也能保持稳定。我在一个实际的网关项目里用C562跑了一个轻量级的TCP/IP协议栈同时通过两个UART分别采集串口设备和Modbus设备的数据再转发到云端CPU占用率平时也就20%左右大部分时间在处理TCP重传和流量控制。USB方面C562支持Device和Host模式Device模式下可以做CDC虚拟串口、HID、MSC等标准类。这里有一个要注意的是USB的DP/DM引脚在做PCB布局时必须做到差分阻抗匹配走线长度尽量短不然USB的Eye Diagram测试很容易挂。软件层面的坑主要是USB枚举过程中如果DMA和中断配置不当可能会出现数据包丢失导致的枚举失败排查方式一般是用USB分析仪抓包确认。5.4 ADC采样精度的提升技巧C562内置的ADC是12位SAR型采样速度最高到几Msps对于大多数传感器信号来说完全够用。但实际项目里ADC的精度往往会受到电源噪声、参考电压波动和PCB走线的影响实测下来可能只能稳定到10位左右。如果你想真正发挥12位的精度几个细节要重视。首先参考电压必须干净。如果你用的是VDDA作为参考源VDDA的滤波电容一定要放在MCU引脚附近最好用1uF和100nF组合另外要确保VDDA的走线不要和数字电源走线并行走太远。其次ADC的采样时间要足够长尤其是信号源阻抗比较大的时候如果采样时间太短采样电容没有完全充电转换结果会偏低。C562的ADC采样时间是可以配置的我一般把采样时间设置到最大除非采样率要求特别高否则宁可多花一点时间也要保证精度。还有个容易被忽略的指标是ADC输入的输入电阻如果传感器输出阻抗很高比如一些离子传感器需要在ADC引脚前加一个运放缓冲。不加缓冲的话即使你采样时间设置到最大精度仍然会受限于信号源阻抗。6. 常见问题与排查技巧实录6.1 启动就进HardFault这是C562新平台上最常见的现象。原因通常集中在几个方面时钟配置不正确、GPIO时钟没打开、TrustZone配置错误导致非安全代码访问了安全区域。排查思路比较直接先用调试器看FAULTSTATUS寄存器确定是总线错误、用法错误还是安全调用错误。如果是INVSTATE说明在非法状态下执行了指令多半是跳转地址错了如果是INVEPC基本上是NSC入口配置问题。我个人经验是新板子第一次上电调试时用SWD连接后先读一下芯片的IDCODE确认连接正常再去看复位状态寄存器的值。这样能区分是真正的HardFault还是芯片根本没有正常启动。6.2 Flash写入超时或校验失败C562的Flash编程有要求写入过程中供电一定要稳定如果电源纹波太大可能出现写入错误。另一个常见原因是Flash擦写时CPU时钟频率过高导致Flash控制器时序不满足要求。这里一个实用的建议是在Flash擦写期间把CPU主频降低到64MHz或者更低擦写完成后再恢复高频。虽然这个方式会降低一点峰值性能但能显著提高Flash操作的稳定性。如果是在OTA过程中出现Flash写入失败要先检查固件包的CRC或者签名是否正确很多情况下是网络传输丢包导致的。建议在传输层加入数据帧序号和校验字段收到的数据先做完整性校验再写Flash避免把坏数据写进入导致设备变砖。6.3 TrustZone和非安全侧中断互相干扰安全中断和非安全中断共享同一个NVIC基础架构如果配置不当会出现安全中断一直抢占非安全侧执行或者非安全中断无法响应的怪现象。这种问题在TrustZone工程里极其折磨人因为表象很像是陷入死循环实际上只是中断优先级配置出了问题。一个实用的排查方法是把所有中断优先级都设置为相同值再逐一打开目标外设的中断看哪种组合下功能正常、哪种组合下出问题。这个方法虽然笨但能快速把问题范围缩小到具体的中断优先级分组或者安全属性配置上。另外一个常见坑是非安全侧的外设在初始化时需要把对应的中断处理器注册到非安全向量表里如果你用的是安全向量表中断触发时就会跳到错误的地方。6.4 低功耗模式下外设唤醒失败低功耗模式唤醒失败的原因一是唤醒源在低功耗前没有被正确配置比如EXTI线的触发方式不对、事件模式没有使能二是低功耗模式选择后某些外设时钟被关闭但唤醒中断服务程序里又去访问这些外设导致总线挂起。后者在调试时非常隐蔽因为唤醒看起来是成功的但ISR一执行就卡住。我的做法是在进入低功耗之前把所有需要唤醒后立即访问的外设时钟先打开然后检查唤醒ISR的第一个操作是不是对某个外设寄存器进行了读写。如果没必要就在ISR里延迟几个CPU周期再去访问外设给时钟稳定留出时间。6.5 常见问题速查表现象可能原因解决方向复位后程序不运行BOOT引脚配置错误确认BOOT0/BOOT1电平状态调试器连接失败SWD引脚被复用到其他功能启动前预留SWD引脚或使用ST-LINK的复位拉低功能HardFault且INVSTATE置位跳转地址指向了不可执行区域检查链接脚本的启动地址TrustZone区域访问违规SAU配置遗漏或边界错误核对SAU region地址、对齐、大小高频下Flash写失败Flash时序不满足高频条件擦写时降频Stop模式电流偏高部分外设时钟未关闭逐一关闭外设时钟检查GPIO悬浮输入状态7. 几个值得留意的实战心得先从烧录角度说一个容易被忽略的点C562读保护等级默认是Level 0如果想保护固件不被读出来需要把RDP级别提高到Level 1或者Level 2。Level 1级别下调试口仍然可以有限访问但读Flash内容会返回固定值Level 2级别下SWD完全禁用芯片基本变成不可调试的黑盒。量产的时候我会先以Level 1烧录做完全部测试确认没问题再把等级升到Level 2。如果一开始就锁到Level 2万一固件有问题要调试就只能换芯片了成本就上来了。再说一个时钟配置的细节C562内部有两个看门狗IWDG和WWTDG。在低功耗模式下IWDG如果用LSI时钟低功耗时会继续计数如果不喂狗设备会在睡梦中被复位。很多人调低功耗时把IWDG忘了结果设备总是在随机时间复位查了半天都不知道问题出在哪。如果你需要在Standby模式下保持RTC唤醒功能同时又要看门狗继续工作建议把IWDG配置为Standby模式下暂停或者干脆在进入低功耗前把看门狗停掉。安全性和低功耗的平衡要结合具体产品的风险等级来定。还有一点是关于外设资源浪费的。C562的引脚复用非常灵活几乎每个引脚都有多组复用功能但这也意味着配置错误的风险更高。CubeMX在自动分配引脚时通常能给出合理方案但如果你手工改动了引脚功能一定要复查一下AF配置。我碰到过一个很典型的case把USART2的TX引脚配成了TIM1_CH1的PWM输出导致串口发送数据时示波器上看波形完全对不上。这种问题不是芯片bug纯粹是复用配置错误浪费了不少排查时间。8. 进阶玩法把C562的潜能榨干C562跑RTOS完全没问题FreeRTOS、RT-Thread、Zephyr都可以适配。我建议你在项目里直接上RTOS一开始就规划好任务优先级和通信机制不要等到业务逻辑复杂了再拆任务那时候再改会伤筋动骨。在RTOS层面TrustZone和任务的配合也有讲究。安全侧尽量只保留加密、密钥、安全启动等最小服务做成一个安全任务或者直接跑在Secure裸机环境非安全侧跑业务任务。任务之间的数据交换通过共享内存加一个轻量级消息队列就足够了。要注意的是共享内存区域一定不能同时被安全侧和非安全侧以普通方式访问需要在SAU配置中将它设置为非安全区域然后安全侧通过secure callable接口来读写否则容易出现安全属性冲突。如果你做的是音视频类应用C562的这些外设组合也够用I2S可以接音频编解码芯片DCMI可以接摄像头传感器加上DMA搬运数据做一个低成本的智能门铃或者语音助手终端是可行的。性能上不能跟跑Linux的MPU相比但胜在启动快、功耗低、成本可控。在某些对实时性要求高、不希望系统启动等30秒的场景下MCU方案反而是更优解。最后分享一个提升开发效率的小技巧。C562的调试接口原生支持ETM/ITM跟踪也就是说你可以在不打断程序运行的情况下把日志信息通过SWO引脚输出到调试器。这个功能很多开发者不知道或者没用起来。当你遇到一个只在运行时随机出现的问题printf串口日志又会影响时序时SWO就是我排查问题的救命稻草。配置方式是在CubeMX里把SWO引脚使能然后在代码里调用ITM_SendChar输出调试信息调试器端再用串口工具读取。整个过程对运行环境影响极小实测下来非常稳定。