Skip to content

嵌入式基础小知识

记录日常遇到的问题和参考答案

嵌入式基础

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/ORtx_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 退出后 由调度器完成(与架构相关)。

保护临界区的常用方式

  1. 关中断 disable/enable:最短路径保护 少量共享标志/指针;持锁时间必须极短。
  2. Mutex:线程级互斥,可配合优先级继承;不要在持锁时做耗时操作
  3. 只读数据无锁队列(高级):减少锁竞争。

注意:在 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 内只做 拷贝数据、置位、给信号量;重活放到 线程 里做。


项目相关

(根据你的项目,提前准备好「项目难点」「如何解决」的话术)