前言
AD9361作为一款功能强大的射频收发器件,在通信领域被广泛采用。ADI官方提供的Demo主要基于ZYNQ的软件控制方式,这种控制方法的优点是将所有功能配置都封装为API函数,使用者不需要了解太多AD9361的具体细节,特别是其1024个寄存器的具体定义。这种方法适合于快速上手验证,但是在产品实现时有诸多不便,甚至某些场合根本无法使用。本连载基于实际的产品开发经验,介绍了如何用纯逻辑(verilog)的方式,实现AD9361从0到1的无线收发过程。
AD9361是干什么的?
以上这段摘自《AD9361_cn.pdf》
AD9361纯逻辑控制的开发方法
AD9361有1024个寄存器,要了解每个寄存器的功能再写代码,是个很费事费力的工作,并且不是每个寄存器都有文档支撑,完全靠阅读寄存器的文档,很难写出正确的初始化代码。好在ADI官方提供了AD936x Evaluation Software,用于生成初始化的脚本。这个脚本是ADI公司特有的脚本语法,不能直接拿过来用,这个时候就需要将这个脚本语言翻译成verilog语言,一条条执行每条命令,从而完成AD9361的初始化工作。在完成了初始化工作后,还需要配置射频频率、增益控制、发射衰减等参数、读PLL锁定标志等等。如果需要快速跳频工作,那还需要加入Profile的校准相关的代码。后面按照步骤一一介绍。
第一步:实现SPI通信
参考文件《AD9361 Interface Spec v2.5.pdf》
AD9361的控制接口是SPI的接口,属于串行控制总线,以下是SPI的时序图。
AD9361支持三线SPI和4线SPI接口,我这里用4线SPI接口。为了方便,首先要将其转换为并行总线,我这里转换成Avalon总线,下面贴出代码:
module ad9361_spi(
input clk,
input rst_n,
//avalon 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;
wire wr_rdn=write && !read;//read win over write
//spi_clk
assign spi_clk = clk;
//spi_sdo
always @ (posedge clk or negedge rst_n)
begin
if(~rst_n) begin
state<=0;
spi_csn<=1;
spi_sdo<=0;
bit_cnt<=0;
waitrequest<=1;
end
else begin
case(state)
0:begin
if(write|read) begin
state<=1;
bit_cnt<=0;
command<={wr_rdn,3'b000,2'b00,address,writedata};
end
end
1:begin
if(bit_cnt<=23) begin
spi_csn<=0;
spi_sdo<=command[23];
command<=command<<1;
bit_cnt<=bit_cnt+1;
end
else begin
spi_csn<=1;
spi_sdo<=0;
bit_cnt<=0;
state<=2;
waitrequest<=0;
end
end
2:begin waitrequest<=1;state<=0;end
default:state<=0;
endcase
end
end
//spi_sdi
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)
readdata<={readdata_shift[6:0],spi_sdi};
end
endmodule
第二步:实现Avalon总线仲裁,方便多个主控控制AD9361的SPI接口
我的代码中,有4个主控,分别是初始化主控,跳频校准主控,调试主控,参数配置主控,4个主控分时访问SPI接口。这么做的目的是方便切割模块,不然所有的功能代码揉到一起,不利于功能切割。代码可读性就会很差,也不利于维护。后面会一一介绍每个主控的作用。主控切换代码如下:
module avalon_mux #(parameter ADDR_WIDTH=256,DATA_WIDTH=256)
(
input clk,
input rst_n,
input s0_read,
input s0_write,
input [ADDR_WIDTH-1:0] s0_address,
input [DATA_WIDTH-1:0] s0_writedata,
output reg [DATA_WIDTH-1:0] s0_readdata,
output reg s0_waitrequest,
input s1_read,
input s1_write,
input [ADDR_WIDTH-1:0] s1_address,
input [DATA_WIDTH-1:0] s1_writedata,
output reg [DATA_WIDTH-1:0] s1_readdata,
output reg s1_waitrequest,
input s2_read,
input s2_write,
input [ADDR_WIDTH-1:0] s2_address,
input [DATA_WIDTH-1:0] s2_writedata,
output reg [DATA_WIDTH-1:0] s2_readdata,
output reg s2_waitrequest,
input s3_read,
input s3_write,
input [ADDR_WIDTH-1:0] s3_address,
input [DATA_WIDTH-1:0] s3_writedata,
output reg [DATA_WIDTH-1:0] s3_readdata,
output reg s3_waitrequest,
output reg m_read,
output reg m_write,
output reg [ADDR_WIDTH-1:0] m_address,
output reg [DATA_WIDTH-1:0] m_writedata,
input [DATA_WIDTH-1:0] m_readdata,
input m_waitrequest
);
reg [1:0] bus_sel;
reg [1:0] state;
wire [3:0] rd_wr = {
s3_read|s3_write,
s2_read|s2_write,
s1_read|s1_write,
s0_read|s0_write};
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n) begin
state<=0;m_read<=0;m_write<=0;bus_sel<=0;
end
else begin
case(state)
0:begin
if(rd_wr[bus_sel]) state<=1;else bus_sel<=bus_sel+1;
case(bus_sel)
0:begin
m_read<=s0_read;
m_write<=s0_write;
m_address<=s0_address;
m_writedata<=s0_writedata;
end
1:begin
m_read<=s1_read;
m_write<=s1_write;
m_address<=s1_address;
m_writedata<=s1_writedata;
end
2:begin
m_read<=s2_read;
m_write<=s2_write;
m_address<=s2_address;
m_writedata<=s2_writedata;
end
3:begin
m_read<=s3_read;
m_write<=s3_write;
m_address<=s3_address;
m_writedata<=s3_writedata;
end
endcase
end
1:begin
if(!m_waitrequest) begin
state<=2;
m_read<=0;
m_write<=0;
end
end
2:state<=0;
default:state<=0;
endcase
end
end
always @ (posedge clk)
begin
if(state==1 && bus_sel==0 && !m_waitrequest) s0_readdata<=m_readdata;
if(state==1 && bus_sel==1 && !m_waitrequest) s1_readdata<=m_readdata;
if(state==1 && bus_sel==2 && !m_waitrequest) s2_readdata<=m_readdata;
if(state==1 && bus_sel==3 && !m_waitrequest) s3_readdata<=m_readdata;
end
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n) begin
s0_waitrequest<=1;
s1_waitrequest<=1;
s2_waitrequest<=1;
s3_waitrequest<=1;
end
else begin
s0_waitrequest<=(state==1 && bus_sel==0 && !m_waitrequest);
s1_waitrequest<=(state==1 && bus_sel==1 && !m_waitrequest);
s2_waitrequest<=(state==1 && bus_sel==2 && !m_waitrequest);
s3_waitrequest<=(state==1 && bus_sel==3 && !m_waitrequest);
end
end
endmodule
到这里,我们实现了SPI的驱动以及多主控切换的功能。下一章,我会讲解如何用AD936x Evaluation Software生成初始化AD9361的配置脚本
链接:https://pan.baidu.com/s/1ZllL8FwZqQB3KavLxGuakA?pwd=7y0b
提取码:7y0b
–来自百度网盘超级会员V3的分享