
嵌入式开发这个行当外面的人看着觉得门槛高得吓人里面的人心里都清楚真正干活用的东西跟学校里教的那套、跟招聘启事上写的那堆要求中间隔着一道巨大的鸿沟。我见过太多人简历上写着精通C语言、熟悉数据结构、了解操作系统原理结果连个串口收发都调不通连个GPIO的高低电平都测不明白。也见过不少人压根没正经学过嵌入式就是硬着头皮投了简历面试的时候连蒙带猜进去之后边干边学半年下来反而成了团队里最靠谱的那个。这个标题说的“先混进去再说”不是教你弄虚作假而是说这个行业的实际门槛远比表面看起来低真正卡住大多数人的不是技术本身而是那道心理上的坎。我自己就是半路出家。当年从别的方向转过来面试官问的问题我有一半答不上来但我会的东西恰好是他们项目里马上要用的就这么进去了。进去之后才发现组里五个人真正科班出身的就一个其他人都是各种野路子转过来的。有从硬件转的有从PC端开发转的甚至还有从测试转的。大家共同的特点就是遇到问题能查、能试、能熬而不是一开始就什么都会。所以这篇东西我想把“混进去”这件事拆开揉碎了讲清楚——到底什么叫混怎么混混进去之后怎么活下来以及那些招聘要求里没写但实际工作中天天在用的东西到底是什么。1. 招聘要求上的嵌入式跟实际干活用的嵌入式差了多少1.1 招聘启事里的“精通”到底值几个钱你随便打开一个招聘网站搜嵌入式开发看到的岗位要求基本是这么个套路精通C/C熟悉ARM体系结构掌握Linux内核驱动开发了解RTOS原理有通信协议开发经验熟悉硬件原理图阅读具备独立解决问题的能力。这一串看下来感觉没个三五年功底根本不敢投。但实际情况是大部分嵌入式岗位的日常工作跟上面这些“精通”“掌握”“熟悉”之间重合度可能连百分之三十都不到。我待过的第一个做嵌入式产品的团队主要工作内容是维护一个基于Cortex-M3的控制器固件。每天干的事情是什么改改状态机逻辑调调串口协议解析看看ADC采样值对不对偶尔用示波器抓个波形确认时序。什么Linux内核驱动、什么ARM体系结构根本用不上。用的芯片是STM32F103开发环境是Keil调试工具是J-Link就这些。招聘的时候写“熟悉ARM体系结构”实际意思是你知道Cortex-M3是ARMv7-M架构、知道它的中断向量表怎么放、知道NVIC怎么配置就够了。不需要你去研究流水线怎么排、分支预测怎么做。这不是个例。我后来跟做应用层开发的朋友聊他们那边用Qt做界面跑在Linux上招聘要求写“精通C、熟悉Qt框架、了解嵌入式Linux系统”实际工作就是拖控件、连信号槽、调调样式表。真正涉及系统底层的东西有专门的BSP团队在搞应用层的人根本碰不到。所以你看招聘要求是把整个嵌入式链条上所有岗位的技能点堆在一起写出来的你不需要全都会你只需要会其中一小块就能进去。1.2 面试官真正在筛的是什么既然技术要求没那么高那面试到底在面什么我后来自己也当过面试官回过头看筛人的核心标准其实就三条第一你能不能把一个问题说清楚哪怕这个问题你没做过第二你遇到不会的东西第一反应是查还是懵第三你之前做过的东西跟我们现在要做的有没有一点关联。第一条特别重要。很多人技术底子不错但表达一塌糊涂。问他做过什么项目他说“就做了一个温湿度采集”然后没了。你再问用的什么传感器、什么接口、数据怎么处理的、遇到什么问题他答得磕磕巴巴。这种人面试官不敢要因为嵌入式开发大量时间是在跟别人沟通——跟硬件工程师确认引脚定义跟测试确认复现步骤跟产品确认需求边界。你连自己做过的项目都讲不清楚怎么指望你跟别人协作。第二条是潜力判断。嵌入式这个领域芯片型号成千上万外设五花八门你不可能什么都做过。面试官问一个你没接触过的模块比如“你用过CAN总线吗”你没用过很正常。但如果你能说“我没用过CAN但我用过SPI和I2C我知道它们都是串行通信CAN应该是差分信号、多主架构具体寄存器配置我得查手册”这就加分了。面试官要的就是这个态度——知道怎么从已知推未知知道去哪里找答案。第三条是匹配度。有时候你技术一般但之前做的项目跟岗位需求高度重合那你就比技术更好但不匹配的人有优势。比如岗位是做电机控制的你之前正好做过PWM调速哪怕你其他方面弱一点面试官也愿意要你因为你能快速上手。1.3 那些“不会但可以学”的东西其实占了大头我统计过自己从入行到现在用过的技能真正在面试前就已经掌握的可能只有百分之二十。剩下的百分之八十都是进去之后现学的。举几个具体的例子。RTOS。我第一份工作用的是FreeRTOS之前完全没接触过。进去第一周老大扔给我一本FreeRTOS的官方手册说“把这几个API看一下任务创建、信号量、队列然后把这个采集任务改成用RTOS调度”。我就硬着头皮看看完照着例子改改完跑起来发现任务切换有问题再回去查优先级配置折腾了两天搞定了。现在你问我FreeRTOS我能讲得很清楚但当时就是边做边学。通信协议。Modbus、CANopen、自定义串口协议这些我都是工作中用到了才去了解的。Modbus还好协议简单网上资料多。CANopen就复杂一些对象字典、PDO、SDO这些概念我是对着CiA 301文档啃了好几天才搞明白的。但这些东西你让我面试前自学我肯定学不进去因为没有实际场景学了也记不住。硬件调试。看原理图、用示波器、焊板子、飞线这些技能学校里教得很少但工作中天天用。我第一次用示波器抓I2C波形的时候连触发怎么设都不知道还是硬件工程师手把手教的。后来自己调多了慢慢就熟了。现在给我一个板子我至少能判断出供电正不正常、晶振起没起振、复位信号对不对。这些东西招聘要求上可能就一句话带过但实际工作中占用的时间最多。而它们恰恰是最容易“混”进去之后再学的因为脱离了具体项目你学起来既没动力也没方向。2. “混进去”之前你至少得把这几样东西攥在手里2.1 C语言不用学到精通但指针和内存必须门儿清嵌入式开发主要用C语言这个大家都知道。但“会C语言”和“能做嵌入式”之间差的是对指针和内存的理解。你不需要把C标准委员会的文件都读一遍但下面这些东西必须清楚。指针的本质是什么它就是一个存地址的变量。嵌入式里面到处都是指针因为你要直接操作寄存器。比如你想让GPIOA的第5个引脚输出高电平你得这么写*(volatile uint32_t *)0x40020014 | (1 5);这一行代码里面0x40020014是GPIOA输出数据寄存器的地址(volatile uint32_t *)把这个地址强制转换成指向32位无符号整数的指针前面的*是取这个地址的内容|是置位操作。如果你不理解指针和地址的关系这行代码你看着就是天书。内存方面你得知道栈和堆的区别知道全局变量和局部变量存在哪里知道什么是内存对齐。嵌入式系统资源有限栈空间可能只有几KB你如果在中断服务函数里定义一个大的局部数组栈直接溢出程序跑飞。这种问题我遇到过不止一次现象是程序莫名其妙进HardFault查半天才发现是栈不够用。还有volatile关键字。很多初学者不知道它是干嘛的觉得加不加无所谓。但在嵌入式里访问硬件寄存器必须加volatile否则编译器优化的时候可能把你的读写操作优化掉。比如你写一个延时循环for(int i 0; i 1000; i);如果i没加volatile编译器可能直接把这个循环删了因为觉得你没用它。加上volatile之后编译器就知道这个变量可能被外部改变不能优化。2.2 能看懂原理图比会画PCB重要得多嵌入式软件工程师不需要会画板子但必须能看懂原理图。因为你要根据原理图确定引脚怎么配置、外设怎么连接、通信接口用哪个。看不懂原理图你就不知道你的代码该操作哪个寄存器。看原理图其实不难核心就是搞清楚几件事芯片的供电引脚接在哪里、晶振接在哪个引脚、复位电路怎么设计的、你要用的外设比如LED、按键、传感器接在哪个GPIO上、通信接口UART、I2C、SPI的引脚是怎么分配的。我刚开始看原理图的时候最头疼的是网络标号。一张图上密密麻麻的标号不知道哪个连哪个。后来学会了用PDF阅读器的搜索功能搜一个标号所有出现的地方都高亮出来顺着看就能理清连接关系。还有一个技巧是看芯片的数据手册里面有个引脚定义表对照着原理图看很快就知道每个引脚是干嘛的。提示看原理图的时候先找芯片的电源和地确认供电正常再找晶振和复位确认系统能跑起来最后找你关心的外设。这个顺序能帮你快速定位问题。2.3 会用万用表和示波器调试效率翻倍万用表和示波器是嵌入式调试的两把利器。万用表主要用来测通断、测电压。比如你怀疑某个引脚没输出拿万用表一量电压不对那就说明要么配置错了要么硬件有问题。示波器主要用来看波形比如UART的TX线有没有数据出来、I2C的时钟和数据线时序对不对、PWM的占空比是不是你设的值。我第一次用示波器的时候连探头怎么校准都不知道。后来硬件同事教我先把探头接到示波器自带的方波输出上调补偿电容让方波看起来是平的这才算校准好。然后测信号的时候触发模式选边沿触发触发电平调到信号幅度的中间这样波形才能稳定显示。还有一个坑是地线。示波器的探头有信号夹和地夹地夹必须接到被测电路的地上否则测出来的波形全是噪声。我见过有人只夹信号不接地然后抱怨波形不对折腾半天才发现是地没接。2.4 至少玩过一款主流芯片的开发板面试的时候你说你学过嵌入式面试官肯定会问你用过什么芯片。如果你说“我学过51单片机”那基本就凉了因为现在工业界很少用51了。你至少得玩过一款主流的ARM Cortex-M芯片比如STM32。STM32的好处是资料多、社区活跃、工具链成熟。你买个最小系统板照着教程把GPIO、UART、定时器、ADC、I2C、SPI这些外设都跑一遍基本上就对嵌入式开发有个概念了。跑一遍的意思不是把例程下载进去看现象而是自己写代码配置寄存器处理中断解决问题。比如点灯你不要用库函数直接操作寄存器试试。先使能GPIO时钟再配置引脚为推挽输出然后写ODR寄存器控制高低电平。这一套下来你对时钟树、寄存器操作、位操作的理解会深刻很多。之后再学库函数或者HAL库就知道它们底层在干什么。如果你时间充裕还可以玩玩RTOS。在STM32上跑个FreeRTOS创建两个任务一个闪灯一个串口打印看看任务调度是怎么回事。这个经历写在简历上比“熟悉C语言”有说服力得多。3. 简历和面试里的“混”是有技术含量的3.1 简历怎么写才不像培训班出来的培训班出来的简历有个特点项目经历写得特别全什么智能家居、物联网网关、四轴飞行器每个项目都用了十几种技术但一问细节就露馅。面试官看多了这种简历一眼就能认出来。所以你的简历要反着来——项目不用多一两个就行但每个项目都要能讲出细节。什么叫细节比如你写“基于STM32的温湿度采集系统”这太笼统了。你可以写“用STM32F103的ADC采集热敏电阻分压通过查表法转换成温度值用UART以自定义协议每秒钟上报一次数据协议包含帧头、长度、数据、校验和”。这样写面试官就知道你真的做过因为查表法、自定义协议、校验和这些细节没做过的人是编不出来的。还有简历上不要写“精通”写“熟悉”或者“了解”就行。你写精通C语言面试官问你函数指针和指针函数的区别你答不上来那就尴尬了。你写熟悉C语言面试官问的问题会相对基础一些你答上来了反而超出预期。项目经验少也没关系你可以把课程设计、自学的小项目、甚至帮别人调过的一个小bug写上去。关键是你要能说清楚你做了什么、遇到什么问题、怎么解决的。面试官不在乎项目大小在乎你是不是真的动手了。3.2 面试被问到不会的问题怎么接话面试嵌入式岗位遇到不会的问题太正常了。芯片型号那么多外设那么杂谁也不可能全做过。关键是你怎么接话。错误示范面试官问“你用过CAN总线吗”你答“没用过。”然后就没下文了。这样面试官会觉得你沟通能力不行或者学习意愿不强。正确示范你答“CAN总线我没实际用过但我了解它是差分信号、多主架构、带仲裁机制。我之前用过RS485也是差分信号但RS485是单主多从没有仲裁。如果项目需要我可以很快上手因为通信协议这块我有基础。”这样回答既诚实又展示了你的知识迁移能力还表达了学习意愿。还有一种情况是问题本身有坑。比如面试官问“volatile关键字的作用是什么”你如果只答“防止编译器优化”那只能算及格。你可以补充“它告诉编译器这个变量可能被外部改变每次访问都要从内存读取不能缓存在寄存器里。在嵌入式里访问硬件寄存器、中断里修改的全局变量、多任务共享的变量都要加volatile。”这样答面试官就知道你不仅知道概念还知道应用场景。3.3 谈薪资的时候别被“嵌入式工资低”吓住网上总有人说嵌入式工资低不如互联网。这话对也不对。纯做单片机开发的工资确实不高因为门槛相对低竞争也激烈。但嵌入式范围很广你做Linux驱动、做音视频编解码、做汽车电子工资一点都不低。对于刚入行的人来说第一份工作的工资不是最重要的重要的是能不能接触到核心的东西。你进一个做产品的公司能从头到尾跟一个项目从选型到量产这个经验比多拿两千块钱值钱得多。我第一份工作工资很低但那个团队让我独立负责了一个小项目的固件开发从原理图评审到代码编写到产线测试全流程走了一遍。这个经历让我在后面跳槽的时候底气足了很多。谈薪资的时候你可以先问清楚工作内容。如果岗位是做维护、做边角料那工资高也不要去因为学不到东西。如果岗位能让你参与完整的产品开发工资低一点也可以接受就当交学费了。4. 进去之后的前三个月决定你能不能留下来4.1 第一周把开发环境跑通把代码下载进去新人入职第一周最重要的不是看懂多少代码而是把开发环境搭起来把现有的代码编译通过下载到板子上能看到现象。这个过程中你会遇到各种问题编译器版本不对、库文件缺失、下载器驱动没装、板子供电不对。这些问题看起来是小事但如果你一周都搞不定老大就会怀疑你的能力。我的经验是遇到环境问题先看文档再看同事的电脑是怎么配的最后再问人。问人的时候不要问“为什么编译不过”而要问“我编译的时候报了这个错我试了A和B两种方法都不行你觉得可能是什么原因”。这样别人知道你动过脑子了也更愿意帮你。还有一点把遇到的问题和解决方法记下来。我有个习惯每解决一个问题就在笔记里记一条现象是什么、原因是什么、怎么解决的。三个月下来笔记里攒了几十条后面再遇到类似问题翻笔记就行不用再问人。4.2 第一个月从小bug改起建立信任第一个月老大一般不会让你碰核心代码而是让你改一些小bug或者加一些小功能。这时候不要嫌活小要把每个小活都干漂亮。比如让你改一个LED闪烁的频率你不仅要把频率改了还要看看原来的代码有没有问题能不能优化一下。你改完提交代码的时候写清楚改了什么、为什么这么改、测试结果是什么。这样老大看你的代码提交记录就知道你靠谱。我见过一个新人让他改一个串口打印的格式他改完之后顺手把打印函数的缓冲区溢出问题也修了。虽然那个问题不是他负责的但他发现了就修了还在提交说明里写清楚了。老大看到之后第二个月就让他参与核心模块的开发了。4.3 第三个月能独立负责一个模块就算站稳了三个月是个坎。如果你三个月后还不能独立负责一个模块还需要别人手把手教那可能就危险了。独立负责的意思是给你一个需求你能自己拆解成任务自己设计实现方案自己编码调试遇到问题自己先想办法解决实在解决不了再求助。这个能力不是天生的是练出来的。我的方法是接到任务后先别急着写代码先想清楚几件事——这个模块的输入输出是什么、跟其他模块怎么交互、用什么数据结构、有没有现成的代码可以参考、可能遇到什么问题。想清楚了再动手比上来就写要快得多。还有学会看数据手册和参考手册。嵌入式的很多问题答案都在手册里。比如你配置一个外设不工作先查手册看时钟使能了没有、引脚复用配置对了没有、寄存器位设对了没有。手册看多了你就知道哪些地方容易出错调试的时候直接往那些地方查。5. 那些招聘要求上不写但天天在用的东西5.1 版本控制Git不是可选项是必选项学校里做项目代码都是自己一个人写最多用U盘拷来拷去。但工作中代码是团队协作的必须用版本控制。现在主流的是Git你至少得会这几个操作clone、pull、commit、push、branch、merge。不用学到能处理复杂冲突的程度但基本的流程要会。我见过新人把代码改乱了想回退到之前的版本结果不会用Git只能凭记忆手动改回去改了半天还改错了。如果你会用Git一条git checkout就搞定了。还有一个习惯每次提交代码之前先git diff看一下自己改了什么确认没有误改的地方。提交说明写清楚不要写“修改bug”要写“修复串口接收缓冲区溢出问题当接收数据超过缓冲区大小时丢弃多余数据”。这样别人看提交记录就知道你干了什么。5.2 调试手段printf不是万能的但没它万万不能嵌入式调试最常用的手段就是打印。串口打印、RTT打印、SWO打印至少得会一种。串口打印最简单但占用一个UART外设而且速度慢。RTT是Segger的工具速度快不占外设但需要J-Link调试器。SWO是ARM的调试接口速度也快但配置稍微复杂一点。打印的时候要注意不要在中断里打印太多东西因为打印本身很耗时可能导致中断响应不及时。也不要在高频调用的函数里打印否则串口输出会成为瓶颈。我一般的做法是在关键路径上打标记比如进入某个状态打一个字符退出打另一个字符这样既能跟踪流程又不会太影响性能。除了打印还要学会用调试器。单步执行、断点、查看变量、查看寄存器这些基本操作要熟练。有时候打印看不出来的问题用调试器一看寄存器就知道了。比如串口收不到数据你打个断点看状态寄存器的RXNE位有没有置起来就知道是硬件没收到还是软件没读。5.3 阅读英文手册逃避不了的基本功嵌入式的芯片手册、协议文档、应用笔记大部分是英文的。你可以英语不好但你必须能借助翻译工具看懂技术文档。这不是能力问题是态度问题。我刚开始看英文手册的时候一个词一个词查看一页要半小时。后来看多了常见的术语就那些register、bit、enable、disable、interrupt、clock、reset、configure。看多了就快了。而且技术文档的句式很固定看多了基本能猜出意思。提示看英文手册的时候先看目录和框图了解整体结构再看寄存器描述和时序图了解具体细节最后看例程和注意事项。不要从第一页开始逐字读那样效率太低。5.4 硬件协作跟硬件工程师沟通的正确姿势嵌入式软件工程师跟硬件工程师的协作非常频繁。你发现一个引脚输出不对可能是软件配置问题也可能是硬件连接问题。这时候你需要跟硬件工程师一起排查。沟通的时候不要说“你的板子有问题”而要说“我测了这个引脚软件配置是推挽输出高电平但实际测出来是低电平你帮我看看硬件上有没有什么影响”。这样对方更容易接受也更愿意配合你排查。还有提问题的时候尽量提供完整的信息芯片型号、引脚编号、软件配置、实测结果、你试过哪些方法。信息越全硬件工程师越容易帮你定位问题。我见过有人只说“串口不通”然后硬件工程师问半天才知道是哪个串口、哪个引脚、什么现象效率极低。6. 从“混进去”到“站稳脚”心态比技术更重要6.1 接受自己一开始什么都不会很多人不敢投嵌入式岗位是因为觉得自己什么都不会。但你要知道没有人一开始什么都会。那些看起来游刃有余的老手也是从什么都不会过来的。区别在于他们接受了自己的无知然后一点点去学。我入职第一周连公司的代码仓库怎么克隆都不知道问同事的时候脸都红了。但问完之后我就知道了下次就不用问了。三个月后新来的同事问我同样的问题我告诉他怎么操作他也很感激。这个过程很正常不要因为不会就觉得自己不行。6.2 遇到问题先自己查但不要死磕嵌入式开发遇到的问题大部分都能在网上找到答案。芯片厂商的官方论坛、Stack Overflow、各种技术博客都是很好的资源。遇到问题先搜一下很可能别人已经遇到过了。但也不要死磕。如果你查了两个小时还没解决那就该问人了。问人的时候把你查到的信息、试过的方法、现在的现象都说清楚这样别人帮你的时候也有方向。最怕的是那种闷头搞了一天最后发现是一个很低级的错误白白浪费时间。6.3 保持学习但不要盲目追新嵌入式技术更新不算快但也在变。新的芯片、新的协议、新的工具链时不时就冒出来。你不需要每个都追但你要保持学习的状态。比如RISC-V现在很火你可以了解一下它的基本概念但不一定要马上转过去。等工作中真的用到了再深入学也不迟。更重要的是把基础打牢。C语言、数据结构、操作系统原理、计算机组成原理这些基础的东西越扎实越好。因为不管技术怎么变底层的东西是不变的。你基础好学新东西就快基础不好学什么都费劲。6.4 找到自己的方向不要什么都想做嵌入式范围很广有做单片机的、做Linux的、做驱动的、做应用的、做算法的。你不可能什么都精通也没必要什么都做。工作两三年后你应该找到自己感兴趣的方向然后深耕下去。比如你对底层感兴趣可以往驱动开发方向走研究内核机制、设备模型、电源管理。如果你对应用感兴趣可以往应用层开发走研究框架设计、性能优化、跨平台适配。如果你对硬件也感兴趣可以往系统架构方向走做软硬件协同设计。方向没有好坏只有适合不适合。关键是你要在一个方向上积累足够的深度这样才有竞争力。我到现在还记得第一次独立解决一个死机问题的场景。那是一个跑在Cortex-M0上的小系统偶尔会死机概率很低一天可能就一两次。我花了三天时间用打印和调试器一点点缩小范围最后定位到一个中断服务函数里访问了未对齐的地址。M0不支持非对齐访问一访问就HardFault。改掉之后再也没死过机。那个瞬间的成就感比拿多少工资都强。嵌入式这个行当门槛确实有但没你想的那么高。先进去再慢慢学你会发现那些看起来吓人的东西拆开了看也就那么回事。