STM32CubeMX 这个工具我大概是从它 4.x 版本开始用的那会儿还在用 F1 系列的老片子手工翻参考手册配寄存器配到怀疑人生。后来有了它一个工程从建到跑通能省下大半天时间尤其是引脚复用和时钟树这两块直接把最容易出错的地方图形化了。现在网上能找到的教程很多但大部分停留在点这里、点那里的层面很少有人讲清楚为什么这么配、配错了会怎样、重新生成代码时哪些东西会被冲掉。这篇就围绕 STM32CubeMX 的完整使用链路从安装、汉化、时钟树推导到呼吸灯实战把我自己在实际项目里踩过的东西系统梳理一遍。写这篇的目的一是给刚入门的朋友一条能真正跑通的路径二是给用了一阵子但总觉得云里雾里的同学补上底层逻辑。下面所有配置示例我都会标出具体参数和推导过程你照着做就能复现但强烈建议理解每一步背后的原因不然换个芯片、换个外设又得重新查。1. STM32CubeMX 究竟解决了嵌入式开发里的哪个痛点很多初学者会把它当成一个代码生成器点几下就出现成堆的初始化代码觉得很神奇。但如果只把它当生成器用你迟早会在某个深夜对着一个莫名其妙的死机发呆。真正理解它解决的是什么问题比会用它生成代码重要得多。1.1 手工配置寄存器时代的真实成本在没有这类图形化工具之前点亮一个 LED 并且让它按固定频率闪烁需要做的事情包括但不限于查数据手册确认引脚编号和电气特性、查参考手册找到 GPIO 对应的寄存器地址、把 RCC 里对应外设的时钟使能位打开、配置 GPIO 的模式寄存器输入/输出、推挽/开漏、上下拉、速度、如果要定时还得配通用定时器的预分频器和自动重装载值、再开中断、写中断服务函数。这里面的每一个环节都有一堆坑。比如时钟使能位忘记打开代码烧进去后引脚毫无反应你以为是硬件接错了实际是 RCC 没配。再比如 GPIO 模式寄存器是两位一个引脚移位算错就会影响到相邻引脚。F4、F7 这些系列还引入了 GPIO 复用功能选择寄存器同一个引脚能映射好几种外设选错了外设根本不工作。这套流程熟练之后照着手册抄一遍也就十几分钟但问题在于它极易出错而且出错了不容易定位。一个项目里外设几十个全靠手工配出一两个隐蔽错误是非常正常的事。工具的价值恰恰在这里它把芯片手册里那些分散在多章、参数互相依赖的配置项整合成了有约束校验的图形界面。1.2 它到底生成了什么别把它当黑盒你需要建立一个认知STM32CubeMX 生成的代码本质上就是把你图形界面里做的配置翻译成 HAL 库或 LL 库的初始化调用。它并没有做什么魔法最终落到芯片里运行的还是那些熟悉的寄存器操作只不过被 HAL 库的HAL_GPIO_Init()、SystemClock_Config()这类函数包了一层。以 GPIO 为例你在界面上勾选某个引脚为输出生成代码里就会出现一段HAL_GPIO_Init调用里面有个GPIO_InitStruct结构体它的成员就是模式、上下拉、速度这些你熟悉的东西。时钟配置也一样最终生成的是对 RCC 相关寄存器的写操作序列封装在SystemClock_Config()里。理解这一层之后你就不会觉得生成代码神秘了。当程序跑不起来时你完全可以打开生成的main.c和gpio.c、tim.c这些文件对照手册去看它到底配了什么值一级一级往下查。反过来如果你连它生成了什么都不看出问题时就只能反复在界面里勾来勾去效率极低。提示生成代码后先别急着写业务逻辑花十分钟通读一遍MX_xxx_Init()系列函数对照你界面上的勾选逐条核对这个习惯能帮你挡掉大量低级错误。2. 安装与汉化最容易被忽略的准备工作安装这一步看起来简单但从版本选择到环境依赖坑其实不少。我见过太多人在这一步卡半天甚至因为装了个有问题的版本导致后面一直报奇怪的错。2.1 版本选择别盲目追新也别死守老版本STM32CubeMX 大版本更新比较频繁新版本一般会增加对新芯片系列的支持、更新固件包、修复已知 bug。但新版本偶尔也会引入新的问题比如某个版本的代码生成模板改了导致老工程重新生成后编译不过。我的建议是分场景选版本新项目、新芯片用当前较新的稳定版本保证目标芯片的固件包能正常下载到。维护老项目尽量用当初建工程的那个大版本避免重新生成代码时结构变化带来额外工作量。学习练手选一个身边教程配套较多的版本遇到问题好找参考。版本号在官网下载页和软件标题栏都能看到。一个实用技巧是如果你同时要维护多个不同年代的项目可以装两个大版本通过安装目录区分开工程文件用哪个版本打开心里有数。2.2 安装包获取与路径的注意事项安装包从 ST 官方渠道获取最稳妥。下载下来通常是一个可执行安装文件双击一路下一步即可。但有两点要特别注意。第一安装路径不要带中文和空格。这是所有嵌入式工具链的通病。路径里出现中文轻则固件包解压失败重则软件启动直接报错。最保险的做法是装到类似D:\ST\STM32CubeMX这样的纯英文路径下。第二注意 Java 运行环境。较早的版本依赖本机 Java如果系统里没有或者 Java 版本不对启动时会提示找不到运行环境。较新的版本基本已经内置或者改用其他方式不太需要单独装 Java。如果你启动时报相关错误去查一下对应版本的说明即可这是环境问题不是软件坏了。安装完成后第一次启动软件可能会让你选择固件库的存放位置也会提示是否有可用更新。这里建议把固件仓库路径也设成纯英文路径而且养成定期更新固件包的习惯——不是每次都更新到最新而是至少保证你要用的那个芯片系列有对应的固件包。2.3 中文汉化能用但要有心理准备软件本身支持多语言切换中文是可以直接选的。在菜单里找到语言选项切换到简体中文后重启即可界面上的大部分术语会变成中文。但我要泼一盆冷水汉化翻译并不完整而且有些术语翻译得不够准确。比如Clock Configuration被译成时钟配置没问题但一些寄存器级别或者专业术语仍然是英文甚至有些翻译会让人产生误解。所以我个人的习惯是——界面语言保持英文。理由很实际英文界面下你看到的术语比如 Pull-up/Pull-down、Alternate Function和参考手册、数据手册里的表述一致查资料时不会因为翻译对不上而卡壳。而且大部分教程、官方文档、论坛讨论都是基于英文界面的跟着中文界面找对应的选项反而费劲。如果你英语实在吃力先用中文上手理解操作流程等熟悉了再切回英文这是个不错的过渡方案。至于那种把中文字体、外挂翻译工具塞进去的深度汉化我不建议用出问题时排查成本很高。3. 从一个空白工程到跑通完整配置链路拆解这一步是全文的核心。我会用一个具体的场景把流程走一遍重点讲每个配置项的取舍逻辑而不是单纯列步骤。3.1 芯片选型与工程命名打开软件第一步是选择创建方式。通常有两种入口一种是根据芯片型号直接选一种是根据开发板选。如果你是跟着某块具体开发板学习用第二种更方便如果是在做实际项目一般直接按型号选。选芯片时有个细节同一型号往往有多个封装和温度等级后缀引脚数量可能不一样。选错了封装后面引脚图会对不上配置出来的引脚在实物上可能根本不存在。所以务必对着你手上的芯片丝印或者开发板资料确认识别。工程命名和保存路径同样遵循纯英文原则。工程名最好能体现芯片型号和用途比如F103_BreathLed方便日后翻找。3.2 时钟树整个工程最该花时间理解的地方时钟树配置是 STM32CubeMX 里信息密度最高的界面也是最容易配错的地方。很多新手对着一堆 PLL、分频器发呆干脆直接用默认值结果要么性能跑不满要么某些外设时钟对不上导致通信出错。先把核心概念理清楚。STM32 系统的时钟来源主要有几个HSI内部高速时钟、HSE外部高速时钟通常接晶振、以及通过 PLL 倍频后的输出。系统时钟SYSCLK就是从这些来源里选一个再经过 AHB 预分频得到 HCLK进而分出 APB1、APB2 等总线时钟最终送到各个外设。配时钟时我通常按这个顺序思考要不要外接晶振。高精度、对时序敏感的场景比如串口通信、USB强烈建议用 HSE内部 HSI 的精度和温漂在有些场合不够用。如果只是点灯跑跑HSI 也能用。目标主频是多少。每个芯片都有一个最高主频限制超过就工作不稳定。比如经典的 F103 系列常见配置是 72MHz。从时钟源到目标主频中间的倍频和分频系数怎么凑。举个具体的推导假设用的是 HSE 8MHz 晶振目标 SYSCLK 72MHz。典型方案是HSE 8MHz 先经 PLL 的输入分频假设不分频保持 8MHz然后 PLL 倍频 9 倍得到 72MHz作为 SYSCLK。接着 AHB 不分频HCLK 72MHz。APB1 分频 2得到 36MHzAPB2 分频 1得到 72MHz。这里有一个几乎人人都会踩的坑APB 预分频系数不为 1 时挂在该总线上的定时器时钟会被自动倍频。也就是说 APB1 是 36MHz但 APB1 上的定时器实际时钟是 72MHz。这个规则在计算 PWM 频率、波特率时如果不注意算出来的结果会差一倍。CubeMX 界面上会把实际频率显示出来学会看那个数字别自己死记。配置界面上你只需要在对应位置填数字软件会自动帮你算并显示最终各总线频率。填错了它通常会标红警告。养成习惯配置完时钟树盯着界面右侧那个汇总区域逐个确认 SYSCLK、HCLK、PCLK1、PCLK2 的数字是否符合预期不要看个大概就往下走。3.3 引脚与外设的图形化配置时钟配好接下来是引脚和外设。这一块界面直观很多左边是芯片引脚图右边是外设列表。配置顺序建议是先外设、后引脚。因为很多外设一旦启用它占用的引脚就固定了软件会自动帮你把引脚标绿并锁定。你如果先手动配了某个引脚做普通 GPIO后面又启用一个要占用它的外设就会冲突。以串口为例启用 USART1 后界面上 TX、RX 对应的引脚会自动分配好同时可以选择通讯参数波特率、数据位、停止位、校验位。这时如果你的开发板上这两个引脚正好接了 USB 转串口芯片那就直接能用如果引脚被别人占了你可以在引脚图上拖拽调整到备选引脚。GPIO 的配置项里有几个概念值得展开说推挽还是开漏。推挽输出能主动输出高和低驱动能力强开漏输出的高电平需要靠外部上拉常见于 I2C 总线这种需要线与的场景。普通点灯、驱动数字信号用推挽即可。上下拉。输入模式下如果不确定外部是否接了明确电平要配上拉或下拉避免引脚悬空导致读数乱跳。输出速度。这个不是指代码执行速度而是引脚电平翻转的边沿速率。频率不高的时候用低速能减少电磁干扰只有高速信号才需要调高。界面里每个引脚右键都能看到可选项配置不合法时软件会用不同颜色标记。红色的引脚通常是冲突或者配置有误一定要处理干净再生成代码带着红字生成出来的工程多半有问题。3.4 代码生成选项决定后续维护体验的关键前面都配完最后一步是代码生成设置这一步做好能省掉后面无数次重复劳动。有几个选项必须重点关注生成哪些文件。通常勾选生成外设初始化代码对即每个外设一个 .c/.h 文件这样代码按外设拆分比全部堆在main.c里清晰太多。我强烈建议开启一个工程外设一多全塞进 main 里会难受到想重写。是否复制必要的库文件。如果勾选生成的工程里会包含 HAL 库源码工程相对独立换电脑也能直接编译不勾选则引用外部库路径。建议勾选尤其是需要把工程发给别人或者换电脑的场景。工具链选择。CubeMX 能生成给 Keil、IAR、STM32CubeIDE、Makefile 等多种工具链的工程。按你实际用的编译器选选错了打不开。设置完成后点生成软件会输出成堆文件。看到生成成功的提示先别激动第一次生成的工程一定要先编译一遍确认能过再开始写业务代码。这一步能把工具链配置、库路径这类环境问题和你的代码问题隔离开。4. 呼吸灯实战TIM PWM 配置与代码实现到这一步工程框架有了我拿呼吸灯这个经典案例把从配置到出效果的全过程走一遍。之所以选它是因为它同时涉及 GPIO、定时器、PWM 三块内容练手价值高。4.1 呼吸灯的原理与 PWM 参数计算呼吸灯的本质是用 PWM脉冲宽度调制让 LED 的亮度周期性渐变。PWM 是一个方波通过改变方波的高电平占空比等效于改变送到 LED 的平均功率人眼看到的就是不同亮度。一个 PWM 波形由两个关键参数决定频率由预分频器 PSC 和自动重装载值 ARR 共同决定。公式是PWM频率 定时器时钟 / ((PSC1) * (ARR1))。占空比由比较寄存器 CCR 决定占空比 CCR / (ARR1)。拿前面配好的 72MHz 定时器时钟举例。我想要一个大约 1kHz 的 PWM 频率这个频率足够高人眼看不出闪烁。设 PSC 71ARR 999则频率 72,000,000 / (72 * 1000) 1000 Hz正好 1kHz。占空比的分辨率是ARR1 1000级也就是 CCR 从 0 到 999对应占空比 0% 到约 99.9%。这个分辨率做呼吸灯绰绰有余亮度过渡会很细腻。如果你想更细腻可以把 ARR 调大想调频率就动 PSC。记住一个取舍ARR 越大分辨率越高但同样频率下 PSC 的约束也越紧实际调试时两个参数配合着微调。呼吸效果怎么做出来就是让 CCR 的值在一段时间内从 0 慢慢增加到 999渐亮再从 999 慢慢降回 0渐灭循环往复。这个慢慢的速度决定了呼吸的快慢通常几百毫秒到几秒之间。4.2 CubeMX 里 TIM 的配置细节在 CubeMX 里找到你要用的定时器比如 TIM3 的某个通道。配置要点如下时钟源选内部时钟也就是挂在内部总线上的定时器时钟。通道模式选 PWM Generation这样这个通道就工作在 PWM 输出模式。PSC 填 71ARR 填 999对应上面的计算。Pulse也就是初始 CCR 值填 0 或者中间值决定上电时的初始亮度。PWM 模式选 Mode 1含义是计数器小于 CCR 时输出有效电平大于时输出相反电平。这个模式最常用配的时候留意一下界面上的说明。极性根据你的 LED 接法选择。如果 LED 是低电平点亮常见于开发板LED 一端接 3.3V另一端接引脚那有效极性要反着理解否则占空比增大的时候灯反而变暗。这个细节坑过很多人。配置完对应的引脚会自动分配好并锁定。回到引脚图确认一下这个引脚是不是你板子上 LED 接的那个不是的话改一改。生成代码前再确认一遍时钟树因为定时器时钟算错PWM 频率就全错了。前面说过APB1 上的定时器实际时钟可能和总线时钟不一样界面上会显示定时器实际时钟以那个为准。4.3 启动 PWM 与编写呼吸逻辑生成代码后在main.c里做两件事启动 PWM 输出通道然后写循环改变 CCR 值。启动 PWM 用的是 HAL 库函数比如针对 TIM3 通道 1 大致是这样调用HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_1);这句放在初始化之后、主循环之前。它一开始即启动但注意此时 CCR 是初始值所以上电亮度是那个初始亮度。然后在主循环里写呼吸逻辑核心是改 CCR 的值用这个函数__HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, duty);其中duty就是当前占空比对应的整数值。一个简单的写法是两层循环// 渐亮 for (int i 0; i 1000; i) { __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, i); HAL_Delay(1); } // 渐灭 for (int i 1000; i 0; i--) { __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, i); HAL_Delay(1); }这样每级亮度停留 1ms1000 级大约 1 秒完成一次渐亮整体呼吸周期约 2 秒看起来很自然。你可以通过改HAL_Delay的参数来调快慢。注意用HAL_Delay做呼吸会让主循环完全阻塞在这两个循环里这段时间没法干别的事。如果呼吸只是众多任务之一就得改用定时器中断或者状态机来更新 CCR把阻塞式的延时去掉。这是从能跑到能在实际项目里用的分水岭。4.4 让呼吸更平滑的小技巧直接线性改变 CCR 做出来的呼吸灯如果仔细看会发现亮度的变化不是特别自然。原因是人眼对亮度的感知不是线性的低亮度区域对数值变化很敏感高亮度区域则不明显。线性递增会让暗的时候变化快、亮的时候变化慢的感觉不成比例。想让它更顺眼可以对 CCR 的取值做一个简单的曲线映射比如用平方或者查表的方式让低段变化慢一点、高段变化快一点。这个不是必须的属于打磨细节。如果你做出来的效果自己看着舒服就不用折腾。另外HAL_Delay在系统时钟没配好、或者中断优先级配置异常时有可能不准如果发现呼吸节奏忽快忽慢优先回去检查时钟配置和 SysTick 有没有被别的高优先级任务占用。5. 读懂生成代码的结构才能安全地改很多人用 CubeMX 的习惯是生成—写代码—出问题—重新生成—发现代码没了。会掉进这个循环根本原因是没有理解生成代码的结构和用户代码保护机制。5.1 工程目录结构解析生成后的工程大致有这么几类文件main.c程序入口。包含了 HAL 初始化、系统时钟配置调用、各外设初始化调用以及主循环。外设对应的xxx.c/xxx.h比如gpio.c、tim.c、usart.c每个里面是MX_xxx_Init()函数。这些是 CubeMX 每次重新生成时会覆盖重写的文件。stm32xxxx_hal_msp.c外设的底层初始化负责引脚、时钟使能这些板级配置。也是会被覆盖的。stm32xxxx_it.c中断服务函数。你写的中断处理逻辑通常也放这里但要注意保护。HAL 库源码目录库文件本体一般不需要动。最关键的一点除了用户代码保护区间里的内容其他部分在重新生成代码时都会被覆盖。也就是说你如果在MX_GPIO_Init()函数体里直接加代码下次改配置重新生成这段代码就没了。5.2 USER CODE 保护机制的正确用法CubeMX 在会覆盖的文件里预留了很多对注释标志形如/* USER CODE BEGIN xxx */ /* USER CODE END xxx */只有夹在这对标志之间的代码重新生成时才会被保留。这是整个工具最重要的使用规则没有之一。所以你应该养成习惯需要初始化后执行的代码写到USER CODE BEGIN 2和END 2之间或者对应的初始化函数尾部的保护区间。中断处理里的自定义逻辑写到USER CODE BEGIN相关的保护段里。需要额外声明的全局变量、函数原型放到USER CODE BEGIN PV、USER CODE BEGIN PFP这类对应的位置。反过来任何不属于你的东西——包括软件自动生成的初始化代码——都不要去手动修改因为改了也白改。5.3 什么时候该重新生成什么时候该手动改这涉及到工作方式的选择。当你的需求发生变化比如要新增一个外设、改一个引脚、调整时钟理论上都可以回 CubeMX 改配置再重新生成。但因为重新生成会覆盖非保护区的代码所以有两种策略第一能回 CubeMX 改的就回去改尽量让配置的源头保持统一。这样工程结构清晰别人接手也能一目了然。前提是你严格遵守了保护规则。第二涉及复杂业务逻辑的部分不要指望 CubeMX。它只负责初始化业务逻辑是你自己的事这部分代码要么放在保护区内要么放在自己新建的、不被覆盖的文件里比如新建app_xxx.c并在工程配置里加入编译。我见过把大段业务逻辑硬塞进USER CODE区间结果乱成一团的也见过完全不敢重新生成、改个引脚全靠手动导致工程和配置对不上的。这两种都过犹不及。合理的度是初始化相关回工具改业务逻辑独立成文件。6. 那些教程里不常写、但一定会遇到的坑前面讲的是正向流程这一节专门说问题。这些问题我基本都在真实项目里遇到过写出来希望能帮你少熬几个夜。6.1 改了时钟配置程序就死机或跑飞时钟配置最常见的两个错误一是超出芯片最高主频二是外设时钟没按预期工作。第一种情况你填了个超过最高频率的 SYSCLK软件一般会提示但有时只是警告你没注意就生成了。结果芯片在高频下不稳定表现为随机死机、某个外设偶尔失灵。解决办法是严格遵守芯片手册标称的最高主频想要性能就在这个上限内优化别硬超。第二种情况更隐蔽。比如你把某个外设挂在 APB1 上却在别处用了错误的时钟频率去算波特率或定时周期。程序能跑但通信数据错乱、定时不准。这就要回到前面说的理解 APB 预分频和定时器时钟之间的关系。排查这类问题的最快方法是用示波器或逻辑分析仪实地测一下输出的波形频率和你的计算值一对比就知道差在哪。还有一种情况是外接晶振配置了 HSE但板子上晶振没焊或者坏了程序会卡在等待 HSE 稳定的死循环里表现为一上电就停在启动阶段不动。这时候要么检查硬件要么把时钟源切回内部 HSI 应急。6.2 重新生成代码后用户的修改丢失这个前面讲了机制这里说具体怎么防。一个高频场景你在main.c主循环里写好了呼吸灯逻辑然后发现要再加个按键功能回 CubeMX 配了 GPIO 输入重新生成回来一看呼吸灯代码没了——因为它写在了USER CODE区间外或者你记错了标志段落。防范方法很简单但也有点啰嗦每次在用户区以外想动笔之前先停下来想想这段代码会不会被覆盖。如果会就把它挪进保护区。另外工具里有个选项可以控制是否覆盖某些文件但我不建议依赖它还是靠自己的纪律性最保险。还有一个土办法但很有效重要工程定期用版本管理工具存一下哪怕只是简单地复制一份加日期后缀。重新生成代码前先提交一次出问题了随时能对比回来。这个习惯救过我不少次。6.3 固件包缺失导致无法生成或芯片不识别有时候你选了某个芯片软件提示找不到对应的固件包配置界面灰着不能用。这是因为 CubeMX 需要额外下载针对具体芯片系列的固件包Firmware Package。解决办法是在固件管理界面里找到对应系列选择合适版本进行安装。这里有两个现实问题一是固件包体积不小下载需要稳定的网络环境网络不通畅时会很慢甚至失败二是如果你处在一个离线环境可以提前把需要的固件包文件准备好通过从本地导入的方式加载具体操作在固件管理界面里有对应入口。我建议的做法是把你常用芯片系列的固件包一次性下载好并保留新版本不急着更新除非它修了你正在碰到的问题。固件包和软件版本要大致匹配太新太旧都可能不兼容。另外提醒一句同一系列固件包不同版本之间HAL 库的 API 偶尔会有细微变化。如果你照着一份教程写代码结果编译报错说某个函数不存在很可能就是固件包版本不一致导致的去核对一下版本号。6.4 工程换了编译器或者电脑后打不开这个坑多在团队协作或者换设备时出现。现象是把工程拷到另一台电脑用另一个编译器一打开报一堆找不到文件或者无法识别的错误。原因通常有几个生成工程时选的是 Keil结果在 IAR 里打开库文件路径是绝对路径拷过去就失效生成时没选择复制库文件导致依赖的原库找不到。对策在前面生成选项里提过生成时选择把库文件一起复制到工程目录尽量用相对路径。这样工程自包含迁移性最好。如果已经生成完了发现有问题回 CubeMX 把选项改好重新生成比手动改路径靠谱得多。顺带说下跨编译器切换最干净的方式是回 CubeMX 重新选择工具链再生成一份而不是试图把 Keil 工程硬改成 IAR 能用的那样改动量不比重新生成小还容易遗漏。7. 使用节奏上的一些个人体会工具用久了会形成一些节奏上的习惯这些不是什么硬知识但确实能提高效率也一并说说。新手阶段我建议每次配完就立刻生成并编译跑一遍哪怕只配了一个 GPIO。快速验证能帮你建立配置—反馈的直觉出问题时也容易定位因为改动小。等熟练之后可以一次配好多个外设再统一验证但前提是你对每个外设的配置逻辑都心里有数。我还建议给常用的配置建一个模板工程。比如你的主力开发板晶振频率、时钟树、常用外设都配好留着当一个起点。下次开新项目复制一份改改就行省掉重复配置的时间也避免了每次都要重新推导时钟这些容易出错的部分。最后一点别把 CubeMX 当拐杖。它解决了初始化代码又长又容易错的问题但芯片手册、参考手册这些原始资料永远是最权威的。当你发现工具的行为和你的预期不符时回到手册去查那个外设的寄存器定义往往能立刻找到答案。工具是帮你干活的不是替你理解芯片的。真正把芯片吃透的人用不用这个工具都能把工程跑起来区别只是快慢而只会点界面的人一旦遇到工具覆盖不到的边角问题就会束手无策。