STM32CubeMX图形化配置指南:从引脚分配到代码生成,避开常见坑

发布时间:2026/10/5 10:02:36
STM32CubeMX图形化配置指南:从引脚分配到代码生成,避开常见坑
说实话我最早对STM32CubeMX是很不以为然的。上学那会儿习惯了自己写寄存器、自己搭工程总觉得图形化配置工具是给偷懒的人准备的。后来工作里做产品原型一周内要复用到三块不同型号的板子光是把时钟树撸明白、把外设初始化调试通就耗掉两天时间。那个星期我彻底改观了老老实实打开了STM32CubeMX把配置和代码生成全部交给它自己只负责业务逻辑。这篇教程就是基于这些真实使用经历写的适合两类人一类是把“点灯”都要查半天寄存器的新手另一类是想在项目前期快速出原型、后期方便换芯片的老手。文章会从下载、安装讲到图形化生成工程再讲到生成的HAL库代码结构最后专门聊几个我踩过而且网上很少说透的坑。1. 为什么说STM32CubeMX是“起步就必须用”的工具STM32CubeMX是ST官方出的一款图形化配置工具。你通过界面选定芯片型号、配置引脚复用、设置时钟树、勾选要用的外设和中间件它就会自动生成一套初始化代码和可编译的工程文件。很多人一开始只把它当成“代码生成器”但实际用下来它更像一个“硬件资源总管家”。1.1 传统开发方式到底痛点在哪早年间开发STM32标准做法是下载官方标准外设库自己建工程模板然后手写外设初始化的代码。比如你要用USART就得去翻数据手册查寄存器地址、配置波特率寄存器、使能时钟、配置引脚复用模式。这中间任何一步写错调试串口就永远不出数据。更麻烦的是换芯片比如从STM32F103换到STM32F407引脚数量变了、复用关系变了、时钟频率的上限也变了整份初始化代码基本要重写。我见过不少团队光维护一套“能跑的基础工程”就花了两三周而且不同工程师写的工程风格还不一样A写的GPIO初始化长这样B写的又是另一种结构代码合并的时候非常痛苦。这类问题的根源在于外设初始化是一项高度重复、但细节极多的工作它适合用工具来约束和生成靠人肉手写既容易出错、又浪费人力。1.2 CubeMX实际帮你省掉的三件事第一是省去查阅引脚复用表的时间。芯片内部引脚通常有好几组功能比如某个引脚既能当USART1_TX又能当TIM2_CH1。CubeMX的Pinout视图用不同颜色标出了当前选定的功能你按下拉菜单就能切换所有冲突引脚一目了然。以前查复用表几十页数据手册的活儿现在变成下拉框点选。第二是时钟树自动计算。芯片的时钟来源可以是HSI、HSE或者PLL还要根据系统频率反推各总线分频值。手算时倍频系数、分频系数稍微算错一步系统频率就偏了。CubeMX的Clock视图里你只要输入想要的系统时钟频率它会自动调节PLL配置还会标红显示哪个值越界了。这一点在初期查定位“为什么定时器时间不准”的问题时价值极高。第三是工程结构标准化。CubeMX生成的工程外设初始化、中断回调、用户代码区是严格分层的。整个项目结构清晰后来接手的工程师一眼就能看懂哪里是自动生成的代码、哪里是自己写的逻辑团队协作成本明显下降。1.3 什么人适合学什么人可以绕开如果你属于以下几种情况我建议你直接上手新手刚接触STM32想把精力放在业务逻辑而不是底层初始化细节上项目周期紧张需要快速验证硬件方案同一套代码要在不同型号芯片之间移植需要用到FreeRTOS、FatFS、USB协议栈这类中间件CubeMX里勾选一下就能集成如果你是在裸机上学习某一块外设的底层原理专门想研究寄存器时序那CubeMX生成的代码反而不直观这时更适合直接手写操作寄存器的方式。不过这样的需求基本不会出现在正式产品研发里。2. 下载前的准备工作版本、运行环境、网络吃喝下载安装步骤本身不复杂但很多人在“怎么选版本”“为什么打开后一直卡在下载固件包”“要不要装Java”这些环节翻了车。提前把这些弄明白后面会顺利很多。2.1 第一步先确认你的Java环境STM32CubeMX本身是Java程序虽然新版安装包自带JRE但实际运行中尤其是从6.0以上版本开始如果系统里没有可用的Java运行时环境软件启动时会报错或者功能异常。我的习惯是先把Java装好省得后面排查启动问题两小时。在终端里执行以下命令可以快速判断是否已安装java -version如果提示找不到java就去Oracle官网或者用OpenJDK装一个长期支持版本比如Java 11或者Java 17。需要注意别只装JRE装JDK也行反正都能运行。装好后确认环境变量里JAVA_HOME和PATH都能正确指向安装目录。有个容易忽略的点64位系统尽量装64位Java不然CubeMX偶尔会出现内存不足的报错尤其是工程文件大、打开过慢的时候。2.2 官网下载的完整路径与版本选择CubeMX要到ST官网获取路径为ST官网首页 → Tools Software → STM32Cube ecosystem → STM32CubeMX。页面里会有当前最新版本的下载区通常提供Windows、Linux、macOS三个平台的安装包。这里我提个醒ST官网对浏览器兼容性有点挑剔如果你点击下载之后页面没反应可以清一下浏览器缓存或者换成Chrome/Edge再试多数时候就能正常弹出来。版本选择上我个人的建议是选最新的稳定版同时注意看发布说明里支持的固件包版本。CubeMX的版本更新频率中等新版本一般会带来界面优化和新芯片支持但是如果你用的是公司内网的旧工程升版本后固件包可能也会跟着变需要注意兼容性。如果你只是学习、不看最新的芯片型号选择一个稳定的次新版也完全够用。我在项目里长期使用8.0.x系列稳定不太出幺蛾子。新版本界面稍有点变化但核心操作逻辑一致。2.3 安装过程中的Java前置检查与目录选择Windows下安装包是.exe格式双击后一路Next就行。这里有几个细节值得注意安装路径尽量不要带中文和空格建议直接用默认路径或者改为像D:\ST\STM32CubeMX这种简单路径在安装向导的“Select installation folder”界面会有一个选项询问是否创建桌面快捷方式可以按自己喜好勾选如果电脑上装了安全软件第一次运行时可能会拦截CubeMX对用户目录的操作放行即可Linux和macOS用户需要注意权限问题建议把压缩包解压到/opt或者用户目录下不要在/usr这种需要root权限的目录里折腾。2.4 固件包是什么为什么经常下载失败CubeMX生成的代码离不开“固件包”你可以把它理解成一种“芯片家族的资源库”。生成工程前CubeMX会根据你选的芯片型号去下载对应系列的固件包例如STM32F4系列的固件包就包含HAL库、中间件组件和示例代码。这步是国内用户最容易卡住的环节固件包体积大、下载服务器在国外经常下载失败或者速度极慢。第一次使用建议提前做好心理准备方案如下在CubeMX里点击Help → Manage embedded software packages选择需要的芯片系列手动触发下载如果速度过慢可以考虑在下非高峰期尝试比如早上网络空闲时段下载下来的固件包会缓存在本地STM32Cube目录第二次使用时不再需要重新下载有一个规律你可以利用只要本地存在固件包CubeMX离线也能生成工程。所以第一次下载成功后建议备份一下固件包目录。后面换了电脑或重装系统直接把目录复制过去就能用省去再次等待下载的时间。这一步我强烈建议做能帮你节省大量时间。3. 首次启动与界面导航这些功能藏得深安装完成后双击打开你会看到两个主要视图一个是左侧的“New Project”入口一个是中部的工作区后面生成工程后界面会变成Pinout视图、Clock视图等多个页面。首次打开界面可能有点杂但真正常用的只有三个地方芯片选型入口、引脚配置视图、时钟树视图。3.1 新建项目是选芯片还是选开发板点击“New Project”后会有两个标签页MCU Selector和Board Selector。前者是按芯片型号搜索后者是按ST官方的开发板型号搜索。自己设计的板子当然用MCU Selector在搜索框输入芯片型号比如直接敲STM32F103C8下方列表就会出现对应芯片双击就能进入配置界面。如果是初学用官方开发板比如NUCLEO或Discovery板直接在Board Selector里输入板卡型号回车会自动带出该板子基本的引脚配置比如板载LED、按键、调试器等的默认复用关系非常方便。省去你手动去查原理图分配的引脚。这里有一个小技巧即便用开发板的名字进入也能随时在Pinout视图里重新分配引脚并没有说你必须完全按默认配置来。3.2 Pinout视图到底怎么用Pinout视图就是芯片封装图周围是一圈引脚点一个引脚会弹出可选功能菜单。刚开始的人可能会有点懵我到底该选哪一项举一个最基础的例子配置LED的GPIO输出先在芯片图上找到控制LED的引脚点击它弹出的可选功能里选择GPIO_Output然后在中部的“System Core”树里找到GPIO对应引脚会出现在列表中在列表中选中该引脚下方就能配置输出模式、推挽/开漏、上下拉、输出速度等参数整个操作的核心逻辑就是先分配引脚的功能再在外设树里微调参数。你对芯片的理解越深配置起来越顺但即便理解不深界面上每个选项旁边都有简单的英文描述配错了也能在代码里看出问题。3.3 Clock视图里的红色警告不能忽略Clock视图可能是最让新手困惑的界面之一。整个视图看起来像一张复杂的树状图里面有各种分频器、倍频器数值还带颜色变化。你以为要自己算其实只要在“HCLK”输入框里填预期的系统主频比如直接输入72代表72MHzCubeMX会自动反推出一整套频率配置。如果某个配置超出芯片允许范围相关数值会变成红色比如PLL倍频超标。有一个工程上的常见错误系统主频想跑72MHz但晶体接的是8MHz结果PLL配置错误最终生成代码后闪烁的LED频率看着就不对。这种情况下最直接的排查方式就是回到Clock视图看一眼配置值有没有红色异常。如果你对某个时钟的精度不满意也可以在Clock视图里调整输入时钟源类型HSE、HSI、LSE、LSI的切换就在这里。4. 图形化配置要点GPIO、外设和中间件的组合逻辑很多人配置完时钟后就把结构树里能勾的全勾上结果生成代码时看到一大堆初始化函数反而不知道从哪里开始写业务。正确的做法是“够用原则”——你只勾选本次项目真正用到的外设和中间件其余的保持默认禁用这样生成的代码体积小、逻辑清晰、调试时也容易定位问题。4.1 外设树的层级与配置方式左侧的“Categories”树结构是按外设类型分组的比如“Analog”下是ADC、DAC“Timers”下是TIM1~TIM17“Connectivity”下是USART、SPI、I2C、USB等。点开每个外设右侧会出现Mode和Configuration两个区域。Mode这项外设要运行在什么模式比如USART的Asynchronous异步I2C的I2C模式定时器的PWM Generation CH1等Configuration选中一个Mode后下方会展开参数配置项比如波特率、数据位、停止位、极性等有个实际操作经验配置定时器做PWM输出时先选好一个通道的模式然后把预分频系数(Prescaler)和自动重载值(Counter Period)填好再回Clock视图确认定时器时钟源频率。我可以举个例子定时器时钟是72MHz想输出1kHz的PWMPrescaler设为71Counter Period设为999重装载后就是1000Hz。这个换算逻辑清楚之后调频率就是改两个数的事。4.2 中间件选用的容易踩的坑中间件就是库自带的软件组件比如FreeRTOS、FatFS、USB Device、LWIP。在CubeMX里勾选它们确实方便但每个中间件配置项多不像GPIO那样填个模式就完事。比如FreeRTOS在中间件分类下点选FreeRTOS后会自动弹出HAL库与CMSIS_V2的版本选择问题。如果你打算在项目中用CMSIS-RTOS API接口就选CMSIS_V2否则直接用原生FreeRTOS API也完全可以。还有一个常见问题是堆栈大小的设置小程序用默认4096可能够但如果任务里用了较大的数组或递归函数就要在配置里调大堆大小否则跑起来会出现HardFault。还有一点生成出来的FreeRTOS工程里默认会有一个MX_FREERTOS_Init函数里面创建了默认任务。很多新手把业务代码直接写在StartDefaultTask里后来想加第二个任务却在创建任务时找不到对应的错误信息最后发现是堆内存不足。这类问题在网上被问了很多次核心原因就是中间件配置界面里堆大小的数值和小任务数量没配合好。4.3 引脚冲突提示不要强行绕过当你把某个引脚配置成第二个功能时如果这个引脚已经被占用了布局视图上会用特定颜色标出冲突。此时最好停下来换一个空闲引脚而不是强行生成代码。强行生成后你会发现编译能过但实际运行时电平错乱、外设无法正常工作而且这种问题非常难定位。我自己的习惯是先在原理图阶段就列一张“引脚分配表”把每个引脚的用途提前计划好再回到CubeMX里对照配置。这样不仅省事还能在原理图阶段就暴露出引脚冲突的问题不用等到调试时才发现。5. 代码生成设置与Keil对接这一步做对了后面少加班配置完引脚和外设之后点击“Project → Generate Code”会弹出一个工程配置窗口。很多人在这个窗口里随便点Next后面工程在MDK里编译报错原因往往就是这里没设置好。5.1 Toolchain/IDE选择与最小化生成工程设置里最关键的一项是“Toolchain/IDE”。用Keil MDK就选MDK-ARM V5用IAR就选IAR用STM32CubeIDE则选STM32CubeIDE。这里我遇到过一个问题下载的CubeMX版本较新生成MDK工程后用老版本的Keil打开提示Device not found原因是对应芯片的支持包没安装。遇到这种情况去Keil的Pack Installer里更新对应STM32系列的软件包就能解决。另外尽量不要选择“Copy only the necessary library files”除非你完全清楚固件包的依赖结构。正常使用就选择“Add necessary library files as reference in the toolchain”这样生成的工程环境更稳定不至于今天加了某个外设之后发现库文件缺失。还有一个容易被忽略的选项“Generate peripheral initialization as a pair of .c/.h files per peripheral”建议勾选上。勾选后每个外设会单独生成一个.c和.h文件结构清爽。如果不勾选所有初始化代码会被集中堆到一个main.c里几千行代码放在一起后期维护非常痛苦。5.2 用户代码区的概念别把代码写在错误的位置CubeMX生成代码后你再次修改配置并重新生成它会尽量保留你原来手动添加的代码。但这个“保留”是有条件的你的代码必须写在指定的用户代码区内否则重新生成时会直接覆盖掉。在生成的main.c里你会看到类似这样的注释标记/* USER CODE BEGIN Includes */ /* USER CODE END Includes */凡是你手动添加的内容一定要放在这两个注释之间。项目里的每个外设初始化文件、中断回调文件里都有类似的结构养成习惯是最重要的。否则你会遇到这种惨剧上午在代码里加了好几百行逻辑下午因为改了一个引脚配置重新生成工程再编译发现函数全没了。我自己就踩过这个坑。后来我给自己定了一条规矩手动加的代码一律放进USER CODE区域哪怕只有一行函数声明。操作上当你想在某个文件里加东西之前先搜一下“USER CODE BEGIN”的位置找到对应的区域再加一次都不要例外。5.3 生成后的目录结构与工程打开方式生成完毕你会看到工程目录里包含几个文件夹Core/存放main.c和中断回调等核心代码Drivers/HAL库的源代码和头文件Middlewares/如果勾选了中间件这里会有对应代码Debug/或MDK-ARM/根据你选的IDE不同这里存放工程文件和编译中间产物用Keil打开工程时直接打开MDK-ARM目录下的.uvprojx文件即可。打开后第一件事建议先编译一次看看默认生成的工程是否能直接通过。如果报错优先检查三处芯片型号是否选择正确、Keil的CMSIS和Device Pack版本是否太旧、CubeMX生成时Toolchain/IDE是否选对了。6. 上手实测从新建工程到点灯完整走一遍前面讲了很多理论这部分我用一个最常见的例子——GPIO点亮LED把整个流程串起来。即使你已经配过复杂外设再回头看这个过程也能帮你理清CubeMX和实际工程之间的映射关系。6.1 芯片选型与基础时钟配置我这边的目标芯片是STM32F103C8T6蓝色板子那种板载LED一般接在PC13引脚低电平点亮。打开CubeMX新建项目选MCU型号搜索STM32F103C8T6双击进入配置界面。进入后首先配置时钟。在右侧的Clock视图里输入HCLK为72让系统频率跑到72MHz。默认情况可能直接就是72MHz但最好确认一下有没有红色报警。之后在左侧System Core里点SYS把Debug模式选成Serial Wire。这一步非常关键不选的话芯片用过一次后第二次下载程序时会提示找不到设备因为引脚被占用了。6.2 引脚分配与GPIO参数设置在Pinout视图里找到PC13引脚点击后选择GPIO_Output。然后在左侧外设树里展开System Core → GPIO会看到PC13已经出现在列表中。点击它右侧出现详细配置GPIO output level这个决定初始是输出高还是低LED用低电平点亮的话可以先设为Low避免一上电就常亮GPIO mode选Output Push PullMaximum output speed选LowUser Label可以给引脚起个名字比如LED_Pin这样后面生成的代码里就不会直接看到PC13这种原始定义而是能用LED_Pin_GPIO_Port这种语义化名字之后直接点击“Project → Generate Code”填好工程名和路径Toolchain选MDK-ARM生成就行。6.3 在主循环里写业务逻辑生成的main.c里while (1)循环里是空的这地方就是用户代码区。/* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { HAL_GPIO_WritePin(LED_Pin_GPIO_Port, LED_Pin, GPIO_PIN_RESET); // 点亮 HAL_Delay(500); HAL_GPIO_WritePin(LED_Pin_GPIO_Port, LED_Pin, GPIO_PIN_SET); // 熄灭 HAL_Delay(500); /* USER CODE END WHILE */ }这里要注意的是如果你在GPIO配置里设置了User Label为LED_Pin那么生成的头文件里会有#define LED_Pin GPIO_PIN_13和#define LED_Pin_GPIO_Port GPIOC。直接用这两个宏比写原始寄存器地址可读性高得多。编译下载后可以看到LED按1Hz频率闪烁。这一步通了说明你的CubeMX配置、Keil环境、芯片调试链路都正常后面再扩展其他外设就有了基础。7. 工程维护与多人协作CubeMX带来的隐藏红利很多人用CubeMX只是因为“配置起来方便”但真正让我离不开它的原因是工程维护。硬件方案调整是常态比如原来用的USART1调试后来把调试口改到了USART2或者把某个引脚从普通IO变成PWM输出。如果没有CubeMX这种改动意味着要手改初始化结构体、中断处理函数还可能漏改对应的时钟使能函数。有CubeMX的话只需要在界面里改配置、重新生成代码改动便准确落地。我们把CubeMX生成工程作为基础再结合项目的实际业务代码整体维护成本下降了一大截。7.1 工程文件纳入版本管理的细节建议把CubeMX的.ioc文件加入Git或SVN因为.ioc文件记录了芯片型号、引脚分配、外设参数和时钟配置的所有信息。其他同事拉取代码后只要打开.ioc文件就能完整复现所有人对硬件的配置过程。这一点比维护一堆零散的初始化代码文档靠谱得多。同时自动生成出来的Drivers目录、MDK-ARM目录要不要入库这要看团队习惯。我个人的建议是将生成的代码提交入库减少成员之间环境差异带来的编译问题但前提是CubeMX版本要尽量统一。如果团队中有人用CubeMX 8.0有人用7.0生成的代码可能会有一定差异。这种情况下可以考虑只提交.ioc文件和用户代码由各人本地重新生成。7.2 芯片换型时的“软迁移”项目做到一半芯片供货紧张焊盘兼容的另一型号芯片也有可能被用到。这种情况在CubeMX里处理起来非常爽打开.ioc文件在芯片选型界面切换到新的型号CubeMX会重新校验引脚复用关系保留大部分原有配置。如果新芯片引脚布局不同界面会用警告提示你重新规划引脚。整个过程不需要动业务代码只需要解决引脚层面的冲突再重新生成一次工程就行。8. 常见报错与疑难杂症的排查链路用CubeMX时间久了会遇到几种典型问题。我把排查思路和解决过程完整列出来这些经验网上渠道分散今天整理成一份。8.1 第一次下载程序失败芯片像“锁死”了一样现象是CubeMX生成的工程在Keil里编译成功但点下载时提示No target connected或cannot access target。很多人会以为芯片坏了其实大概率是上电后芯片的SWD调试引脚被代码复用掉了或者Debug配置没被正确生成。排查链路是这样的检查CubeMX里是否给SYS选上了Serial Wire调试模式。没选的话生成的代码默认不初始化调试接口。检查BOOT0引脚的电平。正常情况下BOOT0接低电平如果你把它拉高进入了Bootloader模式SWD接口也可能失效。暂时无法确认时可以按住复位键的同时尝试下载点击下载瞬间松开复位键。如果以上都没问题把Keil里Flash Download配置中的“Reset and Run”勾选上下载后自动复位运行排除复位引脚被占用的可能。这类问题第一次遇到比较慌但按这个路数排查基本几分钟就能解开。8.2 生成的代码一运行就进HardFaultHardFault是个笼统的报错出现时先别急着看代码。我的经验是先做两件事一是在Keil的View → Registers里看当前程序卡在哪个函数二是看硬件栈指针的位置。绝大多数情况下是数组越界或者指针操作出错但也有不少时候问题出在CubeMX生成的代码本身比如某个中间件占用内存过大、堆栈设置不足。排查步骤建议如下在CubeMX里把FreeRTOS的堆大小调大重新生成后测试检查是否有中断服务函数里做了耗时操作中断里不宜做复杂逻辑查看main.c里初始化顺序某些外设要在中断优先级分组之后才能初始化CubeMX默认生成顺序一般没问题但你自己添加了代码后顺序可能会被打乱8.3 引脚配置明明改了生成代码却没有变化这种情况有一个非常隐蔽的可能CubeMX默认不会删除你代码里旧引脚相关的初始化。举个例子你原来在PB5上配置了GPIO_Output后来改到PB6重新生成后MX_GPIO_Init里可能会发现两个引脚都被初始化了。原因是你之前手动在USER CODE区域写过对PB5的操作重新生成时这些代码保留下来看起来就像“配置没生效”。正确做法是每次改引脚配置时去.c文件里搜一下旧引脚相关的宏定义把无用代码清理干净。另外CubeMX生成的代码里引脚宏定义集中在主头文件中如果你发现#define还是旧的确认一下是不是只改了.ioc文件但没有重新生成代码。8.4 固件包版本冲突同一个芯片系列CubeMX自动下载的固件包可能会有多个版本。比如别的东西依赖某个版本的HAL库但本地固件包是另一个版本中间可能出现函数定义不一致、编译报错声明的函数参数对不上。这种问题最好的办法是在项目管理器里重新选择固件包版本或者在外部下载对应版本的固件包手动导入。具体路径是“Help → Manage embedded software packages”在已安装列表里可以看到当前版本和可用状态。如果你不确定版本怎么选直接参考CubeMX提示的建议版本一致的版本通常没太大问题。9. 进阶用法与日常使用习惯总结正文部分写到这里核心流程和常见坑都讲透了。最后补充几个我长期使用中总结出来的小习惯也算是一些能提升效率的细节。习惯一每次生成代码后先备份一份“干净版”。也就是说在修改任何用户代码之前先把CubeMX生成的工程目录完全复制一份作为备份。之后如果改坏了直接拿备份来对比很快就知道是哪里动了。这个习惯成本极低但能把从“程序跑不起来”到“找到原因”的时间缩减到十分之一。习惯二给引脚起语义化名称。所有核心引脚都在CubeMX的User Label里填上含义明确的名字比如KEY_ENTER、OLED_SDA、MOTOR_1_PWM。这样生成的代码里所有外设操作都变成可读性强的宏整个工程像在写业务逻辑而不是在一堆寄存器地址里猜。习惯三把.ioc文件作为硬件配置的唯一事实来源。项目里所有“硬件初始化说明”、“原理图注释”、“代码注释”都可能过期但.ioc文件绝对同步。新人加入时第一步打开.ioc文件就能快速理解整块板子的资源分配比追着老员工问半天效率高得多。习惯四遇到新芯片先用CubeMX把所有外设都“点亮”试一遍。我说的是“试跑”不是在产品里用。拿到一块新板子先在CubeMX里把ADC、PWM、USART、I2C全部配置一遍生成一个测试工程逐一验证硬件通路是否正常。这能让你快速熟悉新平台的坑正式开发时少走弯路。STM32CubeMX说到底是一个把初始化复杂度封装掉、把工程管理标准化的工具。它不能替代你对芯片原理的理解但能帮你把精力集中到真正有价值的业务逻辑和系统设计里。如果你还在观望或者刚接触不妨就按这篇文章的路径先搭一个小工程跑通再从串口、中断这些小外设慢慢拓展。用顺手之后你会回来感谢那个愿意花时间研究它的人。