如何测量电流?

解读

在 Rust 岗位面试中,面试官抛出“如何测量电流”这一看似硬件的问题,并非考察电子学本身,而是考察候选人能否把“测量”这一动作抽象成安全、并发、零开销的软件模型,并用 Rust 的 Ownership、Channel、Async 等机制落地。国内嵌入式与物联网团队普遍缺人,能否用 Rust 写出一套“既不会踩内存、又能中断级并发”的电流采样框架,是加分关键。

知识点

  1. Ownership 与 DMA 缓冲区:ADC 采样需要一块连续物理内存,Rust 的 Pin<Box<[u16; N]>> 可在编译期保证缓冲区不会被意外移动,避免 C 里“指针失效”导致的采样错位。
  2. Borrow Checker 与中断共享:多个中断源(ADC 完成、DMA 半传输、定时器触发)需要共享状态,用 &mut 独占借用即可在编译期消灭数据竞争,无需关中断或禁用抢占
  3. 无锁 RingBuffer:基于 heapless::spsc::Queue,中断端只做 enqueue,主循环做 dequeue编译期确定容量,零堆分配,符合 Rust 嵌入式生态主流做法。
  4. Async 电流采样流:用 embassy 框架把 ADC 封装成 async fn current() -> f32,上层业务直接 while let Some(i) = current().await {}看起来像阻塞,实际是中断级异步,内存安全由编译器背书。
  5. 零成本校准:在 build.rs 里把温度漂移系数编译进 .rodata,运行期只做乘法,无函数调用开销,符合 Rust “零成本抽象”承诺。

答案

“我会把电流测量拆成三层,全部用 Rust 编译期检查保证安全:
第一层是硬件抽象层,用 stm32f4xx-halAdc<Enabled, Clock>,把 ADC 引脚配置成 Analog 模式,借用检查器确保引脚不会被二次移动
第二层是无锁数据链路,在中断里把 ADC 原始值 u16 直接 pushheapless::spsc::Queue队列容量在泛型参数里写死,编译期就确定不会溢出。
第三层是业务校准,用 const fn 在编译期把采样电阻、运放增益、温度系数算成 f32 常量,运行期只做一次乘加,零运行时开销
最终给上层提供一个 async fn read_current() -> f32,调用方完全感知不到中断、DMA、校准细节,编译通过即可保证没有悬垂指针、数据竞争或堆溢出。”

拓展思考

  1. 如果电流范围从毫安级到百安级,需要动态切换采样电阻与 PGA 增益,如何用 Rust 的 enum Gain { G1, G8, G32 } 在编译期枚举所有合法组合,防止非法增益配置?
  2. 当系统进入低功耗 Standby 时,ADC 上下文会丢失,如何用 Drop trait 在 Rust 里自动保存校准参数到 RTC Backup Register,确保唤醒后无需重新校准?
  3. 若把电流波形通过 CAN-FD 实时上传到上位机,怎样用 #[repr(C)] 结构体保证 Rust 端与 C 端二进制布局一致,同时用 zerocopy crate 在编译期检查字节对齐,避免传统 C 结构体对齐错误导致的总线错误?