十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

干了多年嵌入式,最后悔的几件事:写给新手的避坑指南

干了多年嵌入式,最后悔的几件事:写给新手的避坑指南 干了这么多年嵌入式我最后悔的几件事说实话干嵌入式这行越久越觉得“后悔”是个挺有分量的词。我从裸机单片机做起一路折腾过STM32、嵌入式Linux、各种协议栈到现在回头看真正让我睡不着觉的不是哪段代码没写好而是那些本该早几年想清楚的事。这篇东西不是技术教程是我自己给自己做的复盘也是给刚摸到嵌入式门槛的朋友一份避坑参考。你要是正在学嵌入式或者已经在写单片机、搞驱动、调内核应该能从我这些后悔里看到自己现在或未来的影子。很多人以为嵌入式就是写写C语言、点几个LED、调个串口其实这个领域的坑全在地底下。我最后悔的几件事大多不是某个具体知识没学会而是学习方式、工程习惯、行业认知上跑偏了。下面一条条说每一条都会告诉你我怎么踩进去的以及现在让我重来会怎么走。1. 最后悔没把C语言和数据结构砸实工作前三年全靠“脸皮厚”硬撑上学那会儿我觉得自己C语言学得还行指针懂了结构体会用了考试也能过。可真到公司写产品代码第一周就被一个函数指针数组直接干懵。后来我才明白学校里那点C语言只能叫“认识”嵌入式要的是“深入骨髓”的C。单片机跑起来内存就几十K到几百K堆和栈挤在一起一个指针写飞了整个系统就死给你看。1.1 指针、内存、结构体才是嵌入式C的命根子我最后悔的事之一就是前几年写代码全凭感觉从没系统把指针和内存这块打通。嵌入式里大量核心机制本质上都是“在固定内存地址上做文章”寄存器操作就是把外设寄存器映射成指针然后读写这个地址。链表、队列、环形缓冲区全是结构体加指针在跳舞。状态机里的事件表多半是函数指针数组。回调机制不管是定时器回调还是中断回调也都是函数指针。这些基础不牢你后面看任何内核源码、驱动代码都像看天书。我记得第一次啃嵌入式Linux的驱动源码里面全是container_of、list_head这类宏和结构体我当时连offsetof都说不明白硬是翻了两天资料才缓过来。你要是现在还在“会用指针但不敢写指针”的阶段趁早花钱花时间把它啃透这比多会一块开发板值钱一百倍。1.2 面试八股文不是背出来的是本来就该会的网上流传大量“嵌入式八股文”C语言部分的题翻来覆去就是volatile、const、static、内存对齐、大小端、栈和堆。我以前特别烦这些觉得是死记硬背。干了好几年才发现这些题恰恰是嵌入式开发的日常。举个例子volatile。裸机开发里中断里改标志位主循环里判断不加volatile编译器优化一开标志位判断可能永远走不进去。我实际排查过一个bug就是同事的flag变量被编译器优化后主循环读到的永远是寄存器里的缓存值加了volatile秒好。再比如内存对齐结构体字段顺序没排好一个结构体大了好几个字节在RAM紧张的MCU上这就是实打实的成本。做FFT频谱分析这类计算密集型的项目结构体对齐、cache line这些东西直接决定性能。所以别把八股文当考试工具把它当基本功清单。链表会不会手写环形缓冲区能不能十分钟写出来状态机的表驱动方式能不能讲清楚这些才是嵌入式C语言真正的分水岭。2. 后悔只会跑开发板demo不会深挖芯片手册和内核源码嵌入式这行有个“快乐陷阱”就是开发板例程太好跑了。我早年买过好几块板子USB插上例程一烧LED闪了串口打印了感觉今天又学会了。可这种“会”是假象因为例程是别人写好的你只是点了个编译和下载。等到了实际项目里芯片换一颗、外设不一样、时序要求变严你立刻原形毕露。2.1 不读芯片手册的工程师永远只配当“代码搬运工”以STM32F4为例网上串口配置的教程一抓一大把。可很多人的“会串口”就是照着别人的代码改引脚、改波特率跑通了就完事。真问你USART的波特率寄存器怎么算出来的DMA的循环模式在什么情况下会丢数据FIFO和直接寄存器收发有什么区别很多人答不上来。我后来被项目逼着看了两遍《STM32F4参考手册》里串口和DMA的章节才算真正敢说自己会配串口。其实芯片手册没你想的那么难读关键是先找目录找到你要用的外设先看不寄存器先看“功能描述”和“框图”搞清楚数据流从哪来到哪去再对照寄存器去配置。这套方法论在哪个芯片上都通用。内核源码也是同理。学嵌入式Linux光会ifconfig、insmod、mount这些命令只是最表层。设备树怎么写一个GPIO驱动从probe到read/write的完整流程是什么中断下半部为什么要用tasklet或work queue这些答案全在内核源码里。我现在最后悔的就是前几年一直混在应用层调程序迟迟没鼓起勇气去翻drivers/和kernel/目录下的代码。等你真的读进去你会觉得以前那些“神秘”的驱动问题其实全都有迹可循。2.2 别当“收藏家”选一块平台往死里吃透现在网上的信息太丰富了嵌入式开源项目一堆教程漫山遍野很多人今天看中一个AXU15EGP的开发板明天想玩国产FPGA后天又想搞RISC-V结果买回来全吃灰。我踩过这个坑看到什么热就学什么最后啥都只懂个皮。正确的做法是选一个主流平台——比如STM32F4或imx6ull把它从裸机到RTOS再到Linux这条路完整走一遍。同一颗芯片你手写过启动代码配置过时钟树移植过轻量级协议栈再在它上面跑Linux和驱动你对“嵌入式”这三个字的理解会完全不一样。平台只是载体学习底层规律才是目的。后面换任何芯片你看到的是寄存器、总线、时钟、中断、外设这些共性东西而不是那块开发板叫什么名字。3. 后悔不写文档、代码没规范结果自己坑了自己这个后悔特别真实。早年间我写代码那叫一个随性变量名用a、b、tmp注释基本没有一个函数几百行全局变量满天飞。当时觉得自己“快”后来才知道那是给自己埋雷。3.1 代码是写给人看的不是写给编译器看的嵌入式项目常有这种情况半年后需求变更你打开自己半年前写的代码满屏的int x和flag2你根本想不起来当时的逻辑。我记得到后来接手维护的一个环境监控项目里有个串口解析函数我写了两百多行没有分段没有注释全是用if嵌套起来的。客户说通信协议要加一条我改了两天都没敢动最后只能重写。从那时候起我才真正相信“代码可读性就是生产力”这句话。现在我给自己定的规矩很简单变量函数命名必须能看懂绝不省这几个字母。一个函数尽量控制在50行以内超过就拆。关键逻辑必须有注释注释写“为什么这么做”而不是重复代码。提交到Git仓库的代码必须过一遍自己的代码审查像给别人投稿一样认真。3.2 嵌入式升级不是“把程序烧进去”那么简单这几年不少项目上要做OTA升级、远程固件更新我才发现升级方案是个大坑。没有签名机制升级包被篡改设备就可能变砖没有版本回滚策略一次升级失败整个现场就瘫痪升级中断电bootloader如果没做备份区设备就成了板砖。我现在做嵌入式升级至少会考虑这些升级包要有校验和、签名保证完整性和合法性。采用A/B分区方案升级失败自动回滚到上一个可用版本。升级流程里要有“升级状态记录”Bootloader启动时先检查状态再决定启动哪个分区。升级日志必须写清楚版本号、时间、结果出了问题能回溯。这些工程化的东西培训班一般不讲开发板例程里也没有但真实项目里几乎天天遇到。我特别后悔没早点接触这类“工程味”十足的内容导致我当年第一次独立负责OTA项目时天天被测试和产品追着改签名方案和升级策略。4. 后悔看不起硬件结果被电源、时序和示波器教做人嵌入式工程师有个通病就是容易只把自己当“软件工程师”觉得硬件是硬件工程师的事。我前几年就是这个心态写代码时完全不管外设的电气特性结果一到联调就露怯。设备莫名其妙重启串口打印乱码波形不对我只会怀疑程序有bug最后被硬件同事用示波器两分钟定位到问题——是电源纹波太大。4.1 串口配置人人会但电平标准你搞清了吗就拿串口来说很多人写串口驱动只要波特率对、收发对就行但实际项目里TTL、RS232、RS485电平不一样接线方式不一样通信距离和应用场景也完全不同。TTL电平适合板内短距离RS232虽然现在用得少但很多老工业设备还在用它RS485则要靠A/B差分信号做长距离组网还要考虑终端电阻匹配。真实项目里如果你的串口收乱码不一定是你软件改错很可能是接线错、共地没做好、波特率误差超了或者信号线太长干扰大。这些靠看代码看不出来必须用万用表和示波器来量。我后悔自己早期太晚学会用示波器直到一次调试一个基于STM32F4的FFT频谱分析项目时发现ADC采到的数据始终有周期性毛刺查了三天代码都没结果后来才发现是电源上叠加了一个开关噪声去耦电容布局还不到位。这就是典型的“软件跑得没问题硬件没给力”。4.2 电源不只是“供电”它决定整个系统的稳定性嵌入式系统里电源设计简直能决定项目生死。比如很多板子上用LDO芯片像常见的CT1117系列输入输出压差、最大电流、散热这些参数都是有讲究的。你给一颗MCU供电不加去耦电容或者104和10uF放的位置不对高速翻转时电压就可能瞬间跌落导致复位或死机。我后来养成的习惯是硬件原理图送评审的时候别再当甩手掌柜主动去看电源部分。芯片供电引脚旁边有没有足够容量的电容数字地和模拟地有没有处理好晶振的负载电容取值合不合理哪怕你不会画PCB这些基本常识也能帮你避免大量“软件背锅”的情况。嵌入式工程师懂点硬件不是抢别人饭碗是自己救自己。5. 后悔没有早点泡在开源项目里闭门造车浪费了好几年我刚开始搞嵌入式的时候很喜欢自己写代码感觉自己从头写一个协议栈特别有成就感。现在回头看那叫“造轮子”而且是造得最破的那种轮子。自己写的TCP/IP协议栈一堆bug不如直接用lwIP自己写的文件系统脆得一碰就崩不如研究一下LittleFS或FatFS怎么用。真正的高手不是啥都自己写而是极其擅于站在开源巨人肩膀上解决问题。5.1 优秀的开源项目是最好的学习教材嵌入式领域有大量优质开源项目覆盖了你想得到的绝大部分场景。比如lwIP嵌入式TCP/IP协议栈你做物联网、环境监控、远程数据采集基本绕不开它。FreeRTOS / RT-Thread实时操作系统理解任务调度、信号量、消息队列的绝佳教材。FlashDB、LittleFS嵌入式文件系统和键值存储项目里做参数存储、日志记录很实用。PureData、TensorFlow Lite Micro 这类往嵌入式AI方向走的更适合有经验的人深入。我以前总觉得开源项目代码太多看不懂就一直躲着。后来逼着自己先跑通一个最小系统再跟着阅读关键文件的代码配合调试器单步看慢慢才啃下来。当你能看懂lwIP里一个网络接口的收发流程或者能理解RTOS内核里任务切换时保存上下文的代码那种“原来如此”的爽感是任何开发板demo都给不了的。5.2 找项目练手别再做个“流水灯”就发简历网上有很多“嵌入式项目开发实例”但质量参差不齐。我最后悔的是早期做的“项目”全是demo级别的按键点灯、串口打印、数码管显示。这些东西练手可以写简历没意义。真实项目至少要包含完整的功能链路和工程问题做一个环境监控系统就至少包含传感器数据采集、滤波处理、协议打包、网络传输、上位机显示、异常告警。做一个FFT频谱分析仪器就要处理ADC采样率设计、窗函数选择、FFT点数与频率分辨率的关系、结果在LCD上的实时刷新。做一个SNMP嵌入式移植就要理解SNMP协议如何映射到嵌入式设备怎么实现MIB节点怎么在资源受限条件下完成网络通信。把这些项目完整做下来你会碰到的坑基本和工业项目一致内存不够、时序不对、通信不稳定、数据丢包、掉线重连。这个过程中学到的东西比你看十个教程都值。你现在如果还在找项目别抓着一个代码库就死磕先画一个功能框图把数据流走通再一个一个模块推进最后再整体联调这才是真正的项目能力。6. 后悔只钻技术不认行业不懂业务在嵌入式里一样吃亏嵌入式不是一门独立技术它是为某个行业服务的。同样写C代码做智能家居的和做汽车电子的方法论完全不一样。我以前只沉迷于“把代码写得漂亮”对业务场景一问三不知后来才意识到不懂行业你连需求都评审不了方案设计更是无从下手。6.1 汽车电子嵌入式为什么值得多花时间了解这几年汽车电子嵌入式岗位很热不是没道理。车载环境对安全性和实时性要求极高CAN/LIN通信、AUTOSAR架构、功能安全标准、诊断协议这些都有成熟的体系。我虽然没有深耕汽车电子但接触过相关项目后最大的感受是这个领域更需要“工程规范和流程意识”不是代码能跑就算完你得考虑失效模式、冗余设计、软硬件协同。而且汽车电子的经验是可以复利的。你今天搞懂一个CAN节点怎么设计明天换一个域控制器平台理解底层逻辑后上手也会很快。行业知识越早积累你的职业道路越宽别像我一样等到工作好几年了才开始补这些。6.2 嵌入式测试和产品化意识越早建立越好我早期有个毛病把代码写完了就急着交给测试心里想的是“我这边编译通过了功能也差不多了”。实际上真正的嵌入式开发测试是极其重要的一环。嵌入式测试不只是跑跑用例还包括边界条件测试、异常恢复测试、长时间稳定性测试。比如一个串口通信协议你有没有测过数据包半包、粘包、超长包一个OTA升级流程测过断点续传、断电恢复、版本回滚吗我特别记得一次环境监控项目连续跑了一周都没问题结果一到夏天设备在高温房一待就重启。排查到最后是热噪声导致某颗芯片工作异常软件没做相应的看门狗恢复机制。如果我一早就把“异常场景测试”纳入开发流程这种问题能早得多暴露。嵌入式产品最终要出货、要过认证、要在恶劣环境里干活你软件的鲁棒性全靠这些测试补出来。7. 后悔没有一套自己的调试方法学排除bug全靠“加打印”新手和老手之间最大的区别之一是排查问题的思路。我早期排除bug基本就是printf到处打、串口助手一直看改一个地方编译一次烧一次固件祈祷它能好。结果一个bug能搞好几天到最后都不知道是改好的还是蒙好的。7.1 让调试变成有章可循的系统工程后来我慢慢建立起一套调试方法几个关键步骤非常有效先复现再定位。不能稳定复现的问题不要瞎改代码。尽量用一个最小测试用例把问题触发出来。利用日志分级。把日志分成错误、警告、信息、调试几个级别日常跑的时候只开前几级排查问题的时候再把调试信息开出来避免刷屏。合理用断言。嵌入式里在关键位置加断言能尽早发现问题。比如断言一个指针不为空断言一个状态机状态合法。用好调试器。学会看调用栈、变量窗口、反汇编比你打印一百行日志都管用。很多疑难杂症比如栈溢出、指针越界直接看栈帧就能快速锁定。我现在做嵌入式Linux调试还会搭配ftrace、perf这些内核工具甚至用JTAG调试器追到底层汇编去。掌握了这套方法后即使遇到一个完全陌生的系统我心里也有底只要能把问题稳定复现再一步步缩小范围没有解不了的bug。7.2 嵌入式测试和自动化越早铺路越省心很多嵌入式项目还停留在“靠人工点界面验证”的阶段。我后悔的是前几年没把自动化测试当回事导致后期每次改需求都要手动回归全流程点得想吐。现在但凡时间允许我都会给工程加上单元测试框架关键算法模块用上位机跑纯软件测试。比如ADC采集滤波算法可以先在PC上模拟数据流验证滤波效果再下到板上调真实传感器。这就是“硬件在环”思想的简化版本能极大减少板端调试成本。嵌入式Linux环境下的自动化可以用Docker搭交叉编译环境把编译、静态检查、单元测试集成到CI流程里。这样每次提交代码系统自动帮你编译一遍、跑一遍基础测试很多低级错误在提交阶段就被拦截了根本不会带到板子上。这个习惯我建立得晚但一旦成立整个项目的开发效率提升非常明显。8. 写给新人的几句实在话学习路线、面试和心态文章写到最后我想把一些更宏观的后悔也摊开说。主要是对着那些正在纠结“嵌入式学习路线”的年轻人讲讲什么值得做什么不值得。8.1 学习路线其实很清晰难在坚持我在网上看大家的提问最常见的是“嵌入式该怎么学”。说实话路线真的不是秘密难在你能不能沉下心走完。我复盘下来比较靠谱的路径是先扎实C语言和数据结构然后学单片机比如STM32裸机开发熟练之后上RTOS再往后切入嵌入式Linux掌握交叉编译、系统移植、驱动开发最后根据自己的方向深入到内核或应用。每一个阶段都需要配套的实战项目而且要你亲手写代码、亲手调bug只看视频没有任何意义。中途你会遇到无数坑。环境搭不起来、内核编译报错、驱动一加载就崩溃、串口怎么都不通。这些都是学习过程的一部分不是你在浪费时间。我最后悔的就是早期一到坑就换方向换了五六个方向结果每个都在入门处徘徊。8.2 对面试和“八股文”心态放平很多同学一搜“嵌入式面试八股文”就头大。其实面试官问那些基础题不是故意刁难你而是这些基础直接反映了你的代码功底。你连static关键字都说不完整我怎么放心让你去维护一段裸机起飞的代码你连指针和数组的区别都讲不透我怎么能指望你读懂驱动源码所以准备面试不用焦虑就按基础题、项目题、场景题三类准备。基础题靠平时积累项目题靠真实经历场景题考你的工程思维。面试前多看看面经把高频考点过一遍更重要的是把自己做过的项目从头到尾梳理清楚为什么这样做数据流怎么走出了什么问题怎么解决这才是真正面试官想听的。8.3 工具趁手不贪多够用就行工具上我也有过浪费时间的经历。今天想用VSCode明天试Qt后天折腾各种插件最后发现买椟还珠。我的建议是嵌入式开发现在VSCode加几个常用插件就够用了比如EIDE、C/C插件、Remote-SSH配合嵌入式Linux环境就能写代码、编译、调试。做上位机如果需要可视化界面Qt在嵌入式里确实常用尤其是做设备调试工具、数据展示面板Qt的跨平台和信号槽机制非常好用。但工具终究是服务于项目的别在“选编辑器”上消耗太多意志力。我个人的体会是嵌入式是一个需要笨功夫的行业。那些最后走得远的往往不是最聪明的人而是愿意把C语言基础抠到极致、把芯片手册一页页啃完、把每次调试心得一次次沉淀下来的人。我踩过的坑说到底都是因为当年的自己太想“快”反而走了最远的路。希望看到这篇文章的你能少摔几个跟头早几年把地基打稳。
返回列表