系统概述
打算用PYNQ-Z2开发板做MMC变化器的控制,遇到的第一个问题就是做MMC控制需要采集大量的电容电压、电流信号。但是考虑到XILINX官方的PYNQ-Z2开发板管脚很少,所以使用另一块XLINX的FPGA(SPANTAN-6)接两片黑金的AN706AD进行数据采集,通过SPI协议把采集到的数据从SPI从机传输到位于PYNQ开发板上的SPI主机上。最后通过AXI4-Lite总线传输到ARM核上。系统的总图如下:
AN706的控制
关于AN706AD的控制,这里直接使用黑金官方的例程就可以了,就是注意去理解其时序,如下图所示:
直接上代码:
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