AD9361纯逻辑控制从0到1连载0-SPI接口

前言
AD9361作为一款功能强大的射频收发器件,在通信领域被广泛采用。ADI官方提供的Demo主要基于ZYNQ的软件控制方式,这种控制方法的优点是将所有功能配置都封装为API函数,使用者不需要了解太多AD9361的具体细节,特别是其1024个寄存器的具体定义。这种方法适合于快速上手验证,但是在产品实现时有诸多不便,甚至某些场合根本无法使用。本连载基于实际的产品开发经验,介绍了如何用纯逻辑(verilog)的方式,实现AD9361从0到1的无线收发过程。

AD9361是干什么的?
AD9361纯逻辑控制从0到1连载0-SPI接口_第1张图片
以上这段摘自《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纯逻辑控制从0到1连载0-SPI接口_第2张图片
AD9361纯逻辑控制从0到1连载0-SPI接口_第3张图片
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的分享

你可能感兴趣的:(AD9361纯逻辑控制,SDR,AD9361/AD9363,fmcomms3,AD9361,PL,AD9361,SPI驱动)