3、数字下变频(DDC):NCO原理、混频器实现、CIC滤波器设计
各位同学,咱们今天聊聊数字下变频。说实话,DDC 是雷达信号处理里最基础、也最容易被忽视的模块。我刚开始做嵌入式雷达时,总觉得这玩意儿不就是个混频加滤波嘛,有啥难的?结果第一次上板调试,频谱乱七八糟,折腾了两天才发现是 NCO 的相位截断没处理好。嗯,从那以后我再也不敢小看 DDC 了。
3.1 为什么要做数字下变频?
雷达接收到的信号,通常是中频信号。比如 70MHz 的中频,采样率可能到 100MHz 甚至更高。你想想看,如果直接拿这个高速信号去做脉冲压缩、做 MTD,FPGA 的资源消耗会非常恐怖。
DDC 的核心目的就两个:
- 频谱搬移:把中频信号搬移到零中频(基带)
- 降采样:降低数据率,减轻后续处理负担
说白了,就是把 100MHz 的数据率降到 10MHz 甚至更低,同时保留所有有用信息。我见过有人图省事,直接降采样不做滤波,结果频谱混叠得一塌糊涂,目标都找不到了。
3.2 NCO 原理:怎么产生正交本振?
NCO,全称 Numerically Controlled Oscillator,数字控制振荡器。它的任务就是产生两路正交的正弦波:cos 和 sin。
核心公式很简单:
s(n) = cos(2π · f0/fs · n)
c(n) = sin(2π · f0/fs · n)
其中 f0 是本振频率,fs 是采样率。实际实现时,我们不会实时算三角函数,而是查表。
NCO 的典型结构:
- 相位累加器:每个时钟周期累加一个频率控制字
- 相位截断:只取高位地址去查表
- 正弦/余弦查找表:存储一个周期的波形数据
关键参数:
| 参数 | 说明 | 我的建议 |
|---|---|---|
| 累加器位宽 N | 决定频率分辨率 | 一般取 32 位,分辨率够用 |
| 查找表位宽 M | 决定幅度精度 | 12~16 位,看系统需求 |
| 相位截断位数 | N - 地址位宽 | 截断太多会产生杂散 |
我曾经在一个项目里,为了省 LUT,把查找表压缩到 8 位地址。结果频谱上出现了明显的杂散,差点把弱小目标淹没了。后来老老实实用了 12 位地址,问题解决。所以,相位截断的杂散抑制是 NCO 设计的第一要务。
3.3 混频器实现:乘法器与符号处理
混频器就是做乘法。输入信号 x(n) 分别乘以 cos 和 sin,得到 I/Q 两路:
I(n) = x(n) · cos(2π · f0/fs · n)
Q(n) = x(n) · sin(2π · f0/fs · n)
在 FPGA 里实现时,有几个坑要注意:
- 位宽扩展:两个 16 位数相乘,结果 32 位。你得想好截断策略。
- 符号处理:有符号数乘法,别搞成无符号了。
- 流水线:DSP48 块一般有内置流水线,记得用上。
我的小技巧:混频后的数据,先不要急着截断。保留完整位宽送到 CIC 滤波器,CIC 内部会做累加,精度损失更小。等 CIC 输出后再统一截位。
3.4 CIC 滤波器设计:降采样的利器
CIC 滤波器,全称 Cascaded Integrator-Comb。它最大的优点是:不需要乘法器,只有加法器和减法器。在 FPGA 里实现,资源消耗极低。
CIC 的结构:
- 积分器级:N 级积分器串联,每个积分器就是一个累加器
- 降采样:按抽取因子 R 降采样
- 梳状器级:N 级梳状器串联,每个梳状器做差分
频率响应公式:
H(z) = [ (1 - z^(-R)) / (1 - z^(-1)) ]^N
其中 R 是抽取因子,N 是级数。
注意!CIC 的增益很大:
CIC 的增益是 R^N。比如 R=16,N=3,增益就是 4096。输出位宽需要预留足够的比特,否则会溢出。我见过有人没算增益,结果输出全是饱和值,信号全丢了。
设计步骤:
- 第一步:确定抽取因子 R。根据输入数据率和输出数据率计算。
- 第二步:确定级数 N。一般取 3~5 级。级数越多,阻带衰减越大,但通带滚降也越严重。
- 第三步:计算位宽。输出位宽 = 输入位宽 + N · log2(R)。
- 第四步:加补偿滤波器。CIC 的通带不平坦,需要 FIR 补偿。
3.5 实战案例:70MHz 中频 DDC 设计
我拿一个实际项目举例。输入信号:70MHz 中频,采样率 100MHz。需要降到 10MHz 基带数据。
参数计算:
| 参数 | 值 | 说明 |
|---|---|---|
| 本振频率 f0 | 70MHz | 搬移到零中频 |
| 抽取因子 R | 10 | 100MHz → 10MHz |
| CIC 级数 N | 4 | 阻带衰减约 60dB |
| 输入位宽 | 14 bit | ADC 输出 |
| 输出位宽 | 14 + 4·log2(10) ≈ 28 bit | 实际取 32 bit 安全 |
代码片段(Verilog 风格):
// NCO 相位累加器
always @(posedge clk) begin
phase_acc <= phase_acc + freq_word; // 32-bit 累加
end
// 查表(用 Block RAM 实现)
assign sin_addr = phase_acc[31:20]; // 取高 12 位
assign cos_addr = sin_addr + 256; // 90 度偏移
// 混频器
assign i_mix = adc_data * cos_table[cos_addr];
assign q_mix = adc_data * sin_table[sin_addr];
// CIC 积分器级
always @(posedge clk) begin
integ1 <= integ1 + i_mix;
integ2 <= integ2 + integ1;
integ3 <= integ3 + integ2;
integ4 <= integ4 + integ3;
end
// 降采样 + 梳状器
always @(posedge clk_out) begin // clk_out = clk / 10
comb1 <= integ4 - integ4_d1;
comb2 <= comb1 - comb1_d1;
comb3 <= comb2 - comb2_d1;
comb4 <= comb3 - comb3_d1;
end
避坑指南:我曾经在 CIC 的积分器里用了 28 位宽,结果累加时溢出了。后来改成 32 位,并在每个积分器后加了饱和处理。记住,积分器一定要防溢出,否则信号会失真。
3.6 性能验证与调试
DDC 设计完,怎么验证对不对?我一般做三步:
- 看频谱:输入单频信号,看输出频谱是否干净。杂散应该低于 -60dBc。
- 测延时:I/Q 两路的延时必须一致。用阶跃信号测,看两路是否同时跳变。
- 测动态范围:输入大信号和小信号,看 CIC 是否饱和。
嗯,这里要注意。CIC 的通带滚降在雷达里是个大问题。如果你要处理的信号带宽比较宽(比如 5MHz 以上),一定要加 FIR 补偿滤波器。我习惯在 CIC 后面级联一个 32 阶的 FIR,把通带补偿平。
好了,DDC 的核心内容就这些。说白了,NCO 是灵魂,混频器是手脚,CIC 是心脏。三个模块配合好了,你的雷达信号处理就成功了一半。下一章咱们聊聊脉冲压缩,那才是真正考验功力的地方。