嵌入式基础小知识
记录日常遇到的问题和参考答案
嵌入式基础
Q1:什么是 volatile?什么时候用?
答:volatile 告诉编译器该变量可能在程序控制之外被修改(如硬件寄存器、中断服务程序),每次访问都要从内存读取,不能做优化。
典型场景:硬件寄存器、多线程共享变量、ISR 中修改的变量。
Q2:栈和堆的区别?
答:
- 栈:函数局部变量、自动分配/释放、LIFO、空间小、速度快
- 堆:malloc 申请、手动 free、随机分配、空间大、速度慢
Q3:(待补充)
RTOS 相关(以 ThreadX 为主)
Q1:任务调度有哪些方式?
答:常见三类:
- 时间片轮转:同优先级线程轮流占用 CPU 一段时间。
- 优先级抢占:高优先级就绪后立即打断低优先级正在运行的线程。
- 协作式:低优先级主动
yield/阻塞才让出 CPU(现代 RTOS 多为抢占式为主)。
ThreadX 属于 可抢占、基于优先级的调度;同优先级可用 时间片(若开启)轮转。
Q2:ThreadX 里线程、中断与「上下文」指什么?
- 线程(Thread):有独立栈、优先级、入口函数;调度器在就绪线程里选最高优先级运行。
- 中断(ISR):硬件触发,在 中断栈/当前线程栈(与平台相关)上执行,不能阻塞(不能
tx_thread_sleep、不能拿会阻塞的同步原语)。 - 上下文切换:保存当前线程的寄存器/栈指针等到 TCB,恢复下一个线程的寄存器/栈,切换发生在 调度点(阻塞、抢占、时间片耗尽、pend 等)。
Q3:线程调度与线程切换一般在什么时机发生?
典型触发点:
- 线程调用 阻塞 API(如
tx_semaphore_get带 timeout、tx_queue_receive等)。 - 更高优先级线程变为就绪(如 ISR 中
tx_semaphore_put唤醒线程)。 - 时间片用完(同优先级轮转)。
- 线程 主动让出(如
tx_thread_relinquish,视配置)。
Q4:互斥量(Mutex)、信号量、二值信号量、事件标志组各干什么?
| 机制 | 典型用途 | ThreadX 侧要点 |
|---|---|---|
| 互斥量 Mutex | 保护 临界区、互斥访问共享资源;支持 优先级继承(可缓解优先级反转) | tx_mutex_get/put;持锁时间要短 |
| 计数信号量 Semaphore | 资源计数、生产-消费「许可」 | tx_semaphore_get/put |
| 二值信号量 | 可看作计数最大为 1 的信号量;常用于 单次通知/同步 | 同上,初始 count 设为 0/1 |
| 事件标志组 Event Flags | 多条件组合唤醒:等 多个位 的 AND/OR | tx_event_flags_get/set |
选用建议:只保护数据用 Mutex;发信号/计数用 Semaphore;多事件组合用 Event Flags。
Q5:队列(Message Queue)在 RTOS 里怎么用?
- 线程间传递 定长消息(数据块拷贝进队列),发送方与接收方 解耦。
- ThreadX:
tx_queue_create/send/front_send/receive;注意 消息长度固定、队列深度要按峰值流量设计。 - ISR 中通常用 非阻塞 或 专用 ISR 安全 API(视 port 文档),避免在 ISR 里长时间等待。
Q6:字节池(Byte Pool)与堆分配是什么关系?
- Byte Pool:ThreadX 提供的 可变长度 内存池,从池中
allocate/release,可减少碎片、行为比裸malloc更可预期(仍要注意碎片与并发)。 - 堆(Heap):常指 C 库
malloc/free或启动文件划给_heap的区域;与 线程栈(静态或分配自 byte pool)是不同概念。 - 实践:关键路径、ISR 附近避免频繁
malloc;大块、长期对象可静态或池化;用完必须 配对释放,避免泄漏。
Q7:线程与中断切换过程、如何保护临界区?
中断 → 线程:ISR 里可 put 信号量等唤醒线程;真正线程切换往往在 ISR 退出后 由调度器完成(与架构相关)。
保护临界区的常用方式:
- 关中断
disable/enable:最短路径保护 少量共享标志/指针;持锁时间必须极短。 - Mutex:线程级互斥,可配合优先级继承;不要在持锁时做耗时操作。
- 只读数据 或 无锁队列(高级):减少锁竞争。
注意:在 ISR 里不要长时间关中断;不要在中断里等 Mutex(易死锁)。
Q8:优先级反转是什么?Mutex 如何缓解?
- 现象:高优先级线程等低优先级占用的资源,而低优先级又被 中优先级 抢占,导致高优先级 长时间阻塞。
- 缓解:优先级继承 Mutex(ThreadX Mutex 支持):持锁线程临时提升到等待该锁的最高优先级线程的级别,释放后恢复。
- 设计:缩短临界区、减少持锁范围、避免在持锁路径上调用可能阻塞的 API。
PID 与调试
Q1:位置式 PID 与增量式 PID
位置式(全量输出,离散形式示意):
u(k) = Kp*e(k) + Ki*Σe(i) + Kd*(e(k)-e(k-1))- 直接给出 控制量(如 PWM 占空、DAC)。
- 积分项累积明显,参数整定直观;积分饱和(windup)需注意。
增量式(Δu):
Δu(k) = Kp*(e(k)-e(k-1)) + Ki*e(k) + Kd*(e(k)-2e(k-1)+e(k-2))(常见写法之一,与教材离散化可能略有差异,以你实现为准)- 输出为 增量,执行机构积分(如步进、阀位累加)时较自然;切换冲击小。
Q2:如何减小或消除稳态误差?
- 积分项 Ki:消除 常值扰动 带来的静差(I 作用把平均误差拉向 0);Ki 过大易振荡、超调。
- 前馈:已知扰动(如负载、重力)可补偿,减轻对 I 的依赖。
- 死区与量化:传感器噪声大时,过小误差不要进积分(死区或积分分离),避免抖动。
- 抗积分饱和:输出限幅时停止或对积分 clamp,避免 I 项「堆满」导致大超调。
- 采样周期:过慢会等效延迟大,PID 难稳;与滤波截止频率匹配。
Q3:调试顺序建议
先 P 使系统有响应且不过振 → 加 I 压静差 → 必要时加 D 抑超调(对噪声敏感需先低通)。阶跃响应看 超调、调节时间、稳态误差;扫频或记录阶跃数据辅助。
卡尔曼滤波、最小二乘与快排(工程应用)
Q1:卡尔曼滤波(Kalman Filter)在嵌入式里干什么用?
在 有过程噪声 和 测量噪声 时,对状态(位置、速度、姿态、传感器融合)做 最优(线性高斯假设下)估计。
- 预测:用模型推下一时刻状态。
- 更新:用新测量修正,增益 K 由噪声协方差自动权衡「信模型还是信传感器」。
- 应用:IMU+编码器融合、温漂估计、电池 SOC 估计(扩展卡尔曼 EKF/无迹 UKF 用于非线性)。
实践:矩阵维数要小;定点或简化为 一阶低通+互补 若算力极紧。
Q2:最小二乘法拟合曲线
给定数据点 (xi, yi),选模型(如直线 y=ax+b、多项式),使 残差平方和最小。
- 应用:传感器 标定曲线、温漂补偿、简易非线性校正。
- 实现:正规方程或递推最小二乘(RLS)适合在线更新;注意病态矩阵时用 QR/SVD(资源够时)。
Q3:快排(Quicksort)处理数据的实际应用
对 数组排序 平均时间复杂度 O(n log n)。
- 应用:取 中位数 抗野值、滑动窗口内排序后取分位、日志时间戳整理、ADC 批量采样后 剔除极值再平均。
- 嵌入式注意:最坏情况递归深度;可改 堆排序 或 插入排序(小数组)保证栈与最坏时间;避免在中断里做大数组排序。
STM32 定时器与「回调里能做什么」
Q1:定时器中断/回调的「上下文」是什么?
- 定时器到点触发 中断,在 ISR 上下文 执行(优先级由 NVIC 配置)。
- 与 RTOS 线程不同:ISR 内 不能 使用会 阻塞 的 RTOS 调用;应只做 清标志、读计数、写 GPIO、发信号 等短操作。
Q2:执行完为什么会「退出」?下次怎么再来?
- ISR 执行完
return,硬件清除或软件清除 更新/比较中断标志 后,CPU 回到被打断的线程。 - 定时器 自动重装(如 ARR 更新)则到点会 再次 进中断;若未使能下次中断或关闭了定时器,则不再进。
Q3:定时器中断里会不会「优先级反转」?
- 经典优先级反转 多指 RTOS 下 Mutex + 多线程 的场景。
- 若 ISR 里 抢 Mutex 而另一低优先级线程已持锁,可能拖长 ISR(极不推荐在 ISR 里拿 Mutex)。
- NVIC 层面:高抢占优先级的中断可打断低优先级 ISR,这是 中断嵌套,不是 Mutex 反转,但要控制 ISR 长度 与 栈使用。
Q4:为什么定时器回调里不能跑太久?
- 阻塞其他中断与线程:关闭或延迟同级/低优先级中断响应,抖动变大。
- RTOS 时钟节拍漂移:若 SysTick/调度依赖 tick,长时间关中断会破坏 实时性。
- 栈与 WCET:ISR 嵌套消耗栈;过长增大 最坏执行时间,系统难以保证截止期。
建议:ISR 内只做 拷贝数据、置位、给信号量;重活放到 线程 里做。
项目相关
(根据你的项目,提前准备好「项目难点」「如何解决」的话术)