因最近公司需要,借此机会和大家一起学习AD9361
制作不易,记得三连哦,给我动力,持续更新!
工程文件下载:纯硬件SPI配置AD9361 提取码:g9jy
----------------------------------------------------------------------------------------
因为ADI官方,只提供了利用软件(SDK)和硬件平台(vivado)去配置AD936x,但是在一些工程中,这种方法很难去应用到实际的项目中,所以给大家介绍一个纯硬件配置AD936x的一个详细教程。因为是手把手教程,所以有些大佬不要嫌麻烦。同时后期会更新工程上的项目设计。废话不多说了,直接进入主题!和我一起学习神秘而又神奇AD936x吧!少年!
我用的是zedboard+ad9361,和我的硬件一样的伙伴,可以完全按照我的步骤进行,FPGA芯片为zynq7020的应该也可以。其余的根据自身芯片要求略微修改即可
根据前两章的讲解,相信大家已经完成了AD936x的脚本准备工作,这节将给大家讲解如果通过SPI把上一节生成的脚本文件,配置到AD9361上。
一、新建一个vivado工程
新建vivado硬件工程,然后分别把图中这几个文件代码导入工程中,
二、AD9361配置脚本文件
此文件就是上一节,通过AD936X Evaluation Software软件配置好的,脚本文件转化成Verilog的脚本文件,里面包含了所有的ad9361的寄存器配置信息。
三、ad9361接口文件
ad9361接口文件,此模块设计了采用LVDS传输模式的接口,接口设计清晰,可以嫁接到任何ad9361和其他FPGA开发板的接口连接,并且接口均为ad9361的接口,并没有其余无用的端口,移植起来非常方便。
通过设置adc_r1_mode
和dac_r1_mode
来选择单通道或双通道读写功能。这些配置选项允许您在单通道模式下只使用一个通道进行数据读写,或者在双通道模式下同时使用两个通道进行数据读写。
通过设置adc_r1_mode
和dac_r1_mode
,您可以根据系统需求选择合适的模式。在单通道模式下,您只需使用一个通道进行数据读取或写入,这可能在某些应用中更为简单和高效。而在双通道模式下,您可以同时使用两个通道进行数据读取和写入,这可能在一些需要同时处理多个信号的应用中更为有用。
代码:
module axi_ad9361_dev_if (
// 物理接口(接收)
input rx_clk_in_p,
input rx_clk_in_n,
input rx_frame_in_p, //每个数据包起始位置(n:下降 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,
// 数据时钟
output data_clk,
// 接收数据路径接口
output reg adc_valid,
output reg [11:0] adc_data_i1,
output reg [11:0] adc_data_q1,
output reg [11:0] adc_data_i2,
output reg [11:0] adc_data_q2,
output reg adc_status,
input adc_r1_mode,
// 发送数据路径接口
input dac_valid,
input [11:0] dac_data_i1,
input [11:0] dac_data_q1,
input [11:0] dac_data_i2,
input [11:0] dac_data_q2,
input dac_r1_mode
);
字
数
太
多
省
略
一
部
分
// 发送帧接口, oddr -> obuf
ODDR #(
.DDR_CLK_EDGE ("SAME_EDGE" ))
i_tx_frame_oddr (
.CE (1'b1 ),
.R (1'b0 ),
.S (1'b0 ),
.C (data_clk ),
.D1 (tx_frame ),
.D2 (tx_frame ),
.Q (tx_frame_oddr_s ));
OBUFDS i_tx_frame_obuf (
.I(tx_frame_oddr_s),
.O(tx_frame_out_p),
.OB(tx_frame_out_n)
);
// 发送时钟接口, oddr -> obuf (需要和data_clk,相同频率和占空比)
ODDR #(
.DDR_CLK_EDGE ("SAME_EDGE" ))
i_tx_clk_oddr (
.CE (1'b1 ),
.R (1'b0 ),
.S (1'b0 ),
.C (data_clk ),
.D1 (1'b0 ),
.D2 (1'b1 ),
.Q (tx_clk_oddr_s ));
OBUFDS i_tx_clk_obuf (
.I(tx_clk_oddr_s),
.O(tx_clk_out_p),
.OB(tx_clk_out_n)
);
endmodule
四、ad9361读取脚本文件
根据上一节的ad9361_lut.v脚本
文件,可以将读取脚本用状态机实现,分为三个状态为写状态、读状态和延时状态,并通过循环来反复执行这些状态,直到所有寄存器都被配置完毕。最后,可以将状态机设置为一个固定状态,并发出配置结束的标志信号。
为了确保正常操作,我建议将时钟频率设置为20MHz,并与SPI读写时钟保持一致。这样可以确保数据传输的稳定性和可靠性。
通过这种方式,您可以有效地配置AD9361芯片的寄存器,并在配置完成后获得一个明确的结束标志,以确保配置的正确性和完整性。
通过ad9361_config_writedata传输到SPI读写模块进行读写操作,
代码:
module ad9361_init(
input clk,
input rst_n,
output reg read,
output reg write,
output reg [9:0] address,
output reg [7:0] writedata,
input [7:0] readdata,
input waitrequest,
output reg chip_rst_n,
output [12:0] init_index,
output reg init_done
);
//`define MODELSIM
`define SPI_CLK_FREQ 20 //MHz
`include "ad9361_lut.v"
reg [12:0] index;
reg [2:0] state;
reg [31:0] delay_cnt;
reg [18:0] command;
assign init_index = index;
always @ (posedge clk) command <= ad9361_lut(index);
字
数
太
多
省
略
一
部
分
3'd4 : begin
index <= index + 1;
state <= state + 1;
end
3'd5 : begin
state <= state + 1;
end
3'd6 : begin
state <= 1;
end
3'd7 : begin
init_done <= 1;
end
default : state <= 0;
endcase
end
endmodule
五、SPI读写文件
基于上一节读取脚本文件读出的地址和数据,我们可以设计一个通用的SPI控制器,用于读写各种配置寄存器。该控制器采用三个状态机来实现不同的功能:开始传输、传输状态和传输完成。同时,我们可以使用一个always
语句来实时输入数据,实现对寄存器的配置。
这个通用SPI控制器具有以下特点:
- 灵活性:它可以适应不同的寄存器配置需求,根据脚本文件中的地址和数据选择相应的寄存器进行读写操作。
- 可重用性:由于其通用性,该控制器可以在不同的工程中被重复使用,以满足不同的寄存器配置需求。
- 状态机驱动:通过三个状态机(开始传输、传输状态和传输完成),控制器可以在不同的阶段执行相应的操作,以确保正确的数据传输和操作完成。
通过使用这个通用SPI控制器,您可以方便地配置各种寄存器,并在传输完成后获得相应的状态信号,以确保配置的成功和完整性。
代码:
module ad9361_spi(
input clk,
input rst_n,
//ad9361 interface
input read,
input write,
input [9:0] address,
input [7:0] writedata,
output reg [7:0] readdata,
output reg waitrequest,
//SPI interface
output spi_clk,
output reg spi_csn,
output reg spi_sdo,
input spi_sdi
);
reg [4:0] bit_cnt;
reg [23:0] command;
reg [1:0] state;
//SPI state
localparam START = 0,
TR = 1,
DONE = 2;
wire wr_rdn; //只写不读
assign wr_rdn = write && !read;
字
数
限
制
省
略
reg [7:0] readdata_shift = 0;
always @ (posedge clk)
begin
readdata_shift <= {readdata_shift[6:0], spi_sdi};
end
always @ (posedge clk)
begin
if (bit_cnt == 24 && read)begin
readdata <= {readdata_shift[6:0], spi_sdi};
end
end
endmodule
六、工程顶层的设计
需要注意完成配置后,dac_valid才拉高,然后把dac_data传给tx_data,保证传输数据的正确。防止数据进行错位。
代码:
module system_top(
//USER GPIO (PL GCLK 100MHz)
input pl_gclk,
output led7,
output reg led0,
input SW0,
//ad9361 spi
output spi_clk,
output spi_csn,
input spi_sdi,
output spi_sdo,
//ad9361 control
output en_agc,
output reg enable,
output reg txnrx,
output resetb,
output sync_in,
output [3:0] ctrl_in,
input [7:0] ctrl_out,
//ad9361 rx channel
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,
字
数
限
制
省
略
ad9361_spi ad9361_spi_0(
.clk ( gclk_div ),
.rst_n ( rstr ),
.read ( ad9361_config_read ),
.write ( ad9361_config_write ),
.address ( ad9361_config_address ),
.writedata ( ad9361_config_writedata ),
.readdata ( ad9361_config_readdata ),
.waitrequest ( ad9361_config_waitrequest ),
.spi_clk ( spi_clk ),
.spi_csn ( spi_csn ),
.spi_sdo ( spi_sdo ),
.spi_sdi ( spi_sdi )
);
endmodule
七、下板测试
把上述文件全部加入工程之后,然后分配管脚,进行编译和实现,产生bit流文件
AD9361应处在FDD工作状态,tx1和rx1单通道收发,收的频率2.4GHz,发的频率2.4GHz,为了测试方便本次设计产生一个稳定的正弦波信号,用频谱仪和ila分别测试,得出实验结果如下所示:
并且通过ila查看了每个读寄存器的值,均符合要求。
本次纯逻辑配置ad9361设计完成,更多设计后续继续更新,,,