小鹰锐视-嵌入式开发 二面 面经
1. 简单介绍一下你自己和你最擅长的技术领域
参考答案
面试官您好,我是 XXX。我主要的技术方向是嵌入式系统开发,有两年多的项目经验。
我最擅长的是 STM32 的开发和 Linux 系统编程。
STM32 方面:
- 熟悉各种外设的使用,包括 GPIO、定时器、UART、SPI、I2C、ADC、DMA 等
- 做过多个基于 STM32 的项目,从底层驱动到应用逻辑都有涉及
- 对 FreeRTOS 比较熟悉,能够设计合理的任务架构,处理好任务间的同步和通信
Linux 方面:
- 熟悉系统编程,包括进程线程管理、网络编程、文件 IO 等
- 做过高性能服务器项目,对 epoll、多线程、性能优化有深入理解
- 了解一些 Linux 内核的知识,比如虚拟内存、进程调度等
我对嵌入式系统的底层原理很感兴趣,喜欢深入研究技术细节,解决复杂的技术问题。希望能在贵公司继续深入学习和成长。
2. 说说你对 DMA 的理解,DMA 的工作原理是什么?在什么场景下使用 DMA?使用时要注意什么?
参考答案
DMA 基本概念:
DMA 是 Direct Memory Access(直接内存访问),允许外设直接访问内存,不需要 CPU 干预。
- 传统 IO 方式:CPU 从外设读数据到寄存器,再写入内存,CPU 全程参与,效率低
- DMA 方式:CPU 配置好 DMA 控制器后,DMA 控制器接管总线,直接在外设和内存之间传输数据,传输完成后通过中断通知 CPU
DMA 工作原理:
- CPU 配置 DMA 控制器,包括源地址、目标地址、传输大小、传输方向、优先级等
- 启动 DMA 后,DMA 控制器向总线仲裁器请求总线使用权
- 获得总线后开始传输数据,每传输一个数据,地址指针自动递增或递减
- 传输完成后,DMA 控制器触发中断,通知 CPU
DMA 传输模式:
- 外设到内存 (P2M):UART 接收、ADC 采样
- 内存到外设 (M2P):UART 发送、DAC 输出
- 内存到内存 (M2M):内存拷贝
DMA 的优势:
释放 CPU,提高系统吞吐量,特别适合大量数据传输的场景:
- UART 高速收发:DMA 可以在后台接收数据,不需要 CPU 轮询或频繁中断
- SPI 传输大量数据:用 DMA 可以提高速度
- ADC 连续采样:用 DMA 可以自动存储采样结果
使用 DMA 的注意事项:
- Cache 一致性如果 MCU 有 Cache,DMA 直接访问物理内存,绕过了 Cache,可能导致数据不一致发送数据前要 flush Cache,确保数据写入内存接收数据后要 invalidate Cache,确保 CPU 读到最新数据
- 内存对齐DMA 通常要求数据地址和大小对齐,比如 4 字节或 32 字节对齐不对齐可能导致传输失败或效率低
- 内存区域有些 DMA 控制器只能访问特定的内存区域比如 STM32 的 DMA 不能访问 CCM(Core Coupled Memory),要用 SRAM
- 并发访问DMA 传输期间不能修改缓冲区,要用标志或信号量同步DMA 传输完成会触发中断,中断处理函数中要检查传输状态,处理错误
- 循环模式DMA 支持循环模式,传输完成后自动重新开始,适合连续采集场景使用双缓冲可以避免数据覆盖,一个缓冲区 DMA 写入,另一个缓冲区 CPU 处理
项目经验:
在我的项目中,我用 DMA 实现了 UART 的高速收发,接收速率达到了几百 KB/s,CPU 占用率很低。还用 DMA 实现了 ADC 的连续采样,配合定时器触发,实现了高精度的数据采集。
3. 说说你对 PID 控制算法的理解,PID 的三个参数分别起什么作用?如何调参?
参考答案
PID 基本概念:
PID 是比例-积分-微分控制算法,是工业控制中最常用的算法。PID 控制器根据误差计算控制量,使系统输出接近期望值。
PID 数学表达式:
u(t) = Kp * e(t) + Ki * ∫e(t)dt + Kd * de(t)/dt
其中:
e(t)是误差,等于期望值减去实际值u(t)是控制量Kp、Ki、Kd是三个参数
三个参数的作用:
- 比例 P 项:Kp * e(t)与当前误差成正比作用:快速响应误差,误差越大,控制量越大缺点:单纯的 P 控制会有稳态误差,无法完全消除误差Kp 太大会导致超调和振荡,太小响应慢
- 积分 I 项:Ki * ∫e(t)dt是误差的累积作用:消除稳态误差,只要有误差,积分项就会不断累积,直到误差为零缺点:会导致超调,响应变慢Ki 太大会导致振荡,太小消除误差慢
- 微分 D 项:Kd * de(t)/dt是误差的变化率作用:预测误差的趋势,提前调整,减少超调,加快响应缺点:对噪声敏感,噪声会导致微分项剧烈变化Kd 太大会放大噪声,太小抑制超调效果不明显
离散化 PID 算法实现:
float pid_control(float setpoint, float measured_value) {
static float integral = 0;
static float prev_error = 0;
float error = setpoint - measured_value;
// 比例项
float p_term = Kp * error;
// 积分项
integral += error * dt;
// 积分限幅,防止积分饱和
if (integral > integral_max) integral = integral_max;
if (integral < integral_min) integral = integral_min;
float i_term = Ki * integral;
// 微分项
float derivative = (error - prev_error) / dt;
float d_term = Kd * derivative;
prev_error = error;
// 控制量
float output = p_term + i_term + d_term;
// 输出限幅
if (output > output_max) output = output_max;
if (output < output_min) output = output_min;
return output;
}
PID 调参方法:
- 经验法先设置 Ki=0,Kd=0,只调 Kp观察响应,逐渐增大 Kp 直到出现振荡,然后减小到振荡消失再加入 Ki,消除稳态误差最后加入 Kd,减少超调
- Ziegler-Nichols 法先设置 Ki=0,Kd=0,增大 Kp 直到系统持续振荡记录临界增益 Ku 和振荡周期 Tu根据公式计算: Kp = 0.6 * KuKi = 1.2 * Ku / TuKd = 0.075 * Ku * Tu
- 试凑法如果响应慢,增大 Kp如果有稳态误差,增大 Ki如果超调大,增大 Kd 或减小 Kp如果振荡,减小 Kp 和 Ki
实际应用注意事项:
- 积分饱和:积分项累积过大,导致系统响应变慢,可以用积分限幅或积分分离解决
- 微分噪声:可以用低通滤波或不完全微分解决
项目经验:
我在项目中用 PID 实现过温度控制,通过调节加热功率,使温度稳定在设定值。经过调参,温度波动控制在 ±0.5℃ 以内,效果很好。
4. 说说你对卡尔曼滤波的理解,卡尔曼滤波解决什么问题?基本原理是什么?
参考答案
卡尔曼滤波基本概念:
卡尔曼滤波是一种最优估计算法,用于从含有噪声的测量数据中估计系统的状态。它广泛应用于导航、目标跟踪、传感器融合等领域。
解决的问题:
系统的状态无法直接测量,只能通过含有噪声的传感器间接测量。比如:
- 飞机的位置和速度:GPS 测量有误差,加速度计积分也有误差
- 卡尔曼滤波融合多个传感器的数据,给出最优估计
基本原理:
假设系统的状态转移和测量过程都是线性的,噪声是高斯白噪声。通过预测和更新两个步骤,递推地估计系统状态。
预测步骤:
根据系统模型预测下一时刻的状态和协方差:
状态预测:x_pred = A * x + B * u 协方差预测:P_pred = A * P * A^T + Q
其中:
x是状态A是状态转移矩阵B是控制输入矩阵u是控制输入P是协方差矩阵Q是过程噪声协方差
更新步骤:
根据测量值更新状态估计:
计算卡尔曼增益:K = P_pred * H^T * (H * P_pred * H^T + R)^(-1) 状态更新:x = x_pred + K * (z - H * x_pred) 协方差更新:P = (I - K * H) * P_pred
其中:
z是测量值H是测量矩阵R是测量噪声协方差K是卡尔曼增益
卡尔曼增益决定了预测值和测量值的权重:
- 如果测量噪声小,K 大,更相信测量值
- 如果过程噪声小,K 小,更相信预测值
简化的一维卡尔曼滤波示例:
typedef struct {
float x; // 状态估计
float P; // 估计协方差
float Q; // 过程噪声协方差
float R; // 测量噪声协方差
} KalmanFilter;
void kalman_init(KalmanFilter *kf, float Q, float R) {
kf->x = 0;
kf->P = 1;
kf->Q = Q;
kf->R = R;
}
float kalman_update(KalmanFilter *kf, float measurement) {
// 预测
// x_pred = x (假设状态不变)
float P_pred = kf->P + kf->Q;
// 更新
float K = P_pred / (P_pred + kf->R); // 卡尔曼增益
kf->x = kf->x + K * (measurement - kf->x);
kf->P = (1 - K) * P_pred;
return kf->x;
}
优缺点:
- 优点:计算量小,适合实时系统,能够融合多个传感器的数据,给出最优估计
- 缺点:只适用于线性系统和高斯噪声,对于非线性系统需要用扩展卡尔曼滤波 (EKF) 或无迹卡尔曼滤波 (UKF)
应用场景:
- 姿态估计:融合加速度计和陀螺仪的数据,得到准确的姿态角
- GPS 和 IMU 融合:提高定位精度
项目经验:
我在项目中用卡尔曼滤波处理过传感器数据,滤除了噪声,提高了测量精度。虽然算法看起来复杂,但理解了原理后,实现起来并不难。
5. 说说你对嵌入式系统功耗优化的理解,有哪些常用的低功耗技术?
参考答案
嵌入式系统特别是电池供电的设备,功耗优化非常重要。功耗优化需要从硬件和软件两个方面入手。
硬件方面:
- 选择低功耗的 MCU,比如 STM32L 系列,支持多种低功耗模式
- 选择低功耗的外设和传感器,查看数据手册的功耗指标
- 优化电源设计,使用高效的 DC-DC 转换器,减少压降
- 使用低功耗的显示屏,比如电子墨水屏或低功耗 OLED
软件低功耗技术:
1. 睡眠模式
让 MCU 在空闲时进入低功耗状态。STM32 有几种睡眠模式:
- Sleep 模式:CPU 停止,外设运行,唤醒快,功耗中等
- Stop 模式:CPU 和大部分外设停止,只保留 RTC 和备份域,功耗低,唤醒较慢
- Standby 模式:只保持备份域,功耗最低,唤醒最慢,相当于复位
根据应用需求选择合适的睡眠模式:
- 需要快速响应:用 Sleep 模式
- 可以容忍较长的唤醒时间:用 Stop 或 Standby 模式
2. 唤醒源选择
- RTC 定时唤醒:定期采集数据
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
这是一个全面的嵌入式面试专栏。主要内容将包括:操作系统(进程管理、内存管理、文件系统等)、嵌入式系统(启动流程、驱动开发、中断管理等)、网络通信(TCP/IP协议栈、Socket编程等)、开发工具(交叉编译、调试工具等)以及实际项目经验分享。专栏将采用理论结合实践的方式,每个知识点都会附带相关的面试真题和答案解析。