嵌入式面试的高频题清单:面经用来校准标准,模拟追问才形成肌肉记忆
- 轮次
- 面试方法
- 时间
- 2026-10
- 来源
- 牛客网
《面试题目》
- 指针和数组在传参、
sizeof上有什么差别? volatile的使用场景和不适用场景?- 中断处理流程是什么?如何避免丢中断和 ISR 过重?
- 推挽和开漏怎么选?
- UART / SPI / I2C 怎么选型,常见坑有哪些?
- DMA 什么时候上,双缓冲要注意什么?
- FreeRTOS 的任务划分依据是什么?
- 队列、信号量、互斥量分别解决什么问题?
- 栈溢出和 HardFault 怎么定位?
- 项目里最难的一次故障,如何复现和验证修复?
- 启动流程从复位到 main 经历了什么?
- 如何证明你的模块达到了实时性和稳定性要求?
《参考解析》
指针与数组:数组名在多数表达式里退化为指向首元素的指针,但 sizeof(数组) 给整个数组大小、sizeof(指针) 给指针宽度;函数传参时数组一定退化为指针,所以函数内 sizeof 拿不到长度,必须额外传长度。&arr 的类型是「数组指针」,&arr + 1 跨过整个数组,而 arr + 1 只跨一个元素。
volatile 用在这三种场合:ISR 与主循环共享的变量、硬件寄存器映射、以及可能被 DMA 或其它执行流修改的内存。它只保证「每次都从内存读写、不被优化掉」,不保证原子性与内存序——多字节共享数据仍要加临界区或原子操作。不适用场景是把它当同步原语用,那是典型的误解。
中断与 ISR 过重:进 ISR 先保存现场、清中断标志,处理必须短,重活丢队列给任务;避免丢中断要靠「标志位 + 主循环处理」或加大队列深度、必要时用 DMA 搬运;中断优先级与临界区长度要算,长临界区会推迟高优先级中断。
推挽 vs 开漏:推挽能主动拉高拉低、驱动强,适合普通 GPIO 与 LED;开漏只能拉低、高电平靠上拉,适合 I2C 这类线与总线与电平转换,上拉阻值要与总线电容和速率一起算。
UART/SPI/I2C 选型的判据:UART 点到点、无时钟线、适合板间通信与调试;SPI 速率高、四线全双工、片选多设备,适合屏、Flash、传感器;I2C 两线多设备、有地址仲裁、速率低,适合低速传感器与配置寄存器。常见坑:SPI 的 CPOL/CPHA 配错、I2C 上拉不足导致上升沿慢、时钟拉伸处理缺失。
DMA 与双缓冲:外设吞吐高、CPU 不能逐字节搬时上 DMA;双缓冲要保证「DMA 写 A 时 CPU 读 B」,切换处用半传输/传输完成中断并处理缓存一致性(M7 带 D-Cache 时尤其要注意 clean/invalidate)。
FreeRTOS 任务划分:按周期、优先级、是否阻塞来拆,不要把「轮询 + 计算 + 发送」塞进一个任务;任务间通信优先队列与任务通知,共享资源用互斥量(带优先级继承)而不是二值信号量。
队列、信号量、互斥量的分工:队列传数据、信号量做同步与计数(ISR 通知任务)、互斥量保护共享资源并解决优先级反转。三者混用是新手最常见的错误。
栈溢出与 HardFault:先看栈水位与 MPU/栈哨兵,HardFault 里读 SCB 的 CFSR/HFSR/MMFAR/BFAR 判断是总线错误、对齐错误还是非法指令,再用栈回溯找调用点。
启动流程:复位取向量表里的初始 MSP 与 Reset_Handler → 拷贝 .data、清零 .bss、初始化时钟与 FPU → __libc_init_array → main。带 Bootloader 时还要确认向量表重映射与跳转前的状态清理。
「如何证明实时性与稳定性」:给可复现的度量——最坏执行时间(用 GPIO 翻转 + 示波器测)、连续跑机时长与异常率、压力下的丢包率与内存水位。有数字才叫证明。