LPCXpresso1549生成35-45kHz chirp信号频率异常问题咨询
问题分析与修复方案
你的猜测完全正确——主循环里用轮询Timer的方式控制采样输出,确实会导致实际采样率远低于预期的96000Hz,这就是为什么chirp的低频端被拉到3200Hz的核心原因。下面具体说说问题出在哪,以及怎么修复:
为什么当前代码的采样率不准?
你现在的代码是在主循环里不断检查Timer的时间,等达到10µs左右就输出一个样本。这种方式有两个致命缺陷:
- 轮询的延迟不可控:主循环里的浮点数比较(
t.read() >= 0.00001)、Timer的读取操作,甚至MCU后台的系统调度,都会让每次输出样本的间隔远大于10µs。比如浮点数运算本身就比整数慢,加上循环里的其他判断,实际间隔可能变成几十甚至上百微秒,直接把采样率拉低到几千Hz,自然就把chirp的低频段拖垮了。 - Timer的精度不够:mbed的通用
Timer类如果用的是软件模拟或者低精度硬件计时,本身就没法精准捕捉10µs这么小的时间窗口,误差会被不断放大。
正确的实现方式:用硬件定时器触发
要实现96000Hz这种高精度的采样率,必须靠硬件级别的同步触发,不能让CPU在主循环里瞎忙活。推荐两种靠谱的方案:
方案1:用Ticker定时器中断输出样本
mbed的Ticker是基于硬件定时器的高精度计时工具,能精准触发定时中断。我们可以让它每隔1/96000s触发一次中断,在中断里输出下一个样本,这样就能保证严格的时间间隔。
代码示例:
#include "mbed.h" #include "chirp.h" AnalogOut aout(P0_12); Ticker sample_ticker; volatile int sample_idx = 0; void output_next_sample() { // 输出当前样本,然后更新索引(循环播放) aout.write_u16(chirpData[sample_idx]); sample_idx = (sample_idx + 1) % NUM_SAMPLES; } int main() { // 设置触发间隔:1/96000 秒,确保精度用浮点数计算 sample_ticker.attach(&output_next_sample, 1.0f / 96000.0f); // 主循环可以闲置或者做其他任务,不需要管采样输出 while (true) { ThisThread::sleep_for(100ms); } }
方案2:DMA+定时器触发(零CPU开销,精度最高)
如果想追求极致的精度和效率,可以用DMA直接把你的chirpData数组传输到DAC,完全不需要CPU干预。LPC1549支持这个功能,配置步骤大概是:
- 把定时器配置成96000Hz的触发频率
- 开启DAC的DMA请求,关联到定时器的触发信号
- 设置DMA的数据源为
chirpData数组,目标为DAC的数据寄存器 - 启动DMA和定时器,数据就会自动源源不断地输出到DAC
这种方式完全避免了CPU中断的开销,采样率的精度只由硬件定时器决定,是最理想的方案。
验证方法
修复后可以用示波器或者频率计验证:
- 先输出一个固定的交替值(比如0和4095),测量信号周期是否接近
1/96000 ≈ 10.4µs,确认采样率正确 - 再播放chirp信号,测量频率范围是否回到35000Hz到45000Hz的预期区间
内容的提问来源于stack exchange,提问作者Thang Dinh
相关产品推荐
相关产品推荐

