素材来源 米联客
一、AD7606简介
功能框图:
转换控制时序:
AD7606 支持 2 种时序转换, 由于我们采用的时串行 SPI 模式, 本身 SPI 读取数据就会耽误很多时间, 所以必须采用第二种工作时序, 才能确保 200Kbps 的采样率。
空闲SCLK为高,CPOL为1 在SCLK第一个上升沿进行数据读取,CPHA为1
当数据完成转换后, CS 为电平期间, 每个时钟的上升沿完成数据的采样, 表中 FRSTDATA 代表每次转换的第一个数据, 这里实际可以不用这个信号, 因为每次 CS 之后的第一个上升沿时钟我们就是开始采样数据, 每 16 次完成一路 ADC 的采集。
对于 AD7606 具有 8 路 ADC, DOUTA 和 DOUTB 各 4 路。 DOUTA 对于 1~4 路 ADC 通道, DOUTB 对应 5~8路 ADC 通道。而且 ADC 的 2 组 4 个通道可以一次性完成采集, 也就是说 16X4 共计 64 个时钟上升沿完成 4 路 ADC采集。
引脚说明:
模式选择:
参数说明:
串行读取:
二、硬件设计
来源:中瑞科技
三、代码编写
SPI驱动部分:
`timescale 1ns / 1ps
module spi7606(
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_ad_out_a , //串行A组通道采集输入,V1,V2,V3,V4
input I_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) begin
rst_cnt <= 24'd0;
end
else if(!rst_cnt[23]) begin
rst_cnt <= rst_cnt + 1'b1;
end
end
//设置采样频率,AD7606为8通道可以工作于200Kbps采样率,因此采样周期为5us 1KSPS=1KHz,1MSPS=1MHz
reg [9:0] tcnt5us;
wire cycle_end = (tcnt5us == T5US_DIV);
always@ (posedge I_ad_clk)begin
if(O_ad_rst) begin
tcnt5us <= 10'd0;
end
else if(tcnt5us < T5US_DIV) begin
tcnt5us <= tcnt5us + 1'b1;
end
else begin
tcnt5us <= 10'd0;
end
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) begin
clk_div <= clk_div + 1'b1;
end
else begin
clk_div <= 10'd0;
end
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) begin
O_ad_sclk <= 1'b1; //输出高电平
end
else if(clk_en1) begin
O_ad_sclk <= 1'b0; //输出高低平
end
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)begin//延迟50ns,并且等待busy高,只要ADC正常工作,busy必然为高,说明ADC处于采样转换下
AD_S <= 2'd3;
end
2'd3:if(cycle_end)begin//ADC转换时间最大4.2us,5us周期结束,代表本次8通道完全采集结束
AD_S <= 2'd0; //回到初始状态,进行下一次采样
end
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_ad_out_a};//保存A组通道数据,V1~V4,每个通道16bits
O_ad_out_b <= {O_ad_out_b[62:0],I_ad_out_b};//保存B组通道数据,V5~V8,每个通道16bits
end
end
endmodule
顶层状态转移:
`timescale 1ns / 1ns
module ad7606_top
(
input I_sysclk, //系统时钟输入
input I_ad_busy, //ad7606 忙标志位
output O_ad_cs, //ad7606 CS信号输出,低电平SPI数据线输出AD7606寄存器数据
output 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_ad_out_a, //串行A组通道采集输入,V1,V2,V3,V4
input I_ad_out_b, //串行B组通道采集输入, V5,V6,V7,V8
output O_card_power_en //子卡电源使能
);
assign O_card_power_en = 1'b1; //子卡上电
wire clk100M,locked;
clk_wiz_0 clk_7606_inst(.clk_out1(clk100M),.locked(locked),.clk_in1(I_sysclk));
//ad7606 ip相关信号
wire ad_clk_i,ad_rst_i,ad_cap_en;
wire [63:0] ad_out_a,ad_out_b;
assign ad_clk_i = clk100M; //ADC时钟
assign ad_rst_i = !locked; //ADC IP内部代码复位
spi7606 spi7606_inst
(
.I_ad_clk(ad_clk_i), //系统时钟输入
.I_ad_rst(ad_rst_i), //系统复位输入
.I_ad_busy(I_ad_busy), //ad7606 忙标志位
.O_ad_cs(O_ad_cs), //ad7606 CS信号输出,低电平SPI数据线输出AD7606寄存器数据
.O_ad_sclk(O_ad_sclk), //ad7606 SCLK时钟输出
.O_ad_rst(O_ad_rst), //ad7606 复位输出
.O_ad_convsta(O_ad_convsta), //ad7606 A组通道转换
.O_ad_convstb(O_ad_convstb), //ad7606 B组通道转换
.O_ad_range(O_ad_range), //ad7606 模拟输入范围,设置1范围:±10V,设置0范围±5V
.I_ad_out_a(I_ad_out_a), //串行A组通道采集输入,V1,V2,V3,V4
.I_ad_out_b(I_ad_out_b), //串行B组通道采集输入, V5,V6,V7,V8
.O_ad_out_a(ad_out_a), //A组通道采集有效数据输出
.O_ad_out_b(ad_out_b), //B组通道采集有效数据输出
.O_ad_cap_en(ad_cap_en) //采集完成使能
);
wire [15:0] ad_ch1 = ad_out_a[63:48];//ADC 通道1
wire [15:0] ad_ch2 = ad_out_a[47:32];//ADC 通道2
wire [15:0] ad_ch3 = ad_out_a[31:16];//ADC 通道3
wire [15:0] ad_ch4 = ad_out_a[15: 0];//ADC 通道4
wire [15:0] ad_ch5 = ad_out_b[63:48];//ADC 通道5
wire [15:0] ad_ch6 = ad_out_b[47:32];//ADC 通道6
wire [15:0] ad_ch7 = ad_out_b[31:16];//ADC 通道7
wire [15:0] ad_ch8 = ad_out_b[15: 0];//ADC 通道8
//例化 ILA IP 用于在线逻辑分仪观察ADC采样值
ila_0 ila_debug
(
.clk(ad_clk_i), //系统时钟
.probe0(ad_cap_en), //AD采集数据使能,ILA中使用capture功能,可以观察到更多有效数据
.probe1(ad_ch1),//ADC 通道1
.probe2(ad_ch2),//ADC 通道2
.probe3(ad_ch3),//ADC 通道3
.probe4(ad_ch4),//ADC 通道4
.probe5(ad_ch5),//ADC 通道5
.probe6(ad_ch6),//ADC 通道6
.probe7(ad_ch7),//ADC 通道7
.probe8(ad_ch8), //ADC 通道8
.probe9(O_ad_cs),//ADC 通道7
.probe10(O_ad_sclk),//ADC 通道7
.probe11(O_ad_rst), //ADC 通道8
.probe12(O_ad_convsta),//ADC 通道7
.probe13(I_ad_out_a), //ADC 通道8
.probe14(O_ad_convstb),//ADC 通道7
.probe15(I_ad_out_b) //ADC 通道8
);
endmodule
四、仿真波形
五、上板验证
触发方式:
六、Bug解决
问题描述:
vivado 仿真时出现 boost::filesystem::remove: 另一个程序正在使用此文件,进程无法访问。
使用了外部编译器VScode,关闭外部编译器即可正常仿真
楠了个难: 800x480@30hz
fstabbycat: 写得很详细,由零开始,真正很有价值的教程。向作者致敬
GoodDayi: 请问你送的是什么分辨率和帧率呀?
嵌入式物联网小哥: AT+MQTTPUB? ERROR这个固件怎么不支持这个指令的?
Laura_JL: 另外,大家这个温度33真的能传到云平台吗,这里连上传的代码都没有,只有一个printf