FPGA 软件无线电SDR AD9361 ZYNQ开发 只提供文字资料包含详细开发文档和工程源代码。最近在折腾软件无线电项目用AD9361射频前端搭配ZYNQ平台搭了套开发系统。这组合确实香——ZYNQ的ARM核跑Linux做高层协议FPGA处理基带数据流AD9361负责模拟域和射频转换三位一体搞SDR开发简直不要太合适。先看硬件架构图此处假装有图。AD9361通过LVDS接口直连ZYNQ的PL端同步时钟和使能信号用到了FPGA的全局时钟网络。重点是这个SPI配置总线得在ZYNQ的PS端通过AXI Quad SPI控制器来操作// 初始化SPI控制器 void ad9361_spi_init(void) { uint32_t ctrl_reg XSpi_GetControlReg(SPI_BASEADDR); ctrl_reg | XSP_CR_MASTER_MODE_MASK; // 主机模式 ctrl_reg | XSP_CR_CLK_PHASE_MASK; // 时钟相位对齐数据 XSpi_SetControlReg(SPI_BASEADDR, ctrl_reg); }这段代码设置了SPI控制器的关键参数注意时钟相位这个参数配错了会导致AD9361寄存器读写全乱套。之前在这栽过跟头示波器抓了半天波形才发现时钟边沿没对齐。射频参数配置才是重头戏。AD9361的寄存器配置像个精密钟表随便改个参数都可能引发连锁反应。比如设置接收通道带宽def set_rx_bandwidth(bw_mhz): decimation int(122.88 / bw_mhz) analog_bw bw_mhz * 1.2 # 留20%余量 write_register(0x011, analog_bw 8) write_register(0x012, analog_bw 0xFF) # 启动滤波器校准 write_register(0x01A, 0x01) while (read_register(0x01B) 0x80) 0: time.sleep(0.1)这里有个坑——带宽设置必须满足采样率整数倍关系。之前手贱设了个61.44MHz结果发现AD9361内部滤波器根本不支持这个非标数值直接导致接收信号失真。FPGA 软件无线电SDR AD9361 ZYNQ开发 只提供文字资料包含详细开发文档和工程源代码。FPGA端的LVDS接口处理也得讲究。Verilog代码里这个时钟域切换处理特别关键always (posedge rx_clk or posedge reset) begin if(reset) begin data_buffer 12h0; end else begin // LVDS数据对齐 if(rx_frame_sync) begin data_buffer {rx_data_p, rx_data_n}; // I/Q交织数据 end // 跨时钟域到AXI总线时钟 data_cdc data_buffer; end end这里用两级触发器做跨时钟域处理实测在122.88MHz时钟下跑得很稳。但注意rxframesync的建立时间必须满足时序要求否则会丢帧。之前没加时序约束结果收的数据全是错位的。DMA传输是性能瓶颈所在。用Scatter-Gather模式搬运数据时发现中断频率太高会导致系统卡顿。后来改用轮询模式并优化了AXI Burst长度// 配置DMA引擎 XDmaPs_SetChConfig(dma_inst, XDMAPS_CHANNEL_DMA, XDMAPS_DMA_CH_CTRL_BURST_LEN_16 | // 突发长度16拍 XDMAPS_DMA_CH_CTRL_SRC_INC | // 源地址自增 XDMAPS_DMA_CH_CTRL_DST_STATIC); // 目标地址固定突发长度从默认的4改成16后实测吞吐量直接翻倍。不过要注意内存地址必须对齐到64字节边界否则会触发AXI协议错误。调试时最崩溃的是频谱泄露问题。某次发射测试发现带外杂散超标查了三天发现是FPGA的FIR滤波器系数没做窗函数处理。后来用MATLAB生成系数时加了汉明窗h fir1(63, 0.4, hamming(64)); fid fopen(coef.hex,w); fprintf(fid,%x\n, round(h*32767));系数写入FPGA的ROM后频谱立马干净了。这教训告诉我们别相信理想滤波器模型实际处理不加窗就是在耍流氓。整个工程最爽的时刻是第一次抓到清晰的FM广播信号。看着Vivado的ILA抓取的波形从噪声变成整齐的正弦波那种成就感比喝肥宅快乐水还带劲。代码仓库里已经整理好了驱动、FPGA工程和上位机工具注释里写满了踩坑记录各位自取时记得点个star就行手动狗头。