1. 项目概述
作为一名FPGA开发者,我一直想搭建一个自由度更高的SDR(软件定义无线电)平台。自主控制AD9361射频收发器是实现这个目标的第一步。本文记录了我使用ZYNQ7020+AD9361硬件平台,通过官方noos(无操作系统)接口完成AD9361基础配置与测试的全过程。
这个实验的核心思路是:在PS端(ZYNQ的ARM处理器部分)使用ADI官方提供的noos驱动配置AD9361,在PL端(FPGA部分)通过BROM存储测试波形作为发射数据源,并通过ILA(集成逻辑分析仪)捕获AD9361接收到的基带信号。这种设计最大的优势是仅需一块开发板就能完成完整的收发测试,无需额外设备。
2. 硬件平台与架构设计
2.1 硬件选型与连接
我选择的硬件平台是Xilinx ZYNQ7020 SoC搭配ADI AD9361射频收发器。ZYNQ7020内部集成了双核ARM Cortex-A9处理器和Artix-7架构的FPGA,非常适合这种需要软硬件协同的项目。AD9361则是一款高性能、高集成度的射频收发器,工作频率范围70MHz至6GHz,支持多种调制方式。
硬件连接方面,AD9361通过以下接口与ZYNQ连接:
- SPI接口(通过ZYNQ的EMIO引出):用于配置AD9361内部寄存器
- GPIO(通过EMIO引出):控制AD9361复位和IDELAY参数
- LVDS差分接口:用于高速数据传输
- UART:用于调试和实时监控
2.2 系统架构设计
整个系统采用PS+PL协同工作的架构:
- PS端:运行精简的noos程序,通过SPI和GPIO配置AD9361工作参数
- PL端:
- 发射链路:BROM存储测试波形 → 官方LVDS接口模块 → AD9361
- 接收链路:AD9361 → 官方LVDS接口模块 → ILA捕获
这种架构的优势在于:
- 配置与数据处理分离,PS专注控制,PL专注数据流
- 使用官方LVDS接口模块,确保时序正确性
- ILA实时监控,便于调试
3. FPGA端实现细节
3.1 Block Design设计
在Vivado中创建的Block Design非常简洁:
- ZYNQ Processing System:配置启用SPI、GPIO、UART等外设
- AXI BRAM Controller:用于PS与PL之间的数据交互
- 官方提供的AD9361 LVDS接口模块

关键配置点:
- SPI时钟配置为10MHz(AD9361最高支持20MHz)
- GPIO位宽根据实际需要设置(至少需要2位:复位控制+IDELAY参数装载)
- LVDS接口时钟域隔离处理
3.2 数据流实现
FPGA端的顶层模块主要包含三部分:
- PS硬件接口封装(PS_hardwire_wrapper)
- AD9361数据流处理(U_AD9361_TOP)
- 包含官方LVDS接口模块
- 数据接收直接送ILA
- GPIO缓冲器(I_iobuf_gpio)
数据流的具体实现:
verilog复制module U_AD9361_TOP(
input wire clk,
input wire rst_n,
// AD9361 LVDS接口
input wire rx_clk_in_p, rx_clk_in_n,
input wire rx_frame_in_p, rx_frame_in_n,
input wire [5:0] rx_data_in_p, rx_data_in_n,
output wire tx_clk_out_p, tx_clk_out_n,
output wire tx_frame_out_p, tx_frame_out_n,
output wire [5:0] tx_data_out_p, tx_data_out_n,
// 用户接口
output wire adc_valid,
output wire [47:0] adc_data,
input wire dac_valid,
input wire [47:0] dac_data
);
// 实例化官方LVDS接口模块
axi_ad9361_lvds_if #(
.FPGA_TECHNOLOGY(1),
.DAC_IODELAY_ENABLE(0),
.USE_SSI_CLK(1)
) lvds_if_inst (
// 物理接口连接
.rx_clk_in_p(rx_clk_in_p),
.rx_clk_in_n(rx_clk_in_n),
// ...其他信号连接...
// 用户接口
.adc_valid(adc_valid),
.adc_data(adc_data),
.dac_valid(dac_valid),
.dac_data(dac_data),
// ...其他控制信号...
);
// BROM存储测试波形
bram_waveform waveform_inst (
.clk(clk),
.addr(addr_counter),
.dout(dac_data)
);
// ILA捕获模块
ila_0 ila_inst (
.clk(clk),
.probe0(adc_data),
.probe1(adc_valid)
);
endmodule
3.3 LVDS接口时序解析
AD9361的LVDS接口采用双边沿采样技术,时序较为复杂。官方提供的axi_ad9361_lvds_if.v模块已经完美处理了这些时序转换:
-
发射时序:
- FPGA内部单边沿数据 → ODDR转换为双边沿数据
- 单端信号 → OBUFDS转换为差分信号
-
接收时序:
- 差分信号 → IBUFDS转换为单端信号
- 双边沿数据 → IDDR转换为单边沿数据


4. PS端软件配置
4.1 noos驱动框架
ADI官方提供的noos驱动包结构如下:
code复制noos_ad9361/
├── ad9361_api.c # AD9361配置接口实现
├── ad9361_api.h # 结构体定义
├── config.h # 硬件平台配置
├── parameters.h # 硬件参数定义
├── console.c # 串口交互实现
├── command.c # 命令行处理
└── main.c # 主程序
4.2 关键配置函数
4.2.1 AD9361初始化
c复制AD9361_InitParam default_init_param = {
// 基础配置
.rx1tx1_mode = 1, // 单发单收模式
.data_rate_hz = 61440000, // 数据速率61.44MHz
.rf_bandwidth_hz = 18000000,// RF带宽18MHz
.rx_lo_freq_hz = 2400000000,// RX LO 2.4GHz
.tx_lo_freq_hz = 2400000000,// TX LO 2.4GHz
// 其他参数...
};
int32_t ad9361_init(struct ad9361_rf_phy **ad9361_phy,
AD9361_InitParam *init_param);
4.2.2 采样率设置
c复制// 设置TX采样率
int32_t ad9361_set_tx_sampling_freq(struct ad9361_rf_phy *phy,
uint32_t sampling_freq_hz);
// 设置RX采样率
int32_t ad9361_set_rx_sampling_freq(struct ad9361_rf_phy *phy,
uint32_t sampling_freq_hz);
4.2.3 滤波器配置
c复制// 禁用TX FIR滤波器
ad9361_set_tx_fir_en_dis(phy, 0);
// 禁用RX FIR滤波器
ad9361_set_rx_fir_en_dis(phy, 0);
4.3 主程序流程
精简后的主程序逻辑:
- 初始化硬件平台(SPI、GPIO、UART)
- 配置AD9361初始化参数
- 调用ad9361_init完成射频前端配置
- 进入主循环,等待串口命令或处理数据
c复制int main()
{
// 1. 硬件初始化
platform_init();
// 2. AD9361配置
struct ad9361_rf_phy *phy;
AD9361_InitParam init_param = default_init_param;
if (ad9361_init(&phy, &init_param) < 0) {
printf("AD9361初始化失败!\n");
return -1;
}
// 3. 禁用数字滤波器
ad9361_set_tx_fir_en_dis(phy, 0);
ad9361_set_rx_fir_en_dis(phy, 0);
// 4. 主循环
while (1) {
console_poll(phy); // 处理串口命令
// 其他处理...
}
return 0;
}
5. 关键问题与解决方案
5.1 数据接口时序对齐问题
现象:
- ILA捕获到的adc_valid信号始终为低
- 直接抓取IDDR输出有数据,但经过接口模块后丢失
原因分析:
AD9361的LVDS接口对时序非常敏感。由于PCB走线长度差异,数据与时钟信号可能存在相位偏移,导致采样错误。
解决方案:
- 使用官方接口模块中的IDELAY功能
- 通过PS端GPIO动态调整延时参数(0-31)
- 观察ILA信号,找到adc_valid能正确置位的延时值
重要提示:IDELAY的参考时钟建议使用200MHz,这是Xilinx官方推荐值。延时值装载需要同步脉冲信号,不能持续拉高。
5.2 数据有效信号控制
常见错误:
- dac_valid信号持续拉高
- 数据速率与配置不匹配
正确做法:
- 单发单收模式下,每2个数据时钟周期产生1个有效脉冲
- 数据格式:I/Q交替,12位有符号数
verilog复制// 数据有效信号生成示例
reg [1:0] valid_cnt;
always @(posedge data_clk) begin
valid_cnt <= valid_cnt + 1;
dac_valid <= (valid_cnt == 2'b00);
end
5.3 硬件约束要点
-
引脚约束:
- 必须正确定义LVDS引脚对
- 电压标准:通常LVDS为2.5V
-
差分终端电阻:
- 添加DIFF_TERM约束
tcl复制
set_property DIFF_TERM TRUE [get_ports {rx_clk_in_p}] -
时钟约束:
- 对AD9361提供的时钟需要create_generated_clock
6. 测试结果与性能分析
通过ILA捕获到的接收信号如下图所示:

关键指标测量:
- 采样率:61.44MHz(符合配置)
- 数据延迟:约8个时钟周期(含接口处理延迟)
- 信号质量:SNR > 60dB(使用正弦波测试)
在实际测试中发现,当关闭数字滤波器时,带外噪声会明显增加。因此在实际应用中,建议根据信号带宽合理配置滤波器参数。
7. 项目优化建议
-
动态重配置:
- 通过UART实现实时参数调整
- 支持跳频、带宽变更等操作
-
性能优化:
- 启用数字滤波器改善信号质量
- 优化IDELAY参数提升时序裕量
-
功能扩展:
- 添加DMA传输,实现高速数据流
- 集成DDS模块,生成复杂波形
这个项目最令我惊喜的是ADI官方提供的noos驱动和LVDS接口模块的成熟度,大大降低了开发难度。在实际操作中,最重要的经验是:一定要先确保IDELAY参数正确配置,这是数据通路正常工作的前提。对于想深入开发SDR系统的朋友,建议先从这种最小系统开始,逐步添加功能模块。