/*******************************AD7606 SPI串行采样********************* --以下是米联客设计的AD7606 SPI串行采样驱动程序 --1.代码简洁,占用极少逻辑资源,代码结构清晰,逻辑设计严谨 --2.AD7606可以工作于并行模式,和串行模式,串行模式可以使用更少的IO --3.AD7606最高可以工作于200K 8通道同时采样,默认就是工作于200K 采样,SPI_DIV 和T5US_DIV根据不同的系统时钟需要正确设置 *********************************************************************/
`timescale 1ns / 1ns//仿真时间刻度/精度
module uispi7606 ( input I_ad_clk, //系统时钟输入 input I_ad_rst, //系统复位输入 input I_ad_busy, //ad7606 忙标志位 output [2:0] O_ad_os, //ad7606 过采样倍率选择,本驱动不使用 output O_ad_cs, //ad7606 CS信号输出,低电平SPI数据线输出AD7606寄存器数据 output reg O_ad_sclk, //ad7606 SCLK时钟输出 output O_ad_rst, //ad7606 复位输出 output O_ad_convsta, //ad7606 A组通道转换 output O_ad_convstb, //ad7606 B组通道转换 output O_ad_range, //ad7606 模拟输入范围,设置1范围:±10V,设置0范围±5V input I_O_ad_out_a, //串行A组通道采集输入,V1,V2,V3,V4 input I_O_ad_out_b, //串行B组通道采集输入, V5,V6,V7,V8 output reg [63:0] O_ad_out_a, //A组通道采集有效数据输出 output reg [63:0] O_ad_out_b, //B组通道采集有效数据输出 output O_ad_cap_en //采集完成使能 ); localparam CLK_FREQ = 100000000; localparam SET_5US = 5; //5us周期 localparam T5US_DIV = CLK_FREQ*SET_5US/1000000 -1; //计算5us 分频系数 localparam SPI_DIV = CLK_FREQ/16666666 -1 ;//产生SPI时钟,设置最高16.666667MHZ
assign O_ad_range = 1'b1; //±10V真直流输入范围 assign O_ad_os = 3'b000; //无过采样
//ad复位时间高电平,复位时间最少50ns reg [23: 0] rst_cnt; assign O_ad_rst = !rst_cnt[23]; always@(posedge I_ad_clk or posedge I_ad_rst)begin if(I_ad_rst) rst_cnt <= 24'd0; else if(!rst_cnt[23]) rst_cnt <= rst_cnt + 1'b1; end
//设置采样频率,AD7606为8通道可以工作于200Kbps采样率,因此采样周期为5us reg [9:0] tcnt5us; wire cycle_end = (tcnt5us == T5US_DIV); always@ (posedge I_ad_clk)begin if(O_ad_rst) tcnt5us <= 10'd0; else if(tcnt5us < T5US_DIV) tcnt5us <= tcnt5us + 1'b1; else tcnt5us <= 10'd0; end
localparam [9:0] SPI_DIV1 = SPI_DIV/2; //半周期分频 reg [9:0] clk_div = 10'd0;//分频计数器
//SPI 时钟分频,对于200K采样,设置20M时钟 always@(posedge I_ad_clk)begin if(clk_div < SPI_DIV) clk_div <= clk_div + 1'b1; else clk_div <= 10'd0; end
//产生SPI时钟 wire clk_en1 = (clk_div == SPI_DIV1);// 半周期使能,控制输出0 wire clk_en2 = (clk_div == SPI_DIV);// 半周期使能,控制输出1
always@(posedge I_ad_clk)begin if(clk_en2) O_ad_sclk <= 1'b1; //输出高电平 else if(clk_en1) O_ad_sclk <= 1'b0; //输出高低平 end
//AD转换状态机 reg [1:0] AD_S; reg ad_convst; //ADC转换控制信号,A组通道和B组通道采用同一通道 //ADC工作于SPI模式,以及边读边转换模式,本次数据转换的同时,可以读出前一次转换的结果 assign O_ad_cs = ~((AD_S == 2'd3)&&I_ad_busy);//当AD7606工作在串行模式,cs=0代表输出通过SPI数据总线,输出之前的采样数据 assign O_ad_convsta = ad_convst; //ADC转换控制信号,A组通道和B组通道采用同一通道 assign O_ad_convstb = ad_convst; //ADC转换控制信号,A组通道和B组通道采用同一通道
always @(posedge I_ad_clk) begin if(O_ad_rst||I_ad_rst)begin ad_convst <= 1'b1; AD_S <= 2'd0; end else begin case(AD_S) 2'd0:if(clk_en2)begin //clk_en2,控制SCLK输出1,因此这里相当于SCLK上升沿 ad_convst <= 1'b0; //设置ad_convst=0 AD_S <= 2'd1;//下一状态 end 2'd1:if(clk_en2)begin //延迟50ns,ad_convst低电平至少25ns,对于SCLK时钟是20MHZ,所以这里ad_convst低电平是50ns ad_convst <= 1'b1;//ad_convst 低电平50ns后拉高为高电平 AD_S <= 2'd2;//下一状态 end 2'd2:if(clk_en2&&I_ad_busy)//延迟50ns,并且等待busy高,只要ADC正常工作,busy必然为高,说明ADC处于采样转换下 AD_S <= 2'd3; 2'd3:if(cycle_end)//ADC转换时间最大4.2us,5us周期结束,代表本次8通道完全采集结束 AD_S <= 2'd0; //回到初始状态,进行下一次采样 endcase end end
//SPI采样 reg [7 : 0] nbits; // bits计数器,用于计数完成了从AD7606 SPI总线读出的bits数 wire ad_cap_en_r1 = (nbits==8'd64); //数据同步输出使能 reg ad_cap_en_r2 = 1'b0; //数据同步输出使能寄存一次 assign O_ad_cap_en = ({ad_cap_en_r1,ad_cap_en_r2}==2'b10);//高电平输出系统时钟的一个周期
always@(posedge I_ad_clk) begin //寄存一次ad_cap_en_r1 ad_cap_en_r2 <= ad_cap_en_r1; end
//当CS信号为低电平,每个SCL的下降沿输出采样数据,每组通道采样64bits wire cap_en = (!O_ad_cs)&&clk_en1&&(nbits<8'd64);
//ADC 串并转换模块 always@(posedge I_ad_clk) begin if(O_ad_rst) begin //ADC复位期间重置相关寄存器 O_ad_out_a <= 64'd0; O_ad_out_b <= 64'd0; nbits <= 8'd0; end else if(O_ad_cs)begin //高电平,重置 nbits nbits <= 8'd0; end else if(cap_en)begin//当CS信号为低电平,每个SCL的下降沿采样数据,每组通道采样64bits nbits <= nbits + 1'b1; // O_ad_out_a <= {O_ad_out_a[62:0],I_O_ad_out_a};//保存A组通道数据,V1~V4,每个通道16bits O_ad_out_b <= {O_ad_out_b[62:0],I_O_ad_out_b};//保存B组通道数据,V5~V8,每个通道16bits end end
endmodule |