因最近公司需要,借此机会和大家一起学习AD9361
制作不易,记得三连哦,给我动力,持续更新!
工程文件下载:纯硬件SPI配置AD9361 提取码:g9jy
----------------------------------------------------------------------------------------
因为ADI官方,只提供了利用软件(SDK)和硬件平台(vivado)去配置AD936x,但是在一些工程中,这种方法很难去应用到实际的项目中,所以给大家介绍一个纯硬件配置AD936x的一个详细教程。因为是手把手教程,所以有些大佬不要嫌麻烦。同时后期会更新工程上的项目设计。废话不多说了,直接进入主题!和我一起学习神秘而又神奇AD936x吧!少年!
我用的是zedboard+ad9361,和我的硬件一样的伙伴,手把手教你配置9361,可以完全按照我的步骤进行,FPGA芯片为zynq7020的应该也可以。其余的根据自身芯片要求略微修改即可
根据前两章的讲解,相信大家已经完成了AD936x的脚本准备工作,这节将给大家讲解如果通过SPI把上一节生成的脚本文件,配置到AD9361上。
一、新建一个vivado工程
新建vivado硬件工程,然后分别把图中这几个文件代码导入工程中,
二、AD9361配置脚本文件
此文件就是上一节,通过AD936X Evaluation Software软件配置好的,脚本文件转化成Verilog的脚本文件,里面包含了所有的ad9361的寄存器配置信息。
三、顶层设计
顶层主要连接各个模块例化
部分代码:
module system_top(
//系统输入时钟
input i_clk,
//AD936X SPI配置
output o_spi_clk,
output o_spi_csn,
input i_spi_miso,
output o_spi_mosi,
//AD936X 状态控制引脚
output en_agc,
output reg enable,
output reg txnrx,
output chip_rst_n,
output sync_in,
output [3:0] ctrl_in,
//AD936X 接收通道数据引脚
input rx_clk_in_p,
input rx_clk_in_n,
input [5:0] rx_data_in_n,
input [5:0] rx_data_in_p,
input rx_frame_in_n,
input rx_frame_in_p,
//AD936X 发射通道数据引脚
字
数
太
多
省
略
一
部
分
//接收数据信号
.rx0_i_data (rx0_i_data ),
.rx0_q_data (rx0_q_data ),
.rx1_i_data (rx1_i_data ),
.rx1_q_data (rx1_q_data ),
/*
.tx0_i_data (tx0_i_data1),
.tx0_q_data (tx0_q_data1),
.tx1_i_data (tx1_i_data1),
.tx1_q_data (tx1_q_data1)*/
.tx0_i_data (tx0_i_data),
.tx0_q_data (tx0_q_data),
.tx1_i_data (tx1_i_data),
.tx1_q_data (tx1_q_data),
.clk_200M (clk_200M )
);
endmodule
四、数据处理模块
ad9361数据处理模块,此模块设计了采用LVDS传输模式的接口,接口设计清晰,可以嫁接到任何ad9361和其他FPGA开发板的接口连接,并且接口均为ad9361的接口,并没有其余无用的端口,移植起来非常方便。其中包含了9361数据发射和数据接收。
部分代码:
module data_process(
//接收数据通道
input rx_clk_in_p,
input rx_clk_in_n,
input rx_frame_in_p,
input rx_frame_in_n,
input [5:0] rx_data_in_p,
input [5:0] rx_data_in_n,
//发送数据通道
output tx_clk_out_p,
output tx_clk_out_n,
output tx_frame_out_p,
output tx_frame_out_n,
output [5:0] tx_data_out_p,
output [5:0] tx_data_out_n,
//复位信号
input reset,
output sample_clk,
//接收数据信号
output reg [11:0] rx0_i_data,
output reg [11:0] rx0_q_data,
output reg [11:0] rx1_i_data,
output reg [11:0] rx1_q_data,
//发送数据信号
input [11:0] tx0_i_data,
input [11:0] tx0_q_data,
input [11:0] tx1_i_data,
input [11:0] tx1_q_data,
input wire clk_200M
);
/****************************SELECTIO IO***********************************/
//selectIO输入和输出的数据
wire [13:0] selectio_rx_data_delay,selectio_rx_data;
reg [13:0] selectio_tx_data;
//selectIO输出的单端时钟
// wire data_clk;
//物理接口
wire [6:0] selectio_rx_data_in_p;
wire [6:0] selectio_rx_data_in_n;
wire [6:0] selectio_tx_data_out_p;
wire [6:0] selectio_tx_data_out_n;
assign selectio_rx_data_in_p = {rx_frame_in_p,rx_data_in_p};
assign selectio_rx_data_in_n = {rx_frame_in_n,rx_data_in_n};
wire [4:0]DELAY;//步长78ps,最大步数31约为2.418ns
vio_0 vio_0_inst (
.clk(clk_200M), // input wire clk
.probe_out0(DELAY) // output wire [4 : 0] probe_out0
);
五、ad9361初始化模块
根据上一节的ad9361_lut.v脚本
文件,可以将读取脚本用状态机实现,分为三个状态为写状态、读状态和延时状态,并通过循环来反复执行这些状态,直到所有寄存器都被配置完毕。最后,可以将状态机设置为一个固定状态,并发出配置结束的标志信号。
为了确保正常操作,我建议将时钟频率设置为25MHz,并与SPI读写时钟保持一致。这样可以确保数据传输的稳定性和可靠性。
通过这种方式,您可以有效地配置AD9361芯片的寄存器,并在配置完成后获得一个明确的结束标志,以确保配置的正确性和完整性。
通过ad9361_config_writedata传输到SPI读写模块进行读写操作,实现对9361配置脚本的读写,从而配置9361
部分代码:
module AD936X_Init#
(
parameter SPI_CLK_FREQ = 25
)
(
input i_clk ,
input i_rst ,
output o_spi_clk ,
output o_spi_cs ,
output o_spi_mosi ,
input i_spi_miso ,
output reg chip_rst_n ,
output reg init_done
);
/************************reg****************************/
reg [11:0] index ; //初始化参数索引
reg [18:0] command ; //配置参数命令
reg [7:0] readdata ; //读出的8位数据
/************************wire***************************/
wire w_active; //SPI总线激活信号
reg [23:0] i_user_data ; //SPI写数据信号
reg i_user_valid; //SPI写有效信号
wire o_user_ready; //SPI驱动准备好信号
wire [23:0] o_user_read_data; //读出数据
wire o_user_read_valid; //读出数据有效信号
/*********************component*************************/
/*****配置命令索引控制*****/
always @(posedge i_clk or posedge i_rst) begin
if(i_rst)
index <= 'd0;
else if(st_current == P_ST_ADD)
index <= index + 1;
else
index <= index;
end
/*****初始化完成信号*****/
always @(posedge i_clk or posedge i_rst) begin
if(i_rst)
init_done <= 'd0;
else if(st_current == P_ST_END)
init_done <= 'd1;
else
init_done <= init_done;
end
六、SPI驱动模块
基于上一节读取脚本文件读出的地址和数据,我们可以设计一个通用的SPI控制器,用于读写各种配置寄存器。同时,我们可以使用一个always
语句来实时输入数据,实现对寄存器的配置。
这个通用SPI控制器具有以下特点:
- 灵活性:它可以适应不同的寄存器配置需求,根据脚本文件中的地址和数据选择相应的寄存器进行读写操作。
- 可重用性:由于其通用性,该控制器可以在不同的工程中被重复使用,以满足不同的寄存器配置需求。
- 状态机驱动:通过三个状态机(开始传输、传输状态和传输完成),控制器可以在不同的阶段执行相应的操作,以确保正确的数据传输和操作完成。
通过使用这个通用SPI控制器,您可以方便地配置各种寄存器,并在传输完成后获得相应的状态信号,以确保配置的成功和完整性。
部分代码:
module spi_drive#(
parameter P_DATA_WIDTH = 8 ,
parameter P_CPOL = 0 ,
parameter P_CPHA = 0
)(
input i_clk ,
input i_rst ,
output o_spi_clk ,
output o_spi_cs ,
output o_spi_mosi ,
input i_spi_miso ,
input [P_DATA_WIDTH-1:0] i_user_data ,
input i_user_valid ,
output o_user_ready ,
output [P_DATA_WIDTH-1:0] o_user_read_data ,
output o_user_read_valid
);
字
数
限
制
省
略
always @(posedge i_clk or posedge i_rst) begin
if(i_rst)
ro_user_read_data <= 'd0;
else if(!r_spi_cnt && P_CPHA==0 && r_run)
ro_user_read_data <= {ro_user_read_data[P_DATA_WIDTH - 2:0],i_spi_miso};
else if(r_spi_cnt && P_CPHA==1 && r_run)
ro_user_read_data <= {ro_user_read_data[P_DATA_WIDTH - 2:0],i_spi_miso};
else
ro_user_read_data <= ro_user_read_data;
end
always @(posedge i_clk or posedge i_rst) begin
if(i_rst)
ro_user_read_valid <= 'd0;
else if(r_spi_cnt && r_cnt == P_DATA_WIDTH - 1)
ro_user_read_valid <= 'd1;
else
ro_user_read_valid <= 'd0;
end
endmodule
七、下板测试
把上述文件全部加入工程之后,然后分配管脚,进行编译和实现,产生bit流文件
AD9361应处在FDD工作状态,tx1和rx1,tx2和rx2双通道收发,收的频率2.4GHz,发的频率2.4GHz,为了测试方便本次设计产生一个稳定的正弦波信号,用频谱仪和ila分别测试,得出实验结果如下所示:
并且通过ila查看了每个读寄存器的值,均符合要求。
本次纯逻辑配置ad9361设计完成,更多设计后续继续更新,,,