基于PYNQ的AD采集系统

基于PYNQ的AD采集系统

  • 系统概述
  • AN706的控制
  • SPI通讯
  • AXI4-LITE总线
  • 打包IP核
  • 建立PYNQ工程
  • 编写SDK程序
  • 上板验证
  • 代码下载地址

系统概述

打算用PYNQ-Z2开发板做MMC变化器的控制,遇到的第一个问题就是做MMC控制需要采集大量的电容电压、电流信号。但是考虑到XILINX官方的PYNQ-Z2开发板管脚很少,所以使用另一块XLINX的FPGA(SPANTAN-6)接两片黑金的AN706AD进行数据采集,通过SPI协议把采集到的数据从SPI从机传输到位于PYNQ开发板上的SPI主机上。最后通过AXI4-Lite总线传输到ARM核上。系统的总图如下:
基于PYNQ的AD采集系统_第1张图片

AN706的控制

关于AN706AD的控制,这里直接使用黑金官方的例程就可以了,就是注意去理解其时序,如下图所示:
基于PYNQ的AD采集系统_第2张图片
直接上代码:

module ad7606(
	input          clk,
	input          rst_n,
        input [15:0]   ad_data,    
        input          ad_busy,    
        input          first_dat
        output [2:0]   ad_os,      
        output reg     ad_cs,      
        output reg     ad_rd,      
        output reg     ad_reset,   
        output reg     ad_convstab,
        output reg [15:0] ad_ch1,
        output reg [15:0] ad_ch2,
        output reg [15:0] ad_ch3,
        output reg [15:0] ad_ch4,
        output reg [15:0] ad_ch5,
        output reg [15:0] ad_ch6,
        output reg [15:0] ad_ch7,
        output reg [15:0] ad_ch8,
        output reg ad_finish
        );
reg [15:0]  cnt;           
reg [5:0] i;               
reg [3:0] state;           
                           
parameter IDLE=4'd0;       
parameter AD_CONV=4'd1;    
parameter Wait_1=4'd2;     
parameter Wait_busy=4'd3;  
parameter READ_CH1=4'd4;   
parameter READ_CH2=4'd5;   
parameter READ_CH3=4'd6;   
parameter READ_CH4=4'd7;   
parameter READ_CH5=4'd8;   
parameter READ_CH6=4'd9;   
parameter READ_CH7=4'd10;  
parameter READ_CH8=4'd11;  
parameter READ_DONE=4'd12; 
                           
assign ad_os=3'b000;
always@(posedge clk)      
 begin                    
    if(cnt<16'hffff) begin
        cnt<=cnt+1;       
        ad_reset<=1'b1;   
      end                 
      else                
        ad_reset<=1'b0;   
   end
always @(posedge clk)                       
 begin                                      
  if (ad_reset==1'b1) begin                 
    state<=IDLE;                            
    ad_ch1<=0;                              
    ad_ch2<=0;                              
    ad_ch3<=0;                              
    ad_ch4<=0;                              
    ad_ch5<=0;                              
    ad_ch6<=0;                              
    ad_ch7<=0;                              
    ad_ch8<=0;                              
    ad_cs<=1'b1;                            
    ad_rd<=1'b1;                            
    ad_convstab<=1'b1;                      
    i<=0;                                   
    ad_finish<=1'b0;                        
  end                                       
  else begin                                
    case(state)                             
    IDLE: begin                             
     ad_cs<=1'b1;                           
     ad_rd<=1'b1;                           
     ad_convstab<=1'b1;                     
     if(i==20) begin                        
      i<=0;                                 
      state<=AD_CONV;                       
     end                                    
     else                                   
      i<=i+1'b1;                            
    end                                     
    AD_CONV: begin                          
     if(i==2) begin                       
      i<=0;                                 
      state<=Wait_1;                        
      ad_convstab<=1'b1;                    
      ad_finish<=1'b0;                      
     end                                    
     else begin                             
      i<=i+1'b1;                            
      ad_convstab<=1'b0;                    
     end                                    
    end                                     
    Wait_1: begin                           
     if(i==5) begin                         
      i<=0;                                 
      state<=Wait_busy;                     
     end                                    
     else                                   
      i<=i+1'b1;                            
    end                                     
    Wait_busy: begin                        
     if(ad_busy==1'b0) begin                
      i<=0;                                 
      state<=READ_CH1;                      
     end                                    
    end                                     
    READ_CH1: begin                         
     ad_cs<=1'b0;                           
     if(i==3) begin                         
      ad_rd<=1'b1;                          
      i<=0;                                 
      ad_ch1<=ad_data;                      
      state<=READ_CH2;                      
     end                                    
     else begin                             
      ad_rd<=1'b0;                          
      i<=i+1'b1;                            
     end                                    
    end                                     
    READ_CH2: begin                         
     if(i==3) begin                         
      ad_rd<=1'b1;                          
      i<=0;                                 
      ad_ch2<=ad_data;                      
      state<=READ_CH3;                      
     end                                    
     else begin                             
      ad_rd<=1'b0;                          
      i<=i+1'b1;                            
     end                                    
    end                                     
    READ_CH3: begin                         
     if(i==3) begin                         
      ad_rd<=1'b1;                          
      i<=0;                                 
      ad_ch3<=ad_data;                      
      state<=READ_CH4;                      
     end                                    
     else begin                             
      ad_rd<=1'b0;                          
      i<=i+1'b1;                            
     end                                    
    end                                     
    READ_CH4: begin                         
     if(i==3) begin                         
      ad_rd<=1'b1;                          
      i<=0;                                 
      ad_ch4<=ad_data;                      
      state<=READ_CH5;                      
     end                                    
     else begin                             
      ad_rd<=1'b0;                          
      i<=i+1'b1;                            
     end                                    
    end                                     
    READ_CH5: begin                         
     if(i==3) begin                         
      ad_rd<=1'b1;                          
      i<=0;                                 
      ad_ch5<=ad_data;                      
      state<=READ_CH6;                      
     end                                    
     else begin                             
      ad_rd<=1'b0;                          
      i<=i+1'b1;                            
     end                                    
    end                                     
    READ_CH6: begin                         
     if(i==3) begin                         
      ad_rd<=1'b1;                          
      i<=0;                                 
      ad_ch6<=ad_data;                      
      state<=READ_CH7;                      
     end                                    
     else begin                             
      ad_rd<=1'b0;                          
      i<=i+1'b1;                            
     end                                    
    end                                     
    READ_CH7: begin                         
     if(i==3) begin                         
      ad_rd<=1'b1;                          
      i<=0;                                 
      ad_ch7<=ad_data;                      
      state<=READ_CH8;                      
     end                                    
     else begin                             
      ad_rd<=1'b0;                          
      i<=i+1'b1;                            
     end                                    
    end                                     
    READ_CH8: begin                         
     if(i==3) begin                         
      ad_rd<=1'b1;                          
      i<=0;                                 
      ad_ch8<=ad_data;                      
      state<=READ_DONE;                     
     end                                    
     else begin                             
      ad_rd<=1'b0;                          
      i<=i+1'b1;                            
     end                                    
    end                                     
    READ_DONE:begin                         
      ad_rd<=1'b1;                          
      ad_cs<=1'b1;                          
      state<=IDLE;                          
      ad_finish<=1'b1;                      
    end                                     
    default: state<=IDLE;                   
    endcase                                 
    end                                     
                                            
 end                                        
                                            
endmodule                                                       
       

这里在黑金官方的代码的基础上加入了ad_finish这个输出信号,目的是每次AD读完8个通道的值后告知SPI从机可以进行数据传输了。

SPI通讯

在两个FPGA之间的通讯,准备采用SPI模式,但是因为数据的流动方向只是从SPANTAN-6流向PYNQ-Z2,所以在这里对SPI通讯协议进行了修改。首先在传统的4线SPI模式中,去掉MOSI这根线,其次为了在传输时加大传输速度,把MISO的数据位宽改为16位,与AD输出的位宽相同。传输时遵从在时钟的下降沿从机准备好需要传输的数据,在时钟的上升沿主机采集数据。
下面直接上代码:

从机
//下降沿发送数据                                                                 
module SPI_SLAVE                                                          
(                                                                         
    input clk,                                                            
    input rst_n,                                                          
    input CS_N,                                                           
    input SCK,                                                            
    input tx_en,   //                                                     
    output reg [15:0]MISO,                                                
    input [16*16-1:0] txd_data                                            
);                                                                        
//---------------------spi_slaver send data---------------------------    
reg [3:0] txd_state;                                                      
always@(negedge SCK or negedge rst_n)                                     
begin                                                                     
    if(!rst_n)                                                            
        begin                                                             
            txd_state <= 1'b0;                                            
            MISO<=0;                                                      
        end                                                               
    else if(!CS_N)                                                        
        begin                                                             
            case(txd_state)                                               
                4'd0:begin                                                
                        MISO <= txd_data[255:240];                        
                        txd_state <= 4'd1;                                
                      end                                                 
                4'd1:begin                                                
                        MISO <= txd_data[239:224];                        
                        txd_state <= 4'd2;                                
                      end                                                 
                4'd2:begin                                                
                        MISO <= txd_data[223:208];                        
                        txd_state <= 4'd3;                                
                      end                                                 
                4'd3:begin                                                
                        MISO <= txd_data[207:192];                        
                        txd_state <= 4'd4;                                
                      end                                                 
                4'd4:begin                                                
                        MISO <= txd_data[191:176];                        
                        txd_state <= 4'd5;                                
                      end                                                 
                4'd5:begin                                                
                        MISO <= txd_data[175:160];                        
                        txd_state <= 4'd6;                                
                      end                                                 
                4'd6:begin                                                
                        MISO <= txd_data[159:144];                        
                        txd_state <= 4'd7;                                
                      end                                                 
                4'd7:begin                                                
                        MISO <= txd_data[143:128];                        
                        txd_state <= 4'd8;                                
                      end                                                 
                4'd8:begin                                                
                        MISO <= txd_data[127:112];                        
                        txd_state <= 4'd9;                                
                      end                                                 
                4'd9:begin                                                
                        MISO <= txd_data[111:96];                         
                        txd_state <= 4'd10;                               
                      end                                                 
                4'd10:begin                                               
                        MISO <= txd_data[95:80];                          
                        txd_state <= 4'd11;                               
                      end                                                 
                4'd11:begin                                               
                        MISO <= txd_data[79:64];                          
                        txd_state <= 4'd12;                               
                      end                                                 
                4'd12:begin                                               
                        MISO <= txd_data[63:48];                          
                        txd_state <= 4'd13;                               
                      end                                                 
                4'd13:begin                                               
                        MISO <= txd_data[47:32];                          
                        txd_state <= 4'd14;                               
                      end                                                 
                4'd14:begin                                               
                        MISO <= txd_data[31:16];                          
                        txd_state <= 4'd15;                               
                      end                                                 
                4'd15:begin                                               
                        MISO <= txd_data[15:0];                           
                        txd_state <= 4'd0;                                
                      end                                                 
                default: ;                                                
            endcase                                                       
        end                                                               
end                                                                       
                                                                          
endmodule                                                                 
主机
//上升沿发送数据                                                                                               
module SPI_MASTER                                                                                       
(                                                                                                       
    input clk,//100M                                                                                    
    input rst_n,                                                                                        
    input rx_en,                                                                                        
    output rx_done,                                                                                     
    output reg [16*16-1:0]data_out,                                                                     
                                                                                                        
    input [15:0]MISO,                                                                                   
    output reg SCK,                                                                                     
    output reg CS_N                                                                                     
);                                                                                                      
                                                                                                        
reg [6:0]i;                                                                                             
reg rx_en_r;                                                                                            
reg rx_done_r;                                                                                          
reg [5:0]rxd_state;                                                                                     
always @(posedge clk or negedge rst_n)                                                                  
if(~rst_n)                                                                                              
begin                                                                                                   
    rxd_state<=5'b0;                                                                                    
    CS_N<=1'b1;                                                                                         
    SCK<=1'b1;                                                                                          
    rx_done_r<=1'b0;                                                                                    
    data_out<=0;                                                                                        
    i<=7'b0;                                                                                            
end                                                                                                     
else                                                                                                    
begin                                                                                                   
    if(rx_en_r)                                                                                         
    begin                                                                                               
        CS_N<=1'b0;                                                                                     
        case(rxd_state)                                                                                 
            6'd0,6'd2,6'd4,6'd6,6'd8,6'd10,6'd12,6'd14,6'd16,6'd18,6'd20,6'd22,6'd24,6'd26,6'd28,6'd30: 
            begin                                                                                       
                if(i==100)                                                                              
                begin                                                                                   
                    SCK<=1'b0;                                                                          
                    i<=0;                                                                               
                    rxd_state<=rxd_state+1'b1;                                                          
                end                                                                                     
                else                                                                                    
                    i<=i+1;                                                                             
                    rx_done_r<=1'b0;                                                                    
            end                                                                                         
            6'd1://接收第15位                                                                               
            begin                                                                                       
                if(i==100)                                                                              
                begin                                                                                   
                    SCK<=1'b1;                                                                          
                    rxd_state<=rxd_state+1'b1;                                                          
                    rx_done_r<=1'b0;                                                                    
                    data_out[255:240]<=MISO;                                                            
                    i<=0;                                                                               
                end                                                                                     
                else                                                                                    
                    i<=i+1;                                                                             
            end                                                                                         
            6'd3://接收第14位                                                                               
            begin                                                                                       
                if(i==100)                                                                              
                begin                                                                                   
                    SCK<=1'b1;                                                                          
                    rxd_state<=rxd_state+1'b1;                                                          
                    rx_done_r<=1'b0;                                                                    
                    data_out[239:224]<=MISO;                                                            
                    i<=0;                                                                               
                end                                                                                     
                else                                                                                    
                    i<=i+1;                                                                             
            end                                                                                         
            6'd5://接收第13位                                                                               
            begin                                                                                       
                if(i==100)                                                                              
                begin                                                                                   
                    SCK<=1'b1;                                                                          
                    rxd_state<=rxd_state+1'b1;                                                          
                    rx_done_r<=1'b0;                                                                    
                    data_out[223:208]<=MISO;                                                            
                    i<=0;                                                                               
                end                                                                                     
                else                                                                                    
                    i<=i+1;                                                                             
            end                                                                                         
            6'd7://接收第12位                                                                               
            begin                                                                                       
                if(i==100)                                                                              
                begin                                                                                   
                    SCK<=1'b1;                                                                          
                    rxd_state<=rxd_state+1'b1;                                                          
                    rx_done_r<=1'b0;                                                                    
                    data_out[207:192]<=MISO;                                                            
                    i<=0;                                                                               
                end                                                                                     
                else                                                                                    
                    i<=i+1;                                                                             
            end                                                                                         
            6'd9://接收第11位                                                                               
            begin                                                                                       
                if(i==100)                                                                              
                begin                                                                                   
                    SCK<=1'b1;                                                                          
                    rxd_state<=rxd_state+1'b1;                                                          
                    rx_done_r<=1'b0;                                                                    
                    data_out[191:176]<=MISO;                                                            
                    i<=0;                                                                               
                end                                                                                     
                else                                                                                    
                    i<=i+1;                                                                             
            end                                                                                         
            6'd11://接收第10位                                                                              
            begin                                                                                       
                if(i==100)                                                                              
                begin                                                                                   
                    SCK<=1'b1;                                                                          
                    rxd_state<=rxd_state+1'b1;                                                          
                    rx_done_r<=1'b0;                                                                    
                    data_out[175:160]<=MISO;                                                            
                    i<=0;                                                                               
                end                                                                                     
                else                                                                                    
                    i<=i+1;                                                                             
            end                                                                                         
            6'd13://接收第9位                                                                               
            begin                                                                                       
                if(i==100)                                                                              
                    begin                                                                               
                    SCK<=1'b1;                                                                          
                    rxd_state<=rxd_state+1'b1;                                                          
                    rx_done_r<=1'b0;                                                                    
                    data_out[159:144]<=MISO;                                                            
                    i<=0;                                                                               
                    end                                                                                 
                else                                                                                    
                    i<=i+1;                                                                             
            end                                                                                         
            6'd15://接收第8位                                                                               
            begin                                                                                       
                if(i==100)                                                                              
                begin                                                                                   
                    SCK<=1'b1;                                                                          
                    rxd_state<=rxd_state+1'b1;                                                          
                    rx_done_r<=1'b0;                                                                    
                    data_out[143:128]<=MISO;                                                            
                    i<=0;                                                                               
                end                                                                                     
                else                                                                                    
                    i<=i+1;                                                                             
            end                                                                                         
            6'd17://接收第7位                                                                               
            begin                                                                                       
                if(i==100)                                                                              
                begin                                                                                   
                    SCK<=1'b1;                                                                          
                    rxd_state<=rxd_state+1'b1;                                                          
                    rx_done_r<=1'b0;                                                                    
                    data_out[127:112]<=MISO;                                                            
                    i<=0;                                                                               
                end                                                                                     
                else                                                                                    
                    i<=i+1;                                                                             
            end                                                                                         
            6'd19://接收第6位                                                                               
            begin                                                                                       
                if(i==100)                                                                              
                begin                                                                                   
                    SCK<=1'b1;                                                                          
                    rxd_state<=rxd_state+1'b1;                                                          
                    rx_done_r<=1'b0;                                                                    
                    data_out[111:96]<=MISO;                                                             
                    i<=0;                                                                               
                end                                                                                     
                else                                                                                    
                    i<=i+1;                                                                             
            end                                                                                         
            6'd21://接收第5位                                                                               
            begin                                                                                       
                if(i==100)                                                                              
                begin                                                                                   
                    SCK<=1'b1;                                                                          
                    rxd_state<=rxd_state+1'b1;                                                          
                    rx_done_r<=1'b0;                                                                    
                    data_out[95:80]<=MISO;                                                              
                    i<=0;                                                                               
                end                                                                                     
                else                                                                                    
                    i<=i+1;                                                                             
            end                                                                                         
            6'd23://接收第4位                                                                               
            begin                                                                                       
                if(i==100)                                                                              
                begin                                                                                   
                    SCK<=1'b1;                                                                          
                    rxd_state<=rxd_state+1'b1;                                                          
                    rx_done_r<=1'b0;                                                                    
                    data_out[79:64]<=MISO;                                                              
                    i<=0;                                                                               
                end                                                                                     
                else                                                                                    
                    i<=i+1;                                                                             
            end                                                                                         
            6'd25://接收第3位                                                                               
            begin                                                                                       
                if(i==100)                                                                              
                begin                                                                                   
                    SCK<=1'b1;                                                                          
                    rxd_state<=rxd_state+1'b1;                                                          
                    rx_done_r<=1'b0;                                                                    
                    data_out[63:48]<=MISO;                                                              
                    i<=0;                                                                               
                end                                                                                     
                else                                                                                    
                    i<=i+1;                                                                             
            end                                                                                         
            6'd27://接收第2位                                                                               
            begin                                                                                       
                if(i==100)                                                                              
                begin                                                                                   
                    SCK<=1'b1;                                                                          
                    rxd_state<=rxd_state+1'b1;                                                          
                    rx_done_r<=1'b0;                                                                    
                    data_out[47:32]<=MISO;                                                              
                    i<=0;                                                                               
                end                                                                                     
                else                                                                                    
                    i<=i+1;                                                                             
            end                                                                                         
            6'd29://接收第1位                                                                               
            begin                                                                                       
                if(i==100)                                                                              
                begin                                                                                   
                    SCK<=1'b1;                                                                          
                    rxd_state<=rxd_state+1'b1;                                                          
                    rx_done_r<=1'b0;                                                                    
                    data_out[31:16]<=MISO;                                                              
                    i<=0;                                                                               
                end                                                                                     
                else                                                                                    
                    i<=i+1;                                                                             
            end                                                                                         
            6'd31://接收第0位                                                                               
            begin                                                                                       
                if(i==100)                                                                              
                begin                                                                                   
                    SCK<=1'b1;                                                                          
                    rxd_state<=rxd_state+1;                                                             
                    rx_done_r<=1'b1;                                                                    
                    data_out[15:0]<=MISO;                                                               
                    i<=0;                                                                               
                end                                                                                     
                else                                                                                    
                    i<=i+1;                                                                             
            end                                                                                         
            6'd32:                                                                                      
            begin                                                                                       
                SCK<=1'b1;                                                                              
                rxd_state<=rxd_state+1'b1;                                                              
                rx_done_r<=1'b1;                                                                        
            end                                                                                         
            default:                                                                                    
            begin                                                                                       
                rxd_state<=6'd0;                                                                        
            end                                                                                         
        endcase                                                                                         
    end                                                                                                 
end                                                                                                     
                                                                                                        
//上升沿检测                                                                                                 
reg btn1;                                                                                               
reg btn2;                                                                                               
always @(posedge clk or negedge rst_n)                                                                  
if(~rst_n)                                                                                              
begin                                                                                                   
   btn1 <= 1'b0;                                                                                        
   btn2 <= 1'b0;                                                                                        
end                                                                                                     
else                                                                                                    
begin                                                                                                   
   btn1  <= rx_done;                                                                                    
   btn2  <= btn1;                                                                                       
end                                                                                                     
wire done=btn1 & ~btn2;                                                                                 
                                                                                                        
                                                                                                        
always @(posedge clk or negedge rst_n)                                                                  
if(~rst_n)                                                                                              
    rx_en_r<=1'b0;                                                                                      
else                                                                                                    
    if(rx_en)                                                                                           
        rx_en_r<=1'b1;                                                                                  
    else                                                                                                
        if(done)                                                                                        
            rx_en_r<=1'b0;                                                                              
                                                                                                        
assign rx_done=rx_done_r;                                                                               

因为偶数状态的逻辑都一样,所以可以合并起来。这里为了方便ARM核通过AXI4总线对主机SPI模块进行控制和读取,加入了rx_en和rx_done两个信号。当rx_en置1时,启动SPI传输。当rx_done置1时,表示SPI传输完成,有了这两个信号就可以很方便的使用ARM的定时器定时读取AD值。具体原理可以结合仿真时序进行理解。

AXI4-LITE总线

在基于zynq架构的Xilinx的FPGA上,PS(ARM核)和PL(可编程逻辑部分)的通讯都是基于AXI4总线的。因为要传输的数据量不大,就16个寄存器中的数据(分别对应于16个AD通道),所以就使用了AXI4-LITE总线,而没有使用AXI4-HP总线。这个总线总结起来就是地址先达,数据随后。就是不管是读寄存器还是写寄存器都要先送地址,然后才能读(写)数据。

具体的总线原理可以参考这篇文章:AXI4总线。需要注意的是LITE总线是不支持突发传输的。

下面直接上基于AXI4-LITE总线的AD寄存器代码:

module axi_reg                                                                        
(                                                                                     
    input clk,                                                                        
    input rst_n,                                                                      
                                                                                      
    //AR channel                                                                      
    input S_AXI_ARVALID,                                                              
    output S_AXI_ARREADY,                                                             
    input [6:0]S_AXI_ARADDR,                                                          
    input [2:0]S_AXI_ARPROT,                                                          
                                                                                      
    //Rd channel                                                                      
    output [32-1:0]S_AXI_RDATA,                                                       
    output [1:0]S_AXI_RRESP,                                                          
    output S_AXI_RVALID,                                                              
    input S_AXI_RREADY,                                                               
                                                                                      
    //AW channel                                                                      
    input S_AXI_AWVALID,                                                              
    output S_AXI_AWREADY,                                                             
    input [6:0]S_AXI_AWADDR,                                                          
    input [2:0]S_AXI_AWPROT,                                                          
                                                                                      
    //Wr channel                                                                      
    input [32-1:0]S_AXI_WDATA,                                                        
    input S_AXI_WVALID,                                                               
    output S_AXI_WREADY,                                                              
    input [4-1:0]S_AXI_WSTRB,                                                         
                                                                                      
    //Wr Resp                                                                         
    output [1:0]S_AXI_BRESP,                                                          
    output S_AXI_BVALID,                                                              
    input S_AXI_BREADY,                                                               
                                                                                      
    //from ad fifo                                                                    
    input [16*16-1:0]ad_data,                                                         
    input data_vld,                                                                   
                                                                                      
    //cmd to SPI_master                                                               
    input rx_done,   //最好放在一个叫做done 的寄存器里                                             
    output rx_en     //rx_en=start                                                    
);                                                                                    
                                                                                      
//内部寄存器                                                                               
             //start reg0                                                             
reg done_r;       //reg1                                                              
reg [15:0]ad1;    //reg2                                                              
reg [15:0]ad2;    //reg3                                                              
reg [15:0]ad3;    //reg4                                                              
reg [15:0]ad4;    //reg5                                                              
reg [15:0]ad5;    //reg6                                                              
reg [15:0]ad6;    //reg7                                                              
reg [15:0]ad7;    //reg8                                                              
reg [15:0]ad8;    //reg9                                                              
reg [15:0]ad9;    //reg10                                                             
reg [15:0]ad10;   //reg11                                                             
reg [15:0]ad11;   //reg12                                                             
reg [15:0]ad12;   //reg13                                                             
reg [15:0]ad13;   //reg14                                                             
reg [15:0]ad14;   //reg15                                                             
reg [15:0]ad15;   //reg16                                                             
reg [15:0]ad16;   //reg17                                                             
                                                                                      
wire start;                                                                           
                                                                                      
always @(posedge clk or negedge rst_n)                                                
if(~rst_n)                                                                            
begin                                                                                 
    done_r<=0;                                                                        
    ad1<=0;                                                                           
    ad2<=0;                                                                           
    ad3<=0;                                                                           
    ad4<=0;                                                                           
    ad5<=0;                                                                           
    ad6<=0;                                                                           
    ad7<=0;                                                                           
    ad8<=0;                                                                           
    ad9<=0;                                                                           
    ad10<=0;                                                                          
    ad11<=0;                                                                          
    ad12<=0;                                                                          
    ad13<=0;                                                                          
    ad14<=0;                                                                          
    ad15<=0;                                                                          
    ad16<=0;                                                                          
end                                                                                   
else                                                                                  
begin                                                                                 
    done_r<=rx_done;                                                                  
    if(data_vld)                                                                      
    begin                                                                             
        ad1<=ad_data[15:0];                                                           
        ad2<=ad_data[31:16];                                                          
        ad3<=ad_data[47:32];                                                          
        ad4<=ad_data[63:48];                                                          
        ad5<=ad_data[79:64];                                                          
        ad6<=ad_data[95:80];                                                          
        ad7<=ad_data[111:96];                                                         
        ad8<=ad_data[127:112];                                                        
        ad9<=ad_data[143:128];                                                        
        ad10<=ad_data[159:144];                                                       
        ad11<=ad_data[175:160];                                                       
        ad12<=ad_data[191:176];                                                       
        ad13<=ad_data[207:192];                                                       
        ad14<=ad_data[223:208];                                                       
        ad15<=ad_data[239:224];                                                       
        ad16<=ad_data[255:240];                                                       
    end                                                                               
end                                                                                   
/**********************************AXI4_LITE****************************************/ 
//写应答                                                                                 
assign S_AXI_BRESP=2'b0;                                                              
reg axi_bvalid;                                                                       
assign S_AXI_BVALID=axi_bvalid;                                                       
always @(posedge clk or negedge rst_n)                                                
if(~rst_n)                                                                            
    axi_bvalid<=1'b0;                                                                 
else                                                                                  
    if(S_AXI_WVALID & S_AXI_WREADY)                                                   
        axi_bvalid<=1'b1;                                                             
    else                                                                              
        if(S_AXI_BREADY)                                                              
            axi_bvalid<=1'b0;                                                         
//写地址                                                                                 
reg [4:0]addr_word_w;                                                                 
wire [4:0]addr_word_w_comb;                                                           
always @(posedge clk or negedge rst_n)                                                
if(~rst_n)                                                                            
    addr_word_w<=0;                                                                   
else                                                                                  
    if(S_AXI_AWVALID & S_AXI_AWREADY)                                                 
        addr_word_w<=S_AXI_AWADDR[6:2];                                               
                                                                                      
assign addr_word_w_comb=(S_AXI_AWVALID & S_AXI_AWREADY)?S_AXI_AWADDR[6:2]:addr_word_w;
assign S_AXI_AWREADY=1'b1;//S_AXI_AWVALID&S_AXI_WVALID;                               
//保障写完地址后再写数据                                                                         
reg w_phase;                                                                          
always @(posedge clk or negedge rst_n)                                                
if(~rst_n)                                                                            
    w_phase<=1'b0;                                                                    
else                                                                                  
    if(S_AXI_AWVALID & S_AXI_AWREADY)                                                 
        w_phase<=1;                                                                   
    else                                                                              
        if(S_AXI_WVALID & S_AXI_WREADY)                                               
            w_phase<=0;                                                               
                                                                                      
assign S_AXI_WREADY=w_phase;                                                          
//读地址,数据                                                                              
assign S_AXI_ARREADY=1'b1;                                                            
                                                                                      
assign S_AXI_RRESP=2'b0;                                                              
reg [32-1:0]rdata;                                                                    
assign S_AXI_RDATA=rdata;                                                             
reg rvalid;                                                                           
assign S_AXI_RVALID=rvalid;                                                           
always @(posedge clk or negedge rst_n)                                                
if(~rst_n)                                                                            
begin                                                                                 
    rvalid<=1'b0;                                                                     
    rdata<=32'b0;                                                                     
end                                                                                   
else                                                                                  
    if(S_AXI_ARVALID & S_AXI_ARREADY)                                                 
    begin                                                                             
 rvalid<=1'b1;                                                                        
 case(S_AXI_ARADDR[6:2])                                                             
   1:rdata<={31'b0,done_r};                                                           
   2:rdata<={31'b0,ad1};                                                              
   3:rdata<={31'b0,ad2};                                                              
   4:rdata<={31'b0,ad3};                                                              
   5:rdata<={31'b0,ad4};                                                              
   6:rdata<={31'b0,ad5};                                                              
   7:rdata<={31'b0,ad6};                                                              
   8:rdata<={31'b0,ad7};                                                              
   9:rdata<={31'b0,ad8};                                                              
   10:rdata<={31'b0,ad9};                                                             
   11:rdata<={31'b0,ad10};                                                            
   12:rdata<={31'b0,ad11};                                                            
   13:rdata<={31'b0,ad12};                                                            
   14:rdata<={31'b0,ad13};                                                            
   15:rdata<={31'b0,ad14};                                                            
   16:rdata<={31'b0,ad15};                                                            
   17:rdata<={31'b0,ad16};                                                            
   default:rdata<=32'b0;                                                              
 endcase                                                                              
    end                                                                               
    else                                                                              
       if(S_AXI_RVALID&S_AXI_RREADY)                                                  
   rvalid<=1'b0;                                                                      
                                                                                      
assign start=S_AXI_WVALID & S_AXI_WREADY & (addr_word_w_comb==0) & S_AXI_WDATA[0];    
assign rx_en=start;                                                                   
endmodule                                                                             

这里在设计时每个寄存器都设计为了32位的,而AD数据只有16位的,所以需要对高位补零。这么设计的原因主要是为了方便地利用地址的[6:2]位确定到底是哪个寄存器(地址的1位,对应于数据的16位),方便SDK软件程序的编写。

打包IP核

需要注意的是需要在AD寄存器模块和SPI主机模块之间加一个FIFO进行数据缓存,FIFO的深度设置为1即可。FIFO采用同步FIFO,且输出输入端口利用握手信号(data_in_rdy,data_in_vld,data_out_rdy,data_out_vld)进行控制。其中data_in_rdy直接置1,data_in_vld由rx_done控制,data_out_rdy直接置1,data_out_vld接到axi_reg模块的data_vld。

最后编写顶层模块(AD),打包成IP核(AD_0)。在顶层模块中需要例化SPI_MASTER、fifo、axi_reg模块。如何打包自定义的IP核可以参考这里。需要注意的是前面的代码已经满足AXI4总线的规范,所以可以自动生成AXI4接口,不需要其他操作。

建立PYNQ工程

建立好的PYNQ系统图如图所示:
基于PYNQ的AD采集系统_第3张图片
如何建工程可以参考这里。
注意,把比特流文件和硬件文件导入Jupyter Notebook就可以使用PYTHON编写驱动和算法了,但是本文为了方便,使用SDK裸机开发。

编写SDK程序

#include                                 
#include "platform.h"                             
#include "xil_printf.h"                           
#include "xparameters.h"                          
#include "xil_io.h"                               
                                                  
#define AD_BASEADDR XPAR_AD_0_BASEADDR            
                                                  
u32 *rdata;                                       
u32 *rdata1;                                      
u32 *rdata2;                                      
u32 *rdata3;                                      
u32 *rdata4;                                      
u32 *rdata5;                                      
u32 *rdata6;                                      
u32 *rdata7;                                      
u32 *rdata8;                                      
u32 *rdata9;                                      
u32 *rdata10;                                     
u32 *rdata11;                                     
u32 *rdata12;                                     
u32 *rdata13;                                     
u32 *rdata14;                                     
u32 *rdata15;                                     
u32 *rdata16;                                     
                                                  
void wirte_ad(int reg, u32 data)                  
{                                                 
 Xil_Out32(AD_BASEADDR+reg*4, data);             
}                                                 
                                                  
void read_ad(int reg, u32 *data)                  
{                                                 
 u32 x;                                          
 float y;                                        
 *data=Xil_In32(AD_BASEADDR+reg*4);              
 x=*data&0x7fff;//提取低15位的数据位,16位为符号位
 y=(float)(x)*5/32758;                           
 printf("read reg%d,data:%f\r\n",reg,y);         
}                                                 
                                                  
void read_ad_reg1(int reg, u32 *data)             
{                                                 
 *data=Xil_In32(AD_BASEADDR+reg*4);              
}                                                 
                                                  
                                                  
int main()                                        
{                                                 
    init_platform();                              
                                                  
    print("Hello World\n\r");                     
    while(1)                                      
    {                                             
                                                  
    //kick of run                                 
    wirte_ad(0,1);                                
    read_ad_reg1(1,rdata);                        
    while(*rdata!=1)//等待SPI传输完成                              
    {                                             
     read_ad_reg1(1,rdata);                      
    }                                             
                                                  
    read_ad(2,rdata1);                            
    read_ad(3,rdata2);                            
    read_ad(4,rdata3);                            
    read_ad(5,rdata4);                            
    read_ad(6,rdata5);                            
    read_ad(7,rdata6);                            
    read_ad(8,rdata7);                            
    read_ad(9,rdata8);                            
    read_ad(10,rdata9);                           
    read_ad(11,rdata10);                          
    read_ad(12,rdata11);                          
    read_ad(13,rdata12);                          
    read_ad(14,rdata13);                          
    read_ad(15,rdata14);                          
    read_ad(16,rdata15);                          
    read_ad(17,rdata16);                          
                                                  
    }                                             
                                                  
    cleanup_platform();                           
    return 0;                                     
}                                                 

上板验证

注意,在两块FPGA烧写好程序后,需要对从机的FPGA进行复位(按一下rst_n信号连接的那个按键)才可以进行可靠传输。
演示图如图所示:
基于PYNQ的AD采集系统_第4张图片
把3.3V接到了第一片AD的第6输入通道,可以看到对应的串口输出的reg7的值为3.3V,实验成功。(reg1为SPI的done信号,reg2存放第一片AD第一个通道的值,所以第一片AD的第6输入通道的值存放在reg7)注意:AD的悬空电压为1.7V.
基于PYNQ的AD采集系统_第5张图片

代码下载地址

https://download.csdn.net/download/qq_34329669/11975542

你可能感兴趣的:(PYNQ)