嵌入式软件开发圈子最近流行一个词叫“古法编程”。说白点就是那种把MCU当单片机用、把所有逻辑都塞进main函数、靠寄存器手册和示波器过日子的开发方式。我刷招聘网站的时候看到不少嵌入式软件开发面试题考察重点早就不再是“会不会点亮LED”而是工程化能力、代码质量、调试方法这些硬功夫。这种变化不是矫情是真的到了该跟古法编程说再见的时候了。这篇文章我打算从几个角度聊清楚这件事古法编程到底指什么、为什么它曾经合理但现在正在变成负债、现代化开发方式到底好在哪、以及一个真实的“去古法”迁移路线。不管你是刚入行的学生、做产品固件的工程师还是带团队的Leader这篇都值得看完。尤其是还在靠“人肉单步调试手工管理代码”干活的朋友看完应该会有不少共鸣。1. 先说说什么是“古法编程”以及它曾经为什么能撑起一片天1.1 古法编程的典型画像古法编程不是一个精确的技术定义它是嵌入式开发圈子里对“传统手工作坊式开发方式”的一种调侃。我见过的古法编程画像大致是这样的一个人守着一种特定的单片机型号手撸寄存器配置依赖一份几百页的芯片参考手册代码风格以“能用就行”为最高准则。工程结构上一个main.c能写到几千行全局变量满天飞状态机靠if-else嵌套硬叠。函数之间的耦合用“我改了这个那边会不会炸”来判断。调试手段更加复古。早年很多人连仿真器都舍不得买或者开发板上的Debug接口没引出来于是全靠串口打印。跑起来不对就满天撒printf然后瞪着眼睛看串口助手。再不行就点灯。红灯闪三下表示“进来了”绿灯闪两下表示“参数不对劲”。代码版本管理没有的。连Git都没用过代码备份方式是复制一份命名为“final_2024_ok_really”的文件夹。工具链和构建就更“原生态”了。有人连IDE都不用直接在文本编辑器里写代码然后用Dev-C风格的手写Makefile编译。或者反过来用某厂商老掉牙的IDE点一下“Build”按钮过一会儿看到“0 Errors, 0 Warnings”就心满意足。至于单元测试、自动化构建、持续集成在这些团队里闻所未闻。1.2 古法不是错但成本在悄悄变化我得先说句公道话古法编程在十年前并没有那么不可接受。那时候的嵌入式产品好多就是单机运行、不联网、没有OTA、不需要过多交互的控制器。芯片主频几十兆Flash和RAM动不动按KB算产品逻辑相对简单。一个人确实能从寄存器到业务全包哪怕代码是一坨也能跑。反正出货之后基本不维护。但现在不一样了。现在的MCU动不动就跑到几百兆赫兹双核、无线协议栈、复杂外设、安全启动、远程升级几乎成了标配。产品从上电到联网再到对接云平台里面的代码量已经不是几万行能打住的。这种复杂度下古法思维就成了最大的风险源。你手写寄存器配置工程师跳槽了整个模块没人敢碰你不写测试改一个中断优先级就能把两周前的功能悄悄弄坏你不做版本管理产品出了批量问题连是哪次改动的锅都定位不到。所以“古法”本身没有原罪它的问题在于过去用这个方式付出的是看得见的代价换回的是“能跑”的结果。现在再这么干连“能跑”都不敢保证了。这已经不是优化不优化的问题而是这个行业从个人英雄主义走向系统工程时代了。2. 嵌入式软件开发为什么要跟古法说再见2.1 芯片和产品的复杂度已经超过了人脑的承载上限我记得有个朋友接手一个项目代码大概二十万行全是C没有任何分层核心业务逻辑混杂着寄存器操作、延迟等待、错误处理和界面显示。他拿到手第一周光是搞清楚“到底哪个函数改了哪个外设”就花了两天。这不是他能力不行而是这种代码本身就超出了人脑的解析能力。现代芯片内部集成了太多东西。一个典型的IoT SoC里有CPU、GPU或者专用加速器、Wi-Fi/BLE基带、电源管理单元、加密引擎、DMA控制器、一摞通信外设。它们之间还存在复杂的时钟树、电源域、中断路由。靠人肉记忆去配置这些再靠人肉推导去判断不同模块之间的影响已经完全不可行。这种感觉就像徒手整理一间塞满杂物的仓库每件东西你都认识但它们之间的关系你理不清。古法编程的精髓是“理解每一个细节”但现代嵌入式系统里单个细节的数量已经爆炸人根本理解不过来。这时候靠什么靠抽象、靠分层、靠工具、靠团队的协作机制。而这些恰恰是古法编程最不擅长的。2.2 团队协作与交付节奏容不下“一个人的工程”嵌入式软件开发早就不是只写固件那么简单了。产品端、云平台、App、算法团队、硬件团队大家都在并行推进。固件作为中间层既要配合硬件的改动又要支持业务的需求变化。这种多线程协作下代码不再是“私有财产”而是整个系统的一部分。古法编程非常依赖个人状态。最典型的情况是某个核心模块只有一个人懂他休假了其他人连编译都跑不顺。这种情况在小团队里尤其致命。我见过一个创业公司固件就三个人在写其中一个人走了之后剩下两人不得不花一个月时间重构他的代码。不是他代码写得多差而是他连注释和提交记录都没有留走之前只甩了一句“有问题来找我”然后电话就再也打不通了。现代开发流程的很多习惯Git提交规范、代码评审、模块化设计、单元测试、CI自动化本质上都是为了降低这种“单点依赖”。代码不挂在某个人脑子上而是挂在流程上。任何人离开文档、提交记录、测试用例都在新人上手的速度会快很多。这不是不爱惜个人能力而是组织要长期运转必须把经验沉淀到流程里而不是人脑里。2.3 现代工具链已经足够成熟没有理由不用以前我写驱动头文件都要自己分类靠人工保证二进制兼容。现在你打开任何一个主流厂商的SDK里面都是完整的驱动层、HAL层、中间件组件代码生成工具一个比一个方便。以前的构建是“我交叉编译一份能跑的固件”现在的构建是“一套配置同时在本地编译、跑单元测试、做静态分析、生成覆盖率报告”。工具链的成熟程度说实话已经远超很多还在用古法的人的认识。编译器、调试器、仿真器、逻辑分析仪、静态分析工具、软件包管理器随便哪一样拿出来都能把开发效率提升一个量级。Git和GitLab/GitHub这些平台更是把协作、评审、CI/CD全打通了几乎成了软件行业的默认基础设施。用不用是一回事但没有理由说“嵌入式特殊不适合现代工具”。嵌入式确实有实时性、资源受限这些特殊性但这些特殊性影响的是具体方案的选型不是要不要工程化的问题。如果一个团队还在用U盘拷贝代码、用Excel记录缺陷、用离线编译器做构建那该反省的不是工具不够好而是自己有没有主动去适应这个已经变化的世界。维度古法编程现代嵌入式开发代码组织单文件几千行、全局变量共享模块化、分层、组件化构建方式手写Makefile或IDE按钮CMakeNinja、可复用工具链版本管理复制文件夹、final版Git全流程分支、Tag、提交规范调试手段printf、串口助手、点灯GDB/仿真器/SWO/逻辑分析仪验证手段靠眼睛看现象单元测试、静态分析、CI自动化知识传承个人记忆、口口相传文档、代码评审、测试用例沉淀资源管理手工初始化、靠人肉避免冲突HAL抽象、框架约束、编译期检查3. 告别古法不是推翻底层而是武装底层3.1 嵌入式软件开发的现代工程化底座很多嵌入式工程师对“工程化”有误解以为工程化就是套用互联网公司的流程写文档、开会、评审、写一堆没人看的PPT。真正的工程化不是搞形式而是把开发过程中必然遇到的“风险”管理起来。举个最简单的例子一个改动了时钟树配置的提交怎么确认其他外设没有受影响过去靠人肉推理现在靠的是“改动被测过”。怎么保证被测过靠可重复的自动化测试。怎么让测试跑起来靠构建系统。怎么知道测试是否通过靠CI报告。这一整套链路从代码提交到结果反馈都是工程化底座的一部分。底座的第一层是代码管理。Git现在基本是标配了。分支策略、提交规范、标签管理、Release分支这些不是浪费时间的仪式感。我自己团队里的规矩是每个严重bug的修复必带一个回归测试用例。这个用例不是为了追求覆盖率数字好看而是确保这个bug不会在下一次改动时悄悄回来。Git里每一次提交都和这个用例挂钩出了问题可以快速定位是哪一次提交引入了回归。底座第二层是构建系统。以前大家嫌构建系统“配置复杂”但现代构建系统已经把复杂性封装得很好了。CMake已经成为跨平台事实标准配合Ninja构建速度飞快还能管理复杂的编译器参数、链接脚本、依赖包。用CMake最大的好处是“描述性”而非“命令式”——你描述目标是什么、依赖什么、怎么编译构建系统负责具体执行。这比手写Makefile里那一堆循环和变量替换要可靠得多。底座第三层是验证体系。这一层包含单元测试、集成测试、静态分析、覆盖率、性能测试。嵌入式开发过去不太讲测试觉得“硬件上跑一跑不就好了”但现在代码量大、迭代快人工验证完全跟不上。现代验证体系的好处是把测试自动跑起来让每个提交都面对同样的检验减少“改这里炸那里”的莫名回归。3.2 关键一步构建系统与依赖管理的现代化如果只能选一件事做我建议先把构建系统换成CMake。这是投入产出比最高的一步而且对现有代码的侵入可以很小。我见过太多老工程长这样根目录放一个Makefile里面定义了几十个变量有编译选项、头文件路径、源文件列表全部手写。每次加一个文件就要去编辑那个长长的SRCS列表。每次换编译器、加编译参数都要小心不要碰坏其他部分。我甚至见过Makefile里的tab空格混用导致明明改了代码却编译的是旧版本的情况排查了整整一下午。CMake把这些东西变成了“描述”。比如说一个典型的MCU固件工程用CMake描述起来大概就是这样cmake_minimum_required(VERSION 3.20) project(firmware C ASM) set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/toolchain-arm-none-eabi.cmake) add_executable(app src/main.c src/gpio.c src/uart.c src/scheduler.c ) target_include_directories(app PRIVATE src/ drivers/ third_party/FreeRTOS/include ) target_compile_options(app PRIVATE -mcpucortex-m4 -mthumb -Wall -Wextra -Werror -Os -ffunction-sections -fdata-sections ) target_link_options(app PRIVATE -Wl,--gc-sections -T${CMAKE_CURRENT_SOURCE_DIR}/linker/STM32F4_flash.ld )这段描述比手写Makefile清晰很多编译器参数在toolchain里定义源文件列表在CMakeLists里维护头文件路径一目了然。新同事接手看工程结构就能快速知道代码组织方式。依赖管理方面CMake的FetchContent或者厂商提供的SDK包管理器可以做模块化集成。有了构建系统做基础后面挂单元测试、CI都顺理成章。3.3 调试和测试从printf到可复现验证printf调试法不是不能用但它有几个硬伤不能看时序、不能看内存、不能条件断点、不能复现随机性问题。遇到一个偶发的崩溃你可能要把同样的操作重复几百次才能抓到一点蛛丝马迹。现代调试手段丰富得多GDB配合OpenOCD/J-Link可以做断点、变量监视、内存查看SWO引脚可以无侵入地输出调试信息逻辑分析仪可以看总线时序Trace工具甚至可以完整记录CPU执行历史。工具可以后面慢慢学但方法论必须先改调试要从“看输出”变成“看状态”。我调试一个偶发的HardFault时先看故障状态寄存器、PC指针、LR寄存器再配合调用栈回溯几秒钟就能定位到问题函数。如果是用printf的老方法可能连问题发生在哪一层都要猜好久。测试也是。嵌入式代码很难测主要是硬件依赖、时序依赖、延迟依赖。但现代做法是分层测试纯逻辑层状态机、协议解析、算法直接跑单元测试硬件相关层驱动、寄存器通过mock或者HAL抽象来测。现在的Unity、CMock、CppUTest这些轻量级测试框架专为嵌入式C/C设计资源开销很小效果却立竿见影。我在一个协议解析模块里写了三百多行单元测试后来固件上线后几乎没在这个模块返工过。3.4 从裸机到RTOS/组件的思维升级古法编程在软件架构上最大的痛点是喜欢用“前后台大循环”。main函数里一个while(1)前面初始化一堆外设后面循环里轮询这、轮询那。逻辑简单时还好一旦外设变多、事件变杂大循环就成了“哪里都卡顿”的重灾区。一个传感器阻塞等待几百微秒另一个通信任务的时序就被拖垮。我并不是说裸机一定不好。对极简单的应用裸机仍然是功耗、延迟、代码量综合最优的选择。但一旦你的应用有多个并发任务、有实时约束、有状态切换需求用RTOS不是因为“MCU跑不动裸机”而是因为RTOS让代码结构变得清晰。任务就是独立的功能单元靠消息队列和信号量通信优先级明确、调度透明。这让代码的逻辑从“线性堆叠”变成了“模块协作”对团队的协作开发和后期维护都友好得多。现代开发的另一个思维升级是“组件化”。把驱动、中间件、协议栈、业务逻辑拆成独立模块模块之间用标准接口通信。好处是驱动层换了芯片型号业务层不用动协议栈升级了驱动和业务都不受影响。这种架构变化根子上是一种“面向系统”的思维取代“面向芯片”的思维而后者正是古法编程最深的烙印。4. 一次完整的“去古法”实操路线4.1 第一步代码先进入Git把历史找回来如果团队还在用“final_v12.rar”这种备份方式第一步就是把所有代码纳入Git。这一步基本没有技术门槛只需要克服“懒”和“不习惯”。实际操作很简单# 进入项目根目录 cd project_dir git init git add . git commit -m init: 导入现有代码基线但这只是开始。要让Git真正发挥价值必须建立几个习惯一次提交只做一件事提交信息写清楚“改了啥、为什么改、影响范围”每个功能/修复开独立分支合并时走代码评审重要版本打Tag比如v1.0.0、v2.1.0方便回滚和追溯。提示千万不要把所有代码压成一个巨大commit。那种“一天改完全部”的提交等于没有提交记录。回滚、查历史、定位回归时这种commit帮不了任何忙。4.2 第二步用CMake把构建标准化把工程从IDE/手写Makefile迁移到CMake是“去古法”最有成就感的一步。迁移过程建议渐进式先在外层加一个CMakeLists.txt把现有的源码目录全部包含进来先保证能用CMake构建出和旧方式一样的固件。构建出来了再逐步把编译参数、链接脚本、预处理宏从Makefile和IDE配置里搬到CMake里。这个阶段不必追求重构代码结构先把“构建结果”标准化。标准化的构建系统有个额外好处可以很容易接入命令行编译和脚本化流程。我团队里的固件工程师现在在本地跑一条命令就能完成编译、烧录、跑测试而不是打开IDE点半天鼠标。CI也是同一套构建本地和云端保持一致省掉了“在我机器上能编译在CI上挂了”的麻烦。4.3 第三步给核心逻辑挂上单元测试这一步很多人会卡住因为“我的代码怎么测啊”。确实直接在寄存器代码和中断回调上写单元测试非常痛苦。正确的做法是先从“纯逻辑”下手。比如协议解析、状态机、PID计算、CRC校验、数据格式转换这些模块往往不依赖硬件或者只依赖抽象出来的接口。以协议解析为例接口设计成“传入字节流、输出解析结果”的纯函数单元测试就好写了// 被测函数 parse_result_t parse_frame(uint8_t *data, uint32_t len); // 单元测试用例Unity框架风格 void test_parse_valid_frame(void) { uint8_t frame[] {0xAA, 0x55, 0x01, 0x02, 0x03, 0x00, 0x7F}; parse_result_t result parse_frame(frame, sizeof(frame)); TEST_ASSERT_EQUAL(PARSE_OK, result.status); TEST_ASSERT_EQUAL_INT32(0x010203, result.payload); } void test_parse_invalid_crc_frame(void) { uint8_t frame[] {0xAA, 0x55, 0x01, 0x02, 0x03, 0x00, 0x00}; parse_result_t result parse_frame(frame, sizeof(frame)); TEST_ASSERT_EQUAL(PARSE_CRC_ERROR, result.status); }测试挂在CMake里也不复杂CMake有原生测试支持enable_testing() add_executable(test_parser tests/test_parser.c src/parser.c third_party/Unity/unity.c ) target_include_directories(test_parser PRIVATE src tests) add_test(NAME parser_tests COMMAND test_parser)跑一下ctest所有用例全绿心里踏实。以后每次改协议解析都会有一排用例帮你盯着。别急着覆盖率百分百先把关键路径覆盖住比什么都强。4.4 第四步用CI让每次提交都可验证单元测试有了、构建系统标准化了下一步就是把它们放进CI流水线。嵌入式项目上CI有个历史痛点交叉编译环境不好搭、硬件板子连不上。但现代CI已经完全能解决这个问题。实际上构建固件本身不需要真实硬件只需要编译器和管理工具链。我们可以把本地用的交叉编译工具链打进一个Docker镜像CI里去拉取镜像构建。单元测试跑在主机上不碰任何硬件硬件相关的扩展测试可以通过专门的测试台架接入。GitLab CI的配置大概是这样stages: - build - test build-firmware: stage: build image: arm-gcc:latest script: - cmake -B build -DCMAKE_TOOLCHAIN_FILEtoolchain-arm-none-eabi.cmake -DCMAKE_BUILD_TYPERelease - cmake --build build -j$(nproc) artifacts: paths: - build/app.bin - build/app.elf rules: - if: $CI_PIPELINE_SOURCE merge_request_event || $CI_COMMIT_BRANCH main run-unit-tests: stage: test image: ubuntu:22.04 script: - cmake -B build-test -DBUILD_UNIT_TESTSON - cmake --build build-test - ctest --test-dir build-test --output-on-failure有了这套流水线团队每次提交代码CI都会自动构建固件、跑单元测试出问题在几分钟内就会反馈到提交者手里。这个“快速反馈”的价值怎么强调都不过分——越早发现问题修复成本越低。4.5 第五步调试与日志体系的升级代码进Git、构建标准化、测试挂上CI之后最后一个“去古法”的关键动作是改造调试体验。这个比想象中重要得多因为人是最容易犯错的环节好的调试工具和方法能大幅减少人肉排查的时间。日志系统至少要做到两个层次第一日志分级。普通开发版打印TRACE/DEBUG信息正式版只输出INFO/WARN/ERROR不能为了调试一个bug就打开全量日志把系统拖慢。第二带时间戳。我见过太多产品日志是“裸打印”没有时间戳出了问题根本没法还原事件顺序。嵌入式日志可以借助调度器的tick或者RTC/系统定时器给每条日志打上时间戳。这些信息是定位复杂问题时最宝贵的材料。调试器方面J-Link配合Ozone或者直接用GDB已经能实现零printf的调试流程设断点、看栈回溯、查局部变量、查外设寄存器。如果芯片支持SWO还可以用Trace功能把格式化日志从SWO引脚输出完全不打乱应用时序。这套组合拳打下来调试效率的提升比想象中还要大得多。我现在写代码最多加几个debug级的日志点更多的靠调试器看状态。5. 迁移过程中踩过的坑和心得5.1 构建系统迁移是最容易“真香”也最容易翻车的地方CMake迁移最顺利的项目一般是“先把构建结果弄出来再说”而不是“借机重构”。我见过一个团队边迁CMake边重构代码结果构建迁移拖着三个月没完成功能需求又来了两头着火。正确姿势是先做“翻译”把旧构建的编译参数、源文件列表、链接脚本原封不动搬到CMake里构建出完全一致的固件重构是另一个阶段的事情不要在同一时间搞两件大事。另一个坑是“编译选项不一致”。很多旧工程靠IDE的Default配置默默塞了很多编译参数一旦迁到CMake断优化、断言就全变了跑出完全不同的现象。迁完之后一定要比对编译日志确认每个源文件的编译选项和旧构建完全一致。这个步不能省我自己就吃过亏迁移CMake后固件能跑但一个浮点运算精度问题在本地不出现、在CI上复现最后排查发现是编译选项少传了一个。5.2 测试不是万能药但能挡住最蠢的回归有人觉得“测试就是写一堆用例过了就说明没问题”。这是对测试的误解。测试不可能证明没有bug它只能证明“覆盖到的逻辑没有已知bug”。嵌入式软件的bug很多来自硬件交互、时序问题、并发问题这些单靠单元测试是挡不住的。但单元测试的价值在于防回归。改了一个函数、加了一个状态、动了一条消息格式测试用例立刻告诉你“你的改动破坏了原有约定”。在项目后期这种防回归能力比新写功能的价值还大。我经手过一个项目发布前一周改了个消息重传逻辑自测没问题直接上线。结果上线后云平台侧一条消息都没收到查了两天发现是消息头的一字节填充被新逻辑吃掉了。这种问题如果有一个协议解析的单元测试分分钟就能拦住。注意嵌入式单元测试最大的坑是测试代码和硬件耦合太紧。我的经验是“能抽象就抽象”凡是要访问硬件、依赖底层的逻辑都尽量设计成接口注入的方式。测试不能陷入“为了测试而测试”的泥潭一定要先测那些纯逻辑、高价值、容易回归的模块。5.3 老代码不是非要重写但要给老代码穿上新衣服很多嵌入式项目里有大量历史遗留代码几千行的大函数、全局变量、goto、魔法数字看着就头疼。但我的观点是别急着“推倒重写”。重写成本极高而且重写出来的代码和旧代码行为可能有意想不到的差异很容易引入新问题。正确做法是“给老代码穿新衣服”也就是在不改业务逻辑的前提下把工程化基础设施搭好源码进Git、构建标准化、核心逻辑加测试、编译告警全部打开、CI跑起来。这些工作不改变代码行为但把项目从“失控”变成“可控”。等到项目大版本升级需要动架构时再逐步重写核心模块。这个渐进策略比一次性推翻重来要稳妥得多。这里要特别提一下编译告警。古法编程很多代码在编译时带着一堆warning大家都熟视无睹。但不少warning其实就是潜在bug未初始化变量、整数溢出、类型转换不匹配。把编译选项加上-Wall -Wextra -Werror是最低成本的质量提升。可能一开始有些老代码编译不过慢慢修修到一个警告都没有的状态。那时候你再回头看代码质量会发现提高的不是一点半点。5.4 团队习惯比工具更难迁移技术问题好解决人的问题才麻烦。团队里总有老工程师觉得“我这样写十年了不也没事”也有年轻工程师觉得“我学了那么多新工具你们怎么不用”。我见过很多工具落地失败的案例最终原因不是工具不行而是团队没有建立使用工具的“习惯”和“共识”。工具的迁移可以很快习惯的养成需要时间。我建议的做法是不按头强制而是先在小项目或者新模块上跑通出效果之后让团队自己感受到工具带来的便利。比如我在团队里给每个人配了一套构建测试CI的环境然后让新人用这套流程来完成一个模块开发。新人在提交MR时CI自动跑构建和测试几秒钟后通过那种“提交即验证”的流畅感比任何宣讲都有说服力。真正的去古法本质上是整个团队从“手工作坊”转向“标准化流水线”的思维转型。工具是抓手但最终改变的是团队对“质量、效率、协作”三位一体的理解。6. 常见问题速查迁移路上的经典场景症状可能原因解决方案迁CMake后固件行为异常编译选项、优化等级、宏定义迁移遗漏新旧构建日志逐项比对确认每个源文件编译参数一致单元测试编译不过被测代码包含硬件寄存器操作先用接口隔离硬件或者mock底层函数测试只编译纯逻辑模块CI上构建固件失败交叉编译器版本或路径不一致把工具链封装进Docker镜像CI使用同一镜像某个bug修完又复发没有对应回归测试每个严重bug修复时补一个最小复现用例挂进测试套件Git提交历史混乱没有分支策略和提交规范建立主分支保护开发分支合并前强制评审提交信息规范化编译只加-O0才能跑可能存在未定义行为、时序依赖打开全量告警逐模块排查数据竞争和初始化顺序问题团队不愿意用新流程工具只是手段习惯需要培养从小模块试点让团队成员亲身感受效率提升不搞一刀切7. 最后想说的关于“底层能力”的误会经常有人担心拥抱工程化会不会让嵌入式工程师荒废底层能力不再懂寄存器、不再会看原理图。这个担心我理解但我觉得这是个伪命题。我今天的日常仍然在查寄存器手册仍然要理解时钟树、电源域、总线拓扑这些底层功底永远有用。但它和价值的关系变了底层能力是内功工程化能力是招式。没有内功招式是花架子没有招式内功再深也使不出来。现代嵌入式软件开发讲究的是“用体系化的方法管理复杂性”而不是“靠个人能力生扛”。在底层能用寄存器操作解决所有问题的时候古法编程是一把趁手的武器当复杂度超出个人脑容量极限的时候还抱着古法不放就是给自己找罪受。我自己的体会是告别古法不是否定过去而是把过去那些珍贵的经验放到它们应该在的位置上。寄存器还是要读时序还是要算但代码的组织方式、验证方式、协作方式必须跟上这个时代。嵌入式软件开发依然是硬核技术活儿只是它现在需要的是“现代武器库里的硬核”而不是“作坊里的纯手工”。真到了产品出货、版本迭代、团队协作的时候你会发现这条路走起来比想象中舒服太多。