昂瑞微嵌入式面试(10.9)
- 时间
- 2026-10
- 来源
- 牛客网
《面试题目》
- 自我介绍。
- 简历上三个项目(逐个拷打)。
- 任务优先级怎么划分,根据什么?
- 主要负责什么?
- 架构怎么设计?
- 任务栈大小怎么分配的?
- 任务中互斥锁和信号量用在哪里?
- 说一下 IIC 的工作流程。
- 有遇到过 IIC 异常吗,怎么解决的?
- SPI 的流程。
- IIC 和 SPI 的区别。
- 串口和上面两个的区别。
- 算法怎么设计的?
- STM32 启动流程。
- .data 中存什么、.bss 中存什么?
- 静态变量存在哪里?
- 任务切换流程。
- 开发过程中会用到 AI 吗?
- 有没有遇到过死机?举例说一下遇到的 HardFault,这个 HardFault 属于哪一种?
《参考解析》
任务优先级与栈大小怎么定:优先级划分的依据是「实时性要求 + 阻塞特性」,而不是「哪个任务重要」。常用做法是速率单调:周期越短、截止时间越紧的任务优先级越高,并保证高优先级任务能被及时抢占;同时要把「事件驱动且必须快速响应」的任务(通信收包、故障处理)放到高位,把周期长、计算量大的任务(数据处理、上报、日志落盘)放到低位。要注意优先级反转:低优先级任务持锁时会挡住高优先级任务,解决办法是互斥量的优先级继承,或者干脆避免高低优先级共享同一把锁。栈大小靠实测而非拍脑袋:先把每个任务栈填成魔术字(如 0xAAAAAAAA),跑满各种场景后统计未被打扰的长度算出水位,再留 30%~50% 余量;额外考虑中断嵌套、函数调用深度、printf/浮点格式化这类吃栈大户,并在栈底放 canary 或配合 MPU 做越界保护。
互斥锁与信号量的使用位置:两者的语义不同,别混用。互斥量用于保护共享资源、有所有权概念、支持优先级继承,典型位置是:多个任务访问同一个 IIC/SPI 总线或同一个外设寄存器组、多个任务读写同一份配置或缓冲区、任务与中断之外的保护场景。信号量用于同步与事件通知,分为二值与计数两类:中断里 give、任务里 take,用来把「数据到了」这件事交给任务处理(串口收包、DMA 完成、GPIO 中断);计数信号量还能用「事件计数」或「资源池计数」的方式管理缓冲块数量,也常与队列搭配做生产者消费者限流。答题时要补两条工程细节:中断里只能用 FromISR 版本,且不能在中断里 take 阻塞等待;临界区要尽量短,绝不能拿着互斥量做耗时操作或再获取第二把锁,否则就是死锁温床。
IIC / SPI / 串口三条总线的流程与区别:IIC 的流程按信号展开:空闲时 SCL、SDA 都为高;起始条件是 SCL 高时 SDA 由高变低;然后逐位发送 7 位地址加读写位,从机在第 9 个时钟拉低 SDA 回应 ACK;接着按字节收发数据,每字节后都有 ACK;结束时 SCL 高时 SDA 由低变高形成停止条件。异常排查的常见顺序是:先看波形(用逻辑分析仪抓 SCL/SDA,确认时序、上拉、电平是否正确),再核对地址(7 位与 8 位写法差一位)、速率与时钟延展,再查总线是否被从机拉死(SDA 一直低需要发 9 个时钟解锁或断电重启),最后确认软件层的重试、超时与总线恢复逻辑。SPI 的流程是拉低片选、按模式(CPOL/CPHA 决定时钟极性与采样边沿)在每个时钟沿移出一位、收完后拉高片选。区别上:SPI 全双工、四线、高速、无地址无应答,适合 flash、屏幕这类高速外设;IIC 两线、半双工、有地址与应答、速率低但引脚省、可挂多从机;串口是异步点对点,无时钟线,靠波特率同步,结构最简单但只适合两个设备之间。
STM32 启动流程与变量存放:从上电到 main 的主线是:复位释放后 CPU 从向量表取初始栈指针与复位向量,执行启动文件里的复位处理程序;接着设置时钟(把系统时钟切到 HSE/PLL),使能必要的外设与内存;然后把 .data 段从 Flash 拷贝到 RAM(已初始化的全局/静态变量)、把 .bss 段清零(未初始化或初始化为 0 的全局/静态变量);再调用 C 库初始化(如 __libc_init_array,跑 C++ 全局构造),最后跳 main。.data 存的是「有初值且初值非零」的全局与静态变量,它同时占用 Flash(初值)和 RAM(运行时);.bss 存的是未初始化或初值为 0 的全局与静态变量,只占 RAM、不占 Flash 镜像大小;const 常量通常放 .rodata(在 Flash 里)。补充点:函数内的 static 变量属于静态存储区、放在 .data/.bss,函数内的普通局部变量在栈上,malloc 出来的在堆上;链接脚本决定各段布局,栈顶地址就写在向量表第一项。
任务切换与 HardFault 归类:RTOS 的任务切换流程是:触发源(SysTick 节拍或任务主动让出)把 PendSV 异常置为挂起,等到当前中断处理完、进入最低优先级的 PendSV 处理程序后,硬件自动把当前任务的 R0–R3、R12、LR、PC、xPSR 压入它的栈,软件再手动保存 R4–R11,然后更新任务控制块里的栈指针、从就绪链表里挑出最高优先级任务、把新任务的栈指针写入 PSP,恢复其寄存器,异常返回后 CPU 就在新任务的上下文里继续跑。HardFault 的归类要会分:按触发来源看是总线错误(取指/数据访问非法地址,如野指针、外设时钟未开就读寄存器)、存储器管理错误(MPU/MMU 权限或越界)、用法错误(除零、非对齐访问、跳到非法状态、从非代码区取指);排查手段是读取 CFSR/HFSR/BFAR/MMAR 寄存器判断是哪一类,再看栈帧里的 PC 定位到具体指令,配合反汇编与 addr2line 找到源码行。答题时如果能说清「我通过 CFSR 的哪一位判断出是总线错误、再根据 BFAR 的地址发现空指针」,比只背分类更能证明你真调过现场。
项目拷打与 AI 使用怎么答:面试官从自我介绍直接跳到三个项目逐项拷问,考查的是「你讲的项目是否经得起追问」。准备方法是给每个项目写一张卡片:一句话说明做什么、系统由哪些模块组成、数据流与控制流怎么走、你负责哪部分、当时最难的三个技术点是什么、你做过什么取舍、结果如何量化。被问「架构怎么设计」时要能画出来:任务划分与优先级、模块间用队列还是信号量通信、共享资源怎么保护、错误怎么处理与上报、升级与配置怎么管理。问到 AI 使用,诚实的答法是分场景:用它做资料检索、样板代码、看报错与写注释,但关键的外设初始化、时序控制、并发保护必须自己读懂再改,且改完要在板子上验证;同时可以说说防坑做法,比如让它解释某段寄存器配置的每一位含义、生成测试用例、帮忙梳理状态机,而不是直接复制粘贴到工程里。最后的复习优先级:RTOS(优先级、栈、同步、调度)> 总线协议与异常排查 > 启动流程与内存布局 > 故障定位(HardFault、看门狗、死机现场保留)。