ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

STM32调试踩坑全记录:从Flash下载失败到时钟串口项目

STM32调试踩坑全记录:从Flash下载失败到时钟串口项目 又双叒叕卡在Flash下载失败了这块STM32板子我已经折腾了两个小时拔插USB线、重装驱动、换下载器最后发现只是Flash算法选错了一个选项。做STM32开发调试久了你会发现真正消耗时间的往往不是业务逻辑而是那些看似不起眼的小坑工程模板不对、下载器连不上、时钟频率跑偏、串口丢数据。这篇文章是我这些年做STM32开发调试踩坑的记录从环境搭建、下载调试、时钟定时、串口通信到完整项目一步一步复盘典型坑的根因和排查思路希望能让你少走点弯路。1. 工程模板与开发环境还没写业务代码就先碰到一堆坑1.1 标准库和HAL库的区别先搞清楚你在跟谁打交道很多刚上手的人会在百度上搜“STM32库函数和标准库有什么区别”这两句话背后的坑太深了。ST官方当前主推的是HAL库和LL库也就是STM32Cube固件包里的东西网上大量老教程用的是标准外设库Standard Peripheral Library简称标准库。标准库底层寄存器操作更透明一个外设初始化函数能把你希望配置的寄存器都写明白断点调试时能直接看到寄存器变化HAL库封装层更厚因为它要照顾全系列芯片像HAL_UART_Transmit这种函数内部会带超时机制和状态机如果用不好一个简单的串口发送都可能卡死你。坦白说如果项目是老的F1芯片、代码量不大、想快速定位底层问题用标准库没毛病如果是新项目、需要CubeMX图形化配置、还打算在F4/H7之间迁移那最好直接用HAL库。最忌讳的是同一个工程里标准库和HAL库混着用我见过有人为了省事外设初始化用CubeMX生成的HAL代码然后又直接调用标准库的函数结果编译能过跑到一半外设状态被两个库的管理代码互相踩找问题找到怀疑人生。从调试经验来看我建议选库的时候先看启动文件。标准库工程里启动文件叫startup_stm32f10x_md.s这类HAL库工程里启动文件叫startup_stm32f103xb.s这类最典型的坑是启动文件里中断向量没写对比如你用标准库跑F103ZE启动文件却是ld.s低密度系统时钟、串口中断全都对不上这种错误除非逐行检查链接脚本否则很难发现。1.2 Keil5同时兼容C51和STM32安装目录和License的坑网上流行一句话“Keil5怎么同时兼容C51和STM32”实际上你完全可以把MDK-ARM和C51装在同一个Keil目录下但需要分开。我踩过的坑是这样的先装了C51版再把MDK安装目录选到了同一个文件夹结果打开工程时Keil老是找不到ARM的编译器。正确做法是在安装时分别选择两个不同的根目录比如C:\Keil_v5\ 下面再分MDK和C51两个子目录。装完之后菜单栏的Project - Manage - Pack Installer 里面会同时显示ARM和C51的packLicense Management界面也能分别看到两个Toolset。切换的时候注意Keil的License是分开的如果你只买了MDK授权C51的Target加载时会报License过期这不是破解问题是真正需要单独授权的。另外一个很隐蔽的坑是C51、MDK共用一个UV4.exe。有时候明明装了MDK双击C51工程文件却提示找不到芯片是因为文件关联被C51抢了。处理办法是右键工程文件打开方式选中MDK的UV4.exe并勾选“始终使用此应用”。1.3 VSCode写代码、Keil调试环境配置的边界要拎清现在很多人的开发方式是VSCode写代码、Keil编译下载调试。这个组合本身没问题但有个前提你得知道VSCode里的语法检查和真正编译用的工具链不是一回事。VSCode用C/C插件配合.vscode/c_cpp_properties.json里的includePath、defines来提供智能提示。很多人配置的时候只加了STM32固件库的include忘了加宏定义导致跳转、提示全是红色波浪线其实代码能编译过。我的建议是先让Keil编译通过然后再去配置VSCode。从Keil的Options for Target - C/C - Include Paths把所有头文件路径复制进includePath再把Define里的宏复制到defines比如STM32F10X_MD这类。还要注意__CC_ARM这个宏如果你用armcc工具链VSCode的IntelliSense会不认识Keil的编译器扩展语法可以在配置里加intelliSenseMode: windows-gcc-x64或者干脆关闭IntelliSense只当高级文本编辑器用。至于在VSCode里做真正调试很多“保姆级教程”会教你用OpenOCD pyOCD ST-Link我试过两次能跑通但坑也不少OpenOCD的target配置、stlink版本、reset_config、DAP的SWD频率任何一个不匹配都会导致连不上目标。如果只是为了调试一个特定芯片我建议直接用Keil自带的调试器VSCode专注编辑就好别把工具链复杂度引入到调试环节。1.4 芯片包与工程模板同代码换颗芯片就编译不过STM32芯片包安装也是一大坑。有些人下载固件包时选错版本比如STM32CubeF1的版本太新HAL库里的API改了名字原来调用HAL_GPIO_WritePin没问题更新后某些函数参数变成uint32_t还是GPIO_PinState类型不匹配直接warnning。新版pack和旧工程模板之间最常见的失败是启动文件和链接脚本.sct不匹配。我接手过一个F103RET6的旧工程原开发者用的启动文件是md.s换成F103RCT6后flash小了代码却还在硬链接到2MB地址一编译就报“overflow area”。如果只是芯片封装的另一种型号强烈建议到Linker -- Scatter File里把.sct内容删掉让Keil自动生成然后选对Flash大小。配置好Device型号后检查宏定义里的芯片密度如STM32F10X_HD同时确认启动文件是hd.s这组三件套必须一致。这三处任何一个不匹配程序加载后跑不到main函数调试时PC指针乱跳和芯片包版本没关系。2. 下载器和调试器板上“幽灵”故障的排查链路2.1 Flash Download Failed先从算法、速度、读保护三座大山查起那个典型的错误信息是load D:\\STM32 Project\\...\\Objects\\project.axf error: Flash Download failed - Cortex-M3。看到这个先别急着怀疑下载器坏了。按我排查的固定顺序来第一检查Target Options里Utilities设置的Flash编程算法是不是匹配。常见情况是芯片明明选的是F103C8但algorithm列表里却只有STM32F10x High-density Flash低容量芯片用了高密度算法下载时地址越界。C8是高密度吗不是C8是中等密度算法要选STM32F10x Med-density 64K或类似。类似地F407要选STM32F4xx Flash选成F1的算法必失败。第二把下载调试器的速率调慢。ST-Link的Max Clock可以试着降到4MHz甚至1MHz尤其在杜邦线比较长、板子供电不稳的时候高速SWD很容易超时。很多人用的ST-Link是盗版或廉价版固件兼容性差SWD频率高了直接掉线调低就好了这是常见但说不出口的坑。第三检查芯片是不是被读保护了。如果之前用ST-Link Utility或CubeProgrammer不小心设置过RDP级别或者芯片本身因为早期的固件开启了读保护Keil下载就会报Flash Download failed。这时候用STM32 ST-LINK Utility连接Target - Option Bytes把Read Out Protection Level从Level 1降回Level 0确认后会执行全片擦除然后再回Keil下载。还有一个很容易被忽略的坑是复位电路。如果板子的NRST引脚被一个过大电容接到地或者被人当普通GPIO控制下载器在连接时执行复位序列就会失败表现为一直Unable to connect。这里教一个救急诀窍不要点Download先进入Options选择“Connect under Reset”这样在复位线为低电平期间强制连接能绕开很多启动异常。2.2 复用JTAG引脚却把调试口也关了一次手贱的救砖记录“STM32禁用JTAG”这个操作会带来一个连锁坑一旦你在程序里执行了GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable, ENABLE)它不仅关掉了JTAG也同时关掉了SWD的调试引脚。这时候一是程序跑飞了二是你没法再通过SWD下载程序。很多人的第一反应是“完了片子锁死了”其实没有。自救方式有两条。最简单的是找个串口工具把BOOT0引脚拉高复位然后通过串口ISP重新烧录一个干净程序把BOOT0接回来即可。更推荐的是ST-Link Utility里的Connect under Reset打开Utility在设置里选“Connect under Reset”按住目标板的复位键不放点击Connect等连上后再释放复位便会进入调试状态。这时你还能读出Flash内容如果还能访问寄存器就先把“禁用SWJ”的寄存器恢复或者直接全片擦除。我实际遇到的情况是给一个量产板做IO复用把PA13/PA14接到按键上代码一进去就把SWJ全部禁用第二天产线没法下载固件。后来在程序里做了妥协上电前3秒不执行禁用代码给产测软件一个窗口期。从这个坑得到的教训是真要复用调试口也应该在main函数里先初始化一个软定时器等外部的“允许禁用”标志位成立后再调用SWJ_Disable留条后路。2.3 ST-Link Utility 和 CubeProgrammer救砖、读保护、速度测试要说调试辅助工具ST-Link Utility虽然名义上被ST停了后续支持但它依然是很多老工程师离不开的工具。它最大的用途是整片擦除、Option Byte编程、以及直接读写内部Flash。量产产线里我常用Utility写一个自动脚本加载hex文件选择“Full Chip Erase”后“Program Verify”再设置Option Bytes一键完成。CubeProgrammer是目前官方主推的工具推荐新项目用它。它是图形界面也能命令行操作。和Utility对比CubeProgrammer的优势是支持ST-Link以外的很多调试器还支持UART、USB DFU、SWD等多种连接方式。不过它也有自己的坑连接F1老芯片时如果下载器引脚接触不好会比Utility更容易报“Connection error”另外它默认会尝试读取整个芯片信息芯片处于某种busy状态时好几秒没响应让人以为卡死了。无论用哪个工具建议在量产前把“连接失败后的自动重试”调低一些。有些产线的地面有静电干扰SWD线稍微一晃就断工具自动重试能大大提高效率。另外下载器线缆最好用带屏蔽的杜邦线超过20cm就很容易在高速下载时不稳定。3. 时钟、定时器与延时精度问题背后的隐形杀手3.1 时钟树晶振型号没改外设全乱STM32时钟树是调试中最容易被忽略的隐形黑手。很多人从某宝买的最小系统板板上晶振是8MHz的但有些国产开发板用的是12MHz甚至25MHz晶振。代码里如果沿用标准库的SystemInit默认值8MHzHSE_VALUE就不对结果是PLL倍频后系统主频跑偏串口波特率、定时器定时、PWM频率全都对不上。调试方法很简单先用MCO引脚输出系统时钟。在F103上配置GPIO_Remap_MCO把PA8设置为MCO输出再用示波器看频率是不是预期的72MHz如果没有示波器可以先用串口打印一个已知时间比如每秒翻转一个LED凭感觉也能判断差了多少。如果确认为晶振型号不匹配需要在编译宏里修改HSE_VALUE。标准库通常在stm32f10x.h里定义#define HSE_VALUE ((uint32_t)8000000)HAL库则在stm32f1xx_hal_conf.h里配置。这个值的优先级非常高很多人的外部晶振明明是25MHz却只改了时钟初始化函数改完后串口打印全乱码就是因为HSE_VALUE没改。3.2 delay函数卡死SysTick到底被谁抢走了“STM32延时函数delay卡死”这个问题在搜索引擎里常年有热度。我自己也卡过一次而且是发生在一次看起来毫无关系的功能升级后。查了半天发现是FreeRTOS启动之后我原来的delay_ms函数还在用SysTick做延时但FreeRTOS内核已经用SysTick做系统节拍了两个代码同时操作SysTick寄存器延时自然就卡死了。还有一个常见场景CubeMX生成的HAL库工程里HAL_Init已经配置了SysTick作为HAL时基然后你在main里又调用了自己写的Delay_Init再次修改SysTick的LOAD/VAL寄存器此时HAL内部的状态机就会紊乱表现为HAL_Delay有时正常、有时超级慢。解决这类问题很简单要么全程用HAL_Delay要么自己用一个独立定时器做延时完全绕开SysTick。在中断服务函数里调用带阻塞的delay是我见过最多的卡死类型。当一个优先级较高的中断里调用了delay而这个delay依赖SysTick的PendSV或低优先级中断来更新计数就会造成死锁。举个典型案例定时器中断里用delay等一个GPIO稳定结果整个系统卡死。正确方案是用DWT的CYCCNT寄存器做微秒延时它不依赖操作系统和中断或者干脆用状态机把延时改成事件调度。3.3 测频法、输入捕获与编码器模式计数器溢出的数学题做转速、流量检测时经常要测频率。测频法固定闸门时间计脉冲数适合高频信号因为在高频下计数误差小测周法测量单个周期的时间适合低频信号因为测周期时误差占比小。STM32的定时器输入捕获天然适合测周法用上升沿捕获CCR值前后两次CCR之差就是周期对应的计数个数。但如果信号频率很低、定时器计数溢出CCR的差值就会出现极大值所以必须配合溢出中断判断溢出了多少次再乘以ARR周期。我在这里踩过的一个坑是输入捕获中断和定时器更新中断的优先级相同或设置不当捕获事件发生时定时器正好溢出更新中断比捕获中断晚来几微秒导致先用了一个“跨溢出的CCR差值”计算出的周期瞬间变大了几万倍。解决方法是在捕获中断里同时判断溢出标志并根据溢出次数修正差值还要注意临界区保护防止读取CCR和溢出计数的过程中发生上下文切换。编码器模式相对更复杂一点。用TIM1或TIM2的编码器接口时最简单的坑是编码器A/B相接反计数器会反向计数。这不是程序问题是接线或方向定义问题。另一个坑是编码器计数值在正转和反转切换时更新中断里的计数方向标志和当前计数值的关系没处理好特别是在两个脉冲几乎同时到达、方向信号抖动时会出现计数多跳几格的现象。实际操作中我建议把编码器计数范围设置成0~65535自动循环而不是用有符号数直接读然后在固定周期内根据更新中断次数判断绝对圈数同时过滤掉宽度小于1ms的毛刺信号。3.4 PPS、Biss-C这类“生僻协议”别被名字吓住热词里还出现了“stm32实现pps”和“stm32 biss-c解码”这两个看着唬人本质都是“在特定时钟边沿捕获/输出一个精确脉冲”。PPS就是每秒一个脉冲一般用于时间同步可以用定时器PWM模式输出1Hz也可以外部捕获来对时。它的坑在于PPS信号沿很窄捕获时定时器时钟源和外部信号之间的延迟会导致微秒级抖动需要利用TIMx_OR的TI映射或外部时钟源1模式做硬件同步而不是在中断里软件记录时间戳。Biss-C是绝对值编码器协议类似SPI但有自己的时序和CRC校验收尾。做Biss-C解码我遇到的第一个问题是编码器时钟频率和MCU主频之间的建立时间不匹配导致高位数据能读对、低位CRC校验老失败。解决办法是把SPI时钟调成编码器允许的中间值并在CS释放前留几个时钟周期另一个坑是中断频繁触发导致编码器数据被覆盖最好用DMA读SPI接收寄存器并配合硬件外部中断判断“busy”和“ready”之间的窗口。这些协议本身不复杂复杂的是时序边界和中断竞争。4. 串口与通信外设数据收发时那些看不见的坑4.1 USB虚拟串口电脑不认设备先看配置和堆栈很多人的STM32板子带有USB口连上电脑后能正常枚举成一个串口但如果你自己画板或者从零移植CDC类那就容易踩坑了。USB虚拟串口“设备不能识别”“设备描述符请求失败”这类问题一半以上出在堆栈设置上。CubeMX生成的工程如果Heap Size太小USB库在初始化时会申请大块内存失败。我习惯把堆栈设为0x1000以上C异常或动态内存较多的工程还要再加大。另一个坑是USB时钟必须精确为48MHz。F1/F4的USB外设时钟来自PLLQ或PLLCLK如果系统主频或PLL配置不对USB模块的传输会出现大量CRC错误电脑看起来是“无法识别的USB设备”。检查点确认PLL输入时钟和HSE_VALUE一致用CubeMX时钟树界面看USB时钟是否为48MHz。发送卡死的问题也别忽视。HAL库的CDC_Transmit_FS函数内部有while循环等待上一次传输完成如果你在中断里调用它高优先级会阻塞USB的中断直接卡死。正确做法是设一个发送标志在主循环里调用或者用DMA方式发送。另外很多有人在虚拟串口发送大文件时发现不定时丢包其实是因为CDC端点每次最大包大小为64字节大于这个数的帧要在应用层做分包和流控。4.2 用串口调PID、控制伺服485方向切换和帧边界是关键用串口调试PID参数也是“调参一时爽找坑火葬场”的典型。常见的流程是用上位机通过串口发送目标值和PID参数STM32返回当前速度/位置。最容易出的问题是上位机发一帧数据下位机receiving中断里直接做PID计算和参数写入而PID计算耗时超过一帧数据的串口间隔导致字符丢失或帧错位。解决方法是串口中断里只做数据搬运把收到的字符塞进环形缓冲由主循环或一个低优先级任务解析帧。485控制伺服电机要特别注意半双工方向切换的时序。有些芯片的RS485收发器比如MAX485的RE/DE是同一个引脚发送前要拉高DE发送后必须等最后一个字节的停止位彻底发出再拉低否则最后一字节会被截断伺服端就收到一个错误帧。用STM32标准库时我习惯在发送完最后一个字节后等待USART_GetFlagStatus(USART_FLAG_TC)置位再切换方向如果用HAL库可以用HAL_UART_Transmit的回调函数来完成切换而不是在发送函数返回后立刻切换。此外帧边界的判断也常出问题。如果以0xAA 0x55开头0x0D 0x0A结尾但数据内容里恰好也出现0xAA就会导致解析错乱。建议使用超时判断帧尾比如利用串口空闲中断IDLE或定时器超过3个字符时间没有新字节则认为一帧结束这样数据中就算有特征字节也不会误判。4.3 多MCU通信K210与STM32之间的电平、共地和帧格式现在很多人做AI视觉常用K210或OpenMV和STM32通信。我最初踩的第一个坑是电平匹配K210核心板标称3.3V但有的IO在供电不稳时可能输出5V左右的高电平STM32的IO在3.3V下虽然能容忍5V但不建议长期直连。稳妥做法是串接电阻或用电平转换芯片至少也要保证共地。串口接好后最常见的是看不到完整数据帧。原因在于两边波特率都配置对了但是大家用了不同的时钟源误差累积导致偶尔错一帧。解决方法是把双方的串口时钟尽量用同一个外部晶振或者选择误差较小的波特率档位如115200以下并且上位机侧加简单校验。帧协议的设计比大多数人想象的重要。如果只是把K210识别到的目标坐标发过来用一个“帧头长度数据CRC”的结构处理起来会很省心。我建议在STM32端写一个非常健壮的串口接收状态机状态机的四个状态分别是WAIT_HEAD、WAIT_LEN、WAIT_DATA、WAIT_CRC任何一个字节校验失败就回到WAIT_HEAD它能自动恢复处理粘包和断帧特别好。4.4 超声波、AD采样和按键模块模拟世界的坑不亚于数字超声波测距用HC-SR04时“程序正常但距离不准”的坑经常来自Echo引脚的高电平宽度测量方法。很多人用delay函数轮询等Echo变高结果为了处理其他任务而错过边沿距离值跳变。最可靠的做法是用定时器输入捕获测Echo高电平宽度并同时开启溢出不误判。如果板子供电不足或超声波模块附近有大电流干扰Echo波形会抖动建议读取时做多次采样取中位数而不是平均值这样抗脉冲干扰比较好。AD采样时间的坑也很典型。STM32的ADC采样是通过采样电容保持电压的如果通道内阻很大比如一个几百kΩ的分压电阻直接接在ADC引脚上采样时间太短电容还没有充满就开始转换测量值会明显偏低。解决方法是把分压电阻阻值降低到10kΩ左右或者在CubeMX里把采样周期加到最大比如480周期精确测量时还能额外配置ADC的采样保持或使用定时器触发连续采样。按键模块看似简单但硬件抖动和软件消抖的处理直接影响手感。很多人用delay(10ms)消抖结果按键响应变慢而且悬空浮空输入在强电磁环境下频繁误触发。我的做法是使用带有RC硬件滤波的按键电路比如100nF电容并联按键软件上用定时器每隔1ms扫描一次连续消抖5次后判定按键状态并用状态机支持短按、长按和双击代码不阻塞手感和稳定性都会好很多。5. 完整项目里的坑从两轮小车到智能台灯5.1 两轮差速小车轮子跑不直真的不怪PID两轮差速小车是很多人的STM32毕业设计首选但跑起来总是往一边偏。这背后的坑主要有三个编码器数据不正确、电机驱动电源干扰、PID参数不合适。编码器数据不正确的典型表现是“一边电机在动速度反馈却是0”十有八九是编码器AB相接到定时器错误的通道上或者编码器供电和MCU供电不是同一地平面。要先用printf打印两个电机的实时转速确保数值稳定且趋近于目标值再去调PID。电源干扰是很多小车的隐形杀手。电机堵转或启动瞬间电流能达到1A以上如果驱动板和MCU共用一根细长的USB线供电电源线上的压降会让STM32瞬间复位表现为小车启动时“咯噔”一下然后掉线。解决办法是给电机驱动单独供电并在MCU电源入口并联一个470uF甚至1000uF的电解电容电机电源和逻辑电源之间加共地但分开走线。PID调试上我发现最坑的是只调速度环不调转向环。两轮差速小车转向时内外侧轮需要的速度不一样如果只对称调节PID转弯必然跑偏。建议先用统一KP/ KI/ KD让直线跑稳再针对转向动作增加一个前馈或转向补偿量最终让直线和转弯都能稳定。另外PWM频率也会影响电机噪声和线性度我一般用10kHz到20kHz太低会听到明显啸叫太高驱动芯片功耗会增大。5.2 鱼缸、智能台灯与环境监测这类“小项目”的共性教训热词里出现了“stm32鱼缸”“基于stm32的智能台灯”“基于stm32空气质量检测开源项目”这几个表面不搭界实际上坑是共通的。第一个共坑是“继电器感性负载导致的复位”。鱼缸里的水泵、加热棒台灯里的调光电源都是感性或容性负载继电器断开瞬间会产生反向电动势轻则MCU复位重则烧IO。常规解决是在继电器线圈两端并联一个续流二极管直流继电器或RC吸收电路交流同时继电器驱动电路用光耦隔离。我之前做过一个鱼缸项目每天晚上水泵断电时MCU都会复位加了续流二极管后才彻底解决。第二个共坑是“传感器数据没有滤波校准”。空气质量检测常用SGP30/MQ系列SGP30需要预热和基线校准MQ系列对温湿度敏感如果你直接拿原始ADC值去显示数值跳到你怀疑人生。经验是先让传感器预热2~3分钟过滤掉前几个读数然后用低通滤波如滑动平均处理输出值如果有温湿度传感器再做简单的温度补偿。第三个共坑是“触控/轻触按键误触发”。智能台灯常见触摸按键误触发或者无响应跟按键电路布局关系很大。触摸按键的IO走线不要穿过电源区域按键电极和地之间要留足够距离并配置成内部上拉或下拉而不是浮空输入。如果是普通轻触按键则参考上一节说的RC滤波加状态机消抖。第四个坑是ADC参考电压。环境监测类项目里如果直接用VDDA作为参考电压电源电压波动就会导致测量值漂移。产品里应该用TL431或REF3325做一个2.5V基准电压接到VREF引脚软件里再根据基准值做校准。这个坑在做空气质量检测项目时特别明显我用同一块板子换了USB口供电后显示值差了15%——不是传感器漂了是参考电压漂了。5.3 毕业设计的最后一公里从能跑到能演示的细节清单最后聊聊毕业设计或者个人作品展示时的“最后一公里”坑。很多人程序逻辑没问题但一到答辩演示就翻车。最典型的几个细节第一复位按钮放在板子侧面演示时一不小心碰一下程序从头开始所有数据清零第二USB线用的是充电线没有数据功能电脑根本识别不到虚拟串口第三串口打印刷得太快上位机日志窗口卡死或者显示器上被刷屏刷到看不清。我的建议是提交演示前按这个清单走一遍检查所有接插件是否紧固电源LED和状态LED是否明显区分串口波特率是否和上位机一致程序里是否写了一个“演示模式”快捷键一键进入只演示核心功能、不刷日志的状态。很多时候这些细节比功能本身更能决定答辩效果。最后再说两句这些年做STM32开发调试我最深的体会是大部分“玄学”问题最终都能在电路连接、时钟配置、库版本、中断优先级和电源完整性上找到原因而不是真的“芯片坏了”。如果你也遇到一个百思不得其解的bug不妨先把所有外设全部断开只留最小系统、下载器和串口把环境因素排除干净再一步一步加回来。另外一个非常实用的小技巧是在代码里统一留一个DEBUG_PRINT宏所有调试信息通过它输出量产时关掉这个宏即可避免满屏的调试语句和功能代码纠缠在一起。踩坑不可怕可怕的是每次都从头查起。希望这篇总结能帮你少走一些弯路。
返回列表