STM32CubeMX从安装到实战这篇把新手绕不开的坑全说透了玩STM32的人应该都听说过STM32CubeMX就算还没用过大概率也在某个教程里瞥见过那个蓝白色调的图形界面。这玩意儿是ST官方出的图形化配置工具简单说就是帮你把芯片的初始化代码自动生成出来省去一页一页翻数据手册、一行一行调试寄存器配置的功夫。以前调一个串口可能要对着参考手册核对波特率寄存器半天现在在图形界面里点几个下拉框代码就出来了还能保证配置不会写错。这篇东西适合刚接触STM32CubeMX、正准备装环境开始搞开发的人也适合已经装了但只会跟着教程点点点、不清楚每一步背后逻辑的兄弟。我尽量按自己实际折腾的经历来写从下载安装、环境配置到上手建工程、配GPIO做呼吸灯再到串口和调试器那些日常必踩的坑一条龙讲清楚。看完不能说让你变成高手但至少能让你少走我当年走过的一大段弯路。1. 项目整体思路与工具选型1.1 STM32CubeMX到底解决什么问题先说个背景。STM32系列芯片型号多、外设多、寄存器多尤其是做项目起步那会儿光是初始化时钟树、配置GPIO模式、设置中断优先级就能耗掉大半天。而且这种代码高度重复、改起来容易出错同一个工程换个引脚或者改个时钟频率就要回头翻代码改半天稍微粗心一点硬件就工作不正常。STM32CubeMX的思路是把“图形化配置”和“代码生成”结合起来。你在界面上选定芯片型号配置引脚功能、时钟树、外设参数点击生成它就把对应的初始化代码按HAL库或者LL库的结构给你生成好。生成出来的工程是完整的可以直接在Keil、IAR或者STM32CubeIDE里打开编译烧录。实际上我们真正要写的业务代码是在它生成的框架基础上往里填逻辑而不是从零搭底子。所以我一直觉得STM32CubeMX不是一个“提升代码能力”的工具而是一个“提升效率、降低出错率”的工具。它把那些机械性的、重复性的初始化工作接管了把时间留给真正有难度的业务逻辑和应用层设计。对于学生做课设、工程师搞原型验证、甚至量产项目的初期版本都非常实用。1.2 为什么建议直接用STM32CubeMX而不是纯手写寄存器有些老工程师习惯直接操作寄存器张口就是GPIOA-ODR | (1 5)这种写法。这种功底确实扎实但对新手并不友好而且在实际项目中维护成本很高。每种芯片寄存器地址不一样每换一个型号就要重新查手册代码可读性也差。STM32CubeMX底层用的是HAL库也就是ST官方封装好的硬件抽象层。它把所有寄存器操作封装成结构清晰、命名统一的函数比如HAL_GPIO_WritePin、HAL_UART_Transmit代码写起来很像读英语句子新手也能看懂。虽然HAL库相比寄存器操作会多一些函数调用层次的消耗但对于绝大多数应用场景这点性能损失完全感知不到换来的开发效率提升却是实打实的。对我来说还有一个核心原因项目迭代时引脚更灵活。比如产品前期调试时LED灯接在PA5后期画板子改成了PB1如果是纯手写代码你得全局搜索所有涉及PA5的地方逐个改。但在STM32CubeMX里只需要在图形界面把引脚重新映射一下重新生成代码受影响的部分自动更新。这个优势在多引脚、多外设的项目里特别明显。1.3 工具链整体构成不只是STM32CubeMX一个软件很多人以为装了STM32CubeMX就能直接烧录跑程序了其实这是个误区。STM32CubeMX的角色是“配置生成器”它生成的工程还需要配合编译器才能变成可以烧录的机器码。完整的一套开发环境通常包含下面几个部分。STM32CubeMX图形化配置 初始化代码生成。编译器/IDE最常用的是Keil MDK其次是IAR EWARM现在ST自家免费的STM32CubeIDE也用得越来越多。交叉编译工具链Keil里内置了ARMCC或者AC6不需要单独装。调试器驱动常见的ST-Link、J-Link、DAP-Link装好驱动才能在IDE里下载和在线调试。固件支持包STM32CubeMX首次使用某个系列芯片时需要下载对应的HAL库固件包比如STM32CubeF1、STM32CubeF4这个后续会细说。如果你用的是STM32CubeIDE那相当于编译器调试环境一整套都打包好了不用再单独装Keil。但考虑到很多学校、培训机构和现有项目仍然在用Keil我更推荐有精力的兄弟把Keil和STM32CubeIDE都折腾一遍反正流程上大同小异多会一个没坏处。2. 核心细节解析从下载安装到环境配置2.1 下载与安装先避开Java这个大坑STM32CubeMX本身是个Java应用所以电脑上需要先有Java运行环境JRE才能跑起来。很多新手装完STM32CubeMX双击没反应十有八九就是Java环境没配好。Java的安装分两步。第一步是下载JDK或JRE比较稳妥的做法是装OpenJDK 17或者Oracle JDK 17太老的版本或者太新的版本都可能跟STM32CubeMX版本有兼容性问题。第二步是配置环境变量在“系统属性-环境变量”里的系统变量中新建一个JAVA_HOME指向你的JDK安装目录然后在Path变量里加上%JAVA_HOME%\bin。配置好了之后打开命令行输入java -version能正常输出版本号就说明成了。STM32CubeMX本身的安装就简单了从ST官网下载对应系统版本的安装包右键以管理员身份运行一路Next安装就行。安装路径建议不要带中文和空格比如放到D:\STM32CubeMX否则后面某些版本的工程路径解析会出现诡异的编码问题。装完打开界面默认是英文的这个不影响使用习惯就好。2.2 中文汉化网上传的方法到底靠不靠谱搜索热词里有一条是“STM32CubeMX中文汉化”说明确实有不少人看英文界面不习惯。这里我得先说句实话STM32CubeMX官方并没有正式的中文语言包网上流传的所谓汉化补丁大多数是第三方做的资源文件替换把界面上的菜单和选项文本换成了中文。我自己试过两次结论是不推荐在重要项目上折腾汉化。原因有两个第一是网上流传的汉化包版本跟当前STM32CubeMX版本经常对不上轻则部分界面显示乱码重则软件启动报错第二是教程、论坛、技术问答里大家用的都是英文界面的术语你汉化之后看到的中文菜单反而不容易跟别人的截图对应上。如果是英文水平实在捉急我的建议是遇到看不懂的选项就查在线翻译配合上下文基本能猜个八九不离十。STM32CubeMX的配置项用词都比较规范翻来覆去就那么几十个高频词用几天就熟了。当你看得懂GPIO、Alternate Function、Prescaler这些词之后汉化就没必要了。2.3 固件包管理初次使用最关键的一步安装好STM32CubeMX之后很多人迫不及待地新建工程结果卡在下载固件包那一步干瞪眼。因为STM32CubeMX第一次使用某个芯片系列的时候需要从ST的服务器下载对应的HAL库固件包。比如你选了一颗STM32F103C8T6它属于F1系列就要下载STM32Cube_FW_F1这个包。固件包的下载方式有两种。一种是新建工程选择芯片型号时软件弹出提示并自动开始下载还有一种是在Help - Manage embedded software packages里面手动勾选需要安装的版本。需要注意固件包是按大版本分的同一个系列下面又有多个小版本号一般选择最新的稳定版就行。同一个系列可以同时装多个版本因为不同CubeMX版本生成的工程可能依赖不同版本固件包。这里有个非常著名的坑国内下载ST服务器上的固件包速度可以慢到让人怀疑人生甚至最终直接下载失败。我见过最夸张的一次一个100多MB的F4固件包挂了一下午都没下载完。这种问题处理思路是这样先检查网络环境切换一下网络运营商如果还是慢可以尝试通过代理方式访问注意这里指的是常规意义上的HTTP代理用于办公或开发环境不是那种特殊工具大家自行把握合规性。还有一个更省心的思路是让有环境的朋友把解压好的固件包直接拷贝给你放到本地的仓库目录里。STM32CubeMX的固件包存储路径默认在用户目录下的STM32Cube\Repository把拷贝的文件夹放进去刷新一下就能识别到了。2.4 关于安装包版本的选择建议STM32CubeMX的版本更新比较频繁新版本通常会增加对新芯片型号的支持、修复已知Bug、优化界面交互。要不要追新版本我的建议是分情况。如果只是学习、做实验、跑跑Demo直接用官网最新版本就好体验新特性也能兼容新出的芯片。如果是在做产品开发、项目已经稳定跑起来那别急着升级保持当前验证过的版本更稳妥。因为新版本固件包更新之后HAL库某些函数的参数或行为可能微调生成的工程代码跟旧版本可能有差异项目里再翻出老工程对比排查费时费力。版本升级还有个坑是工程兼容性问题。用高版本CubeMX生成的.ioc工程文件低版本软件打不开反过来低版本生成的高版本打开虽然一般都能打开但可能会提示固件包版本不匹配需要你选择用哪个版本重新生成代码。所以在团队协作中最好统一STM32CubeMX版本和固件包版本这个约定和统一编译器版本一样重要。3. 实操过程5分钟快速创建你的第一个工程3.1 新建工程的两种路径与芯片选择安装配置都搞定后我们正式走一遍新建工程的流程。打开STM32CubeMX首页会看到几个选项ACCESS TO MCU SELECTOR、ACCESS TO BOARD SELECTOR、New Project之类的入口。这里解释一下区别。MCU Selector按芯片型号选适合自己设计板子的场景。Board Selector按ST官方开发板选适合你手里有Nucleo、Discovery之类的板子可以直接搜板卡型号。如果是自己画的板子或者常见的STM32F103C8T6蓝色Pill板这类非官方板就选MCU Selector然后在搜索框里输入芯片型号关键字。比如输入“STM32F103C8”下面就会列出对应型号双击就能进入配置界面。型号选择大家可以多留意一下后缀比如封装、Flash大小、RAM大小都会影响引脚数量和资源选错了后续布线会很难受。3.2 时钟树配置理解RCC和HSE/HIS的关系进入配置界面后最先做的一件事通常是配置时钟树。默认情况下芯片跑的是内部高速时钟HSI频率一般不高而且内部RC振荡器的精度比不上外部晶振。如果需要用到串口波特率精度、USB外设或者跑更高主频就得切换到外部高速时钟HSE或者外部晶振再通过锁相环PLL倍频到目标主频。以STM32F103系列为例典型的配置流程是在System Core - RCC里面把High Speed Clock (HSE)设为Crystal/Ceramic Resonator然后在Clock Configuration页签里把PLL Source选为HSE输入外部晶振频率一般板子上是8MHzPLLM、PLLN、PLLP这些系数按目标频率计算。最后在HCLKAHB总线时钟里填入期望主频比如72MHz软件会自动计算分频系数。如果配置的时钟树不合法STM32CubeMX会用红色标出来非常直观。新手在时钟树这页最容易犯的错误是想当然地认为“数值填得越高越好”。实际上每个系列的主频上限是不同的F1是72MHzF4是168MHz或者180MHz超出极限值就是芯片跑飞或者稳定性差。而且外设总线时钟APB1、APB2也有上限比如APB1一般不超过36MHzAPB2不超过72MHz。配置的时候盯着右侧的红色警告就能稳住。3.3 GPIO配置输入输出、上下拉、速度等级的考量时钟树搞定之后最常做的事情就是配置GPIO。在芯片引脚视图上直接点击任何一个引脚会弹出可配置的功能列表。比如点PA5下拉菜单里有GPIO_Input、GPIO_Output、各种外设复用功能比如TIM2_CH1、USART1_CK之类。如果你只是要让一个引脚输出高低电平控制LED就选GPIO_Output。然后在下方的System Core - GPIO里可以进一步配置该引脚的初始输出电平、GPIO模式推挽输出还是开漏输出、输出速度、上下拉电阻等参数。这些参数里GPIO output level决定上电时的初始电平如果你控制的是LED通常低电平点亮这里就设为Low。GPIO mode一般选Output Push Pull推挽输出带负载能力强“开漏输出”在不接外部上拉时无法输出高电平新手经常在这里踩坑。Maximum output speed选Low或者Medium就够了LED灯不必开最高速度开高了反而可能引入噪声和功耗问题。3.4 工程生成IDE选择与代码结构说明配置得差不多了点击右上角的GENERATE CODE按钮。这时会弹出一个窗口让你填工程名称、工程路径、工具链/IDE类型。IDE类型这里如果用的是Keil就选MDK-ARM如果用STM32CubeIDE就选STM32CubeIDE。另外还有两个常见选项Toolchain/IDE下方有个Copy only the necessary library files的选项勾上之后不会把整个HAL库全部拷过来只保留当前工程需要的文件工程体积小很多建议勾选。生成之后打开Keil工程左侧项目树里会看到几个主要目录Application/User/Core下是主函数和中断回调等用户文件Drivers/STM32xx_HAL_Driver下是HAL库源码Drivers/CMSIS下是ARM内核相关的头文件和启动文件。我们真正要写的代码主要在main.c里的main函数和while(1)循环里以及少量回调函数中。这里有个经验之谈不要改动STM32CubeMX生成的初始化代码区域因为下次再点生成修改会被覆盖。用户自己的代码要写在规定的用户代码区里比如/* USER CODE BEGIN 3 */和/* USER CODE END 3 */之间这样重新生成代码时内容不会被冲掉。4. 呼吸灯实战把定时器PWM用起来4.1 为什么用定时器PWM而不是延时翻转引脚搜索热词里频繁出现“STM32CubeMX 呼吸灯”说明这是很多人上手后的第一个自定义小项目。呼吸灯的原理并不复杂LED的亮度跟通过它的平均电流有关通过让LED以很高的频率快速亮灭并改变“亮”和“灭”的时间比例也就是占空比人眼看到的就是不同亮度。占空比从0逐渐增加到100%再从100%逐渐减小到0LED就呈现出“呼吸”的效果。实现这个效果有两种常见思路。第一种是在主循环里用延时函数翻转引脚并动态调整延时时长。这种方法实现简单但延时期间CPU被完全占用而且不同主频下延时时间需要重新调整效果也不够细腻。第二种是使用定时器的PWM输出功能由硬件自动产生固定频率、可调占空比的方波信号CPU只需要定期更新占空比寄存器就行。强烈推荐第二种这也是实际项目里驱动LED调光、电机调速、蜂鸣器控制的标准做法。4.2 在STM32CubeMX里配置定时器PWM搞明白原理后操作就很清晰了。还是先打开STM32CubeMX在Timers - TIM2或者TIM3、TIM4都可以看你想用哪个里把Channel1选为PWM Generation CH1。然后在下方配置定时器的参数Prescaler预分频系数、Counter Period计数周期、Pulse脉冲宽度就是初始占空比Auto-Reload Preload使能自动重载预装载。这些参数怎么算举个例子假设定时器时钟为72MHz想让PWM频率为1kHz。PWM频率的计算公式是定时器时钟 / (Prescaler 1) / (Counter Period 1)。先设Prescaler为71这样定时器计数时钟变成72MHz / 72 1MHz再设Counter Period为999那么频率就是1MHz / 1000 1kHz占空比的分辨率就是1/1000足够呼吸灯平滑变化了。Pulse的初值可以设成0也就是上电时候占空比0灯全灭。配置完成后引脚视图里对应引脚旁边会出现一个定时器的复用标签比如TIM2_CH1会显示在某个引脚上默认是PA0。如果默认引脚跟你的板子不一致直接点击引脚手动切换到对应引脚就行。4.3 在代码里实现占空比渐变生成代码后在main.c里需要做的事情有两步。第一步是在主循环之前启动PWM通道调用HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_1)否则引脚上是不会有波形输出的。第二步是在while(1)循环里写占空比渐变逻辑。这里要注意HAL库提供的占空比修改函数是__HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, value)value的取值范围是0到Counter Period也就是0到999。渐变逻辑可以用一个方向标志位实现比如pwm_val从0开始每次加5加到1000就改变方向每次减5减到0再改变方向然后用HAL_Delay控制每步的时间间隔。我实际测试下来每隔3到5毫秒调整一次占空比呼吸效果比较自然太快了能看出明显的阶梯跳变太慢了灯光变化又显得拖沓。你也可以把加/减步长和延时时长做成变量慢慢调到满意的节奏为止。这个过程做完你对定时器PWM的理解会比看书深刻得多。5. 让工程进入“串口时代”配置UART与调试利器5.1 UART配置界面里的参数到底该怎么选串口是嵌入式开发里最常用的调试手段基本没有之一。在STM32CubeMX里配置串口相对简单在Connectivity - USART1或者其他串口里把Mode设为Asynchronous异步模式然后在下方Parameter Settings里配波特率、字长、停止位、校验位。常用的参数组合是115200-8-N-1也就是波特率115200、8位数据、无校验、1位停止位。波特率的上限跟芯片的时钟配置有关如果外部晶振频率不准或者PLL配置有问题串口传数据就会出现乱码。所以如果你发现串口输出乱码第一个要检查的不是串口配置而是时钟树是否正确。硬件上还要注意交叉连接也就是STM32的TX接USB转串口模块的RXRX接TX共地必须接不然数据传输不稳定。生成代码后发送一个字符串需要用到HAL_UART_Transmit函数。这个函数是阻塞式的发送期间CPU会一直等待发送完成对于调试场景完全够用。接收数据则有轮询、中断、DMA三种模式入门先用中断方式就好在初始化之后调用HAL_UART_Receive_IT当数据到达时会进入HAL_UART_RxCpltCallback回调函数在回调里处理数据即可。这里有个新手很常见的疑惑为什么我调用了接收中断函数程序却一直没进回调答案是HAL_UART_Receive_IT是一次性的接收完一帧后需要重新调用才能继续接收很多人第一次用都会漏掉这一步。5.2 调试器配置ST-Link还是J-Link代码下载失败怎么办工程建好、代码写完最后一步是烧录和调试。如果你用的是Nucleo板卡或者带ST-Link的板子直接在Keil的Options for Target - Debug里选择ST-Link Debugger然后点击Settings确认能识别到设备。J-Link用户则选择J-LINK/J-TRACE Cortex。Flash Download那一栏要勾选Reset and Run这样烧录完程序会自动复位运行不然每次烧录完都要手动按一下复位键开始不知道还以为是程序没烧进去。下载失败是新手阶段最容易遇到的问题。常见的报错有No ST-LINK detected、Cannot access target、Flash Download failed - Cortex-M3等。遇到这类问题首先检查USB线是不是数据线很多USB线只能充电不能传数据插上去电脑毫无反应其次看设备管理器里有没有识别到调试器没识别就重装驱动再就是检查调试器的SWDIO、SWCLK、GND是否连接正确松了或者反了都会导致找不到目标芯片。另外目标板供电不足也会导致下载失败这时候需要单独给板子供电不用想当然地认为USB调试器能提供足够电流给整个板子。5.3 中断优先级配置默认配置在什么场景下会出问题STM32CubeMX里还有个经常被忽略的设置是NVIC也就是中断控制器。在配置串口接收中断的时候你会看到生成代码里自动调用HAL_NVIC_SetPriority和HAL_NVIC_EnableIRQ。NVIC的优先级分组方式也需要在初始化里设置比如HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4)。优先级这个东西入门阶段可能很少出问题但项目复杂了就容易翻车。比如你同时用了串口中断和定时器中断默认情况下两者优先级可能相同如果两个中断同时触发处理顺序就可能不符合预期。更隐蔽的问题是如果在中断回调里做了耗时较长的操作比如发送一长串日志可能会阻塞其他中断的执行导致一些需要实时响应的功能出现延迟。所以经验之谈是中断回调函数里不要做复杂的事情收到数据先把数据拷到缓冲区置个标志位真正的处理放在主循环里做。这个习惯越早养成越好后面上RTOS的时候会深有体会。6. 常见问题与排查技巧实录6.1 固件包下载慢、下载失败怎么破前面提过固件包下载这个老大难问题这里再展开说。解决思路不外乎几个一是错峰下载晚上或者凌晨网速通常好一些这个土办法看起来没技术含量但实测有效二是检查公司网络或校园网的防火墙策略有些网络环境会拦截大文件传输换个手机热点说不定就好了三是从朋友、同事那里拷贝固件包放到本地的Repository目录里注意版本号要跟CubeMX要求的完全一致否则软件不认。这里还有一个细节如果之前下载了一部分但中断了重试之前最好把Repository目录里残留的临时文件清掉不然可能提示文件损坏反反复复试不出来。6.2 重新生成代码后自己的代码不见了这是STM32CubeMX用户最崩溃的瞬间之一辛辛苦苦在主函数里写了一堆逻辑点了一下GENERATE CODE再打开工程发现代码全没了。原因几乎可以肯定是你的代码没写在用户代码区。HAL库生成的main.c里到处都有/* USER CODE BEGIN */和/* USER CODE END */注释标记这些是CubeMX在重新生成时唯一保留的区域写在别处的代码会被无差别覆盖。很多新手在初始化那几行之后直接写自己的代码看起来跟周围的代码没区别但CubeMX不这么认为它只认那对注释标记。所以规范做法是自己的业务逻辑全部放在USER CODE区域内。另外工程里新增的.c/.h文件是会被保留的因为生成逻辑只更新它自己生成的模板文件所以在工程里新增用户模块文件是一个比塞进main.c更清爽、更安全的方式。6.3 引脚冲突和复用冲突怎么判断ST芯片的引脚复用功能非常灵活一个引脚可能有五六个可选功能比如PA9既可以是USART1_TX也可以做TIM1_CH2。配置的时候如果某个外设需要的引脚被另一个外设占用了STM32CubeMX图上会显示冲突而且通常是红色或者弹窗警告。新手遇到这种情况容易慌其实处理逻辑很简单先把当前功能引脚记下来然后点一下冲突的引脚看它有哪些其他可选功能把功能调整到其他空闲引脚上就行。比较麻烦的是部分外设是固定引脚的比如USB的D/D-、晶振的OSC_IN/OSC_OUT没法随意改。这种情况只能先满足固定引脚再安排其他外设。项目选型阶段如果能想清楚哪些外设必须用哪些引脚不可替代后面在CubeMX里配置就能省很多麻烦。这个经验在做PCB布局的时候尤其重要往往在软件里移一下引脚硬件布线会简单很多。6.4 编译报错和HAL库函数找不到的排查思路代码写好了一编译报一堆undefined symbol或者找不到头文件的错误这也是高频问题。最常见的两个原因是第一在CubeMX里启用外设时选了对应功能但生成工程后没把对应驱动文件加入编译路径这类问题通常是因为IDE平台选择不对或者库文件裁剪时把用到的东西裁掉了第二自己写的代码里包含了HAL库的某个头文件但是路径没配置对。排查思路很简单先看错误信息定位到具体文件和符号再用全局搜索找到这个符号定义在哪个源文件里检查这个源文件是否工程中已包含对应的头文件路径是否在C/C编译选项里。Keil里是在Options for Target - C/C - Include Paths里配置头文件路径的。多说一句如果你在CubeMX生成时勾选了“Copy only the necessary library files”那么工程里只保留用到的外设驱动想手动加其他外设功能就麻烦一些直接在配置界面里启用再重新生成会顺畅得多。7. 从入门到顺手关于STMCubeMX的几点个人体会用STM32CubeMX大概三年多了从最早STC单片机那种纯寄存器编程到后面转STM32直接用库函数再到后来全面转向CubeMX加HAL库这个转变带来的效率提升是肉眼可见的。说几个我自己使用中的真实感受。第一别人的Demo可以直接借。以前用寄存器编程每换一个芯片平台代码几乎要重写。现在用CubeMX加HAL库不同系列之间的HAL函数接口高度统一把别人工程里的.ioc文件拿过来改改芯片型号和引脚映射重新生成代码再移植业务逻辑工作量小到可以忽略。这对学习阶段参考开源项目尤其方便。第二CubeMX生成代码的规范性值得学习。虽然HAL库的代码数量庞大但它的命名规则、注释规范、模块划分逻辑都很清晰看多了之后自己写代码也会不自觉地往这个风格靠拢这对刚入门的人来说是一份额外的收获。第三永远记得重新生成前备份。虽然CubeMX保留用户代码区的机制已经很可靠但谁也不能保证什么时候手滑写错位置或者固件包版本切换导致意外。我现在的习惯是每次重新生成前用版本管理工具给工程做个提交出了问题随时回退这是成本最低的安全网。最后再提一句呼吸灯那个小项目虽然看起来简单但如果你能在CubeMX里独立完成PWM的配置、频率计算、渐变逻辑编写再通过串口把实际占空比打印出来验证那你的STM32开发基本功就基本过关了。接下来就可以尝试更复杂的组合比如ADC采样、SPI驱动屏幕、I2C接传感器这些在CubeMX里配置起来都遵循同样的套路。工具始终是工具重要的是你通过它把底层原理想明白了。祝折腾愉快。