高云FPGA控制AD7606
AD7606简介
关键引脚介绍
本文不细究AD7606芯片工作供电及参考电压相关引脚,只讨论与控制相关的引脚。
引脚号 | 名称 | 作用 |
---|---|---|
5,4,3 | OS[2:0] | 过采样倍率控制位,具体见下表 |
6 | PAR_n/SER/BYTE SEL | 拉低为并行接口模式,拉高为串行接口模式,拉高且#33引脚拉高为并行字节接口模式 |
9,10 | CONVST A,B | AD7606开始采样控制位,两个直接短接使用 |
11 | RESET | AD7606复位引脚,拉高50ns以上有效 |
12 | RD_n/SCLK | 并行接口模式下拉低读取数据,串行接口模式下为时钟接口 |
13 | CS_n | 片选信号 |
15 | FRSTDATA | 正在读取第一个采样值标志位,高有效 |
OS[2:0] | 过采样倍率 | 最大转换频率(KHz) |
---|---|---|
000 | NO OS | 200 |
001 | 2 | 100 |
010 | 4 | 50 |
011 | 8 | 25 |
100 | 16 | 12.5 |
101 | 32 | 6.25 |
110 | 64 | 3.125 |
111 | 无效 | ~ |
控制时序介绍
这里只阐述并行接口模式下,转换完成后读取的时序。如下图,当CONVST引脚短接情况下,当RESET拉高后25ns可拉低CONVSTA,B,经过25ns拉低RESET,再经过25ns拉高CONVSTA,完成第一次采集开始命令,以后只需执行50ns以上CONVST低电平持续时间,就可以开始采样。在OS为3‘b000时,AD7606的BUSY引脚至多维持4200ns,也就是说在大于t1时间后检测BUSY是否为低电平即可完成检测AD7606是否完成转换。
本文介绍的是CS_n与RD_n分别控制的读取时序,在判断BUSY有下降沿后,拉低CS、RD,当供电电压大于4.75V时,等待19ns可以读取第一通道数据,然后拉高RD,等待15ns以上,拉低RD,延迟19ns读取第二通道数据,以此类推。本文程序未用到FRSTDATA引脚。
单段状态机实现控制单个AD7606
控制程序
`timescale 1ns / 1ps
module AD7606(
input clk, //module operating frequency 500MHz
input rst_n, //reset, active low
input en_read; //enable to read data, active high
input [15:0] data_in, //sampling data input
input busy, //ad7606 busy flag
output reg cs, //ad7606 chip select
output reg rd, //ad7606 read control signal
output reg reset, //ad7606 reset
output reg convstab, //ad7606 AD convert start
output [2:0] os, //ad7606 oversampling rate selection
output reg [15:0] ch1, //ad7606 channel1 data
output reg [15:0] ch2, //ad7606 channel2 data
output reg [15:0] ch3, //ad7606 channel3 data
output reg [15:0] ch4, //ad7606 channel4 data
output reg [15:0] ch5, //ad7606 channel5 data
output reg [15:0] ch6, //ad7606 channel6 data
output reg [15:0] ch7, //ad7606 channel7 data
output reg [15:0] ch8, //ad7606 channel8 data
output reg receive_over //the module cycle over
);
localparam CONVST_DOWN = 5'd0;
localparam WAIT_BUSY_UP = 5'd1;
localparam WAIT_BUSY_DOWN = 5'd2;
localparam WAIT1 = 5'd3; //delay from cs until DB[15:0] three-state disabled
localparam READ_CH1 = 5'd4;
localparam RD_HIGH_CH1 = 5'd5;
localparam READ_CH2 = 5'd6;
localparam RD_HIGH_CH2 = 5'd7;
localparam READ_CH3 = 5'd8;
localparam RD_HIGH_CH3 = 5'd9;
localparam READ_CH4 = 5'd10;
localparam RD_HIGH_CH4 = 5'd11;
localparam READ_CH5 = 5'd12;
localparam RD_HIGH_CH5 = 5'd13;
localparam READ_CH6 = 5'd14;
localparam RD_HIGH_CH6 = 5'd15;
localparam READ_CH7 = 5'd16;
localparam RD_HIGH_CH7 = 5'd17;
localparam READ_CH8 = 5'd18;
localparam END_CIRCUL = 5'd20;
//set ad7606 oversampling rate selection
assign os = 3'b000;
//ad7606 reset port
reg [7:0] cnt_rst;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_rst <= 8'd0;
end else if(cnt_rst < 8'd25) begin
reset <= 1'b1;
cnt_rst <= cnt_rst + 8'd1;
end else
reset <= 1'b0;
end
//ad7606 sampling port
reg [7:0] cnt_sample;
reg [4:0] state;
always @ (posedge clk) begin
if(!rst_n) begin
state <= CONVST_DOWN;
cnt_sample <= 8'd25;
ch1 <= 16'd0;
ch2 <= 16'd0;
ch3 <= 16'd0;
ch4 <= 16'd0;
ch5 <= 16'd0;
ch6 <= 16'd0;
ch7 <= 16'd0;
ch8 <= 16'd0;
cs <= 1'b1;
rd <= 1'b1;
convstab <= 1'b1;
receive_over <= 1'b0;
end else if(!reset) begin
case(state)
CONVST_DOWN: begin
convstab <= 1'b0;
ch1 <= 16'd0;
ch2 <= 16'd0;
ch3 <= 16'd0;
ch4 <= 16'd0;
ch5 <= 16'd0;
ch6 <= 16'd0;
ch7 <= 16'd0;
ch8 <= 16'd0;
receive_over <= 1'b0;
if(cnt_sample == 8'd0) begin
cnt_sample <= 8'd200;
state <= WAIT_BUSY_UP;
end else
cnt_sample <= cnt_sample - 8'd1;
end
WAIT_BUSY_UP: begin
convstab <= 1'b1;
if(cnt_sample == 8'd0)
state <= WAIT_BUSY_DOWN;
else
cnt_sample <= cnt_sample - 8'd1;
end
WAIT_BUSY_DOWN: begin
if(!busy) begin
state <= WAIT1;
cnt_sample <= 8'd10;
end
end
WAIT1: begin
if (en_read) begin
cs <= 1'b0;
if(cnt_sample == 8'd0) begin
state <= READ_CH1;
cnt_sample <= 8'd20;
end else
cnt_sample <= cnt_sample - 8'd1;
end
end
READ_CH1: begin
rd <= 1'b0;
case(cnt_sample)
8'd0: begin
state <= RD_HIGH_CH1;
cnt_sample <= 8'd12;
end
8'd15: begin
cnt_sample <= cnt_sample - 8'd1;
ch1 <= data_in;
end
default:
cnt_sample <= cnt_sample - 8'd1;
endcase
end
RD_HIGH_CH1: begin
rd <= 1'b1;
if(cnt_sample == 8'd0) begin
state <= READ_CH2;
cnt_sample <= 8'd20;
end else
cnt_sample <= cnt_sample - 8'd1;
end
READ_CH2: begin
rd <= 1'b0;
case(cnt_sample)
8'd0: begin
state <= RD_HIGH_CH2;
cnt_sample <= 8'd12;
end
8'd15: begin
cnt_sample <= cnt_sample - 8'd1;
ch2 <= data_in;
end
default:
cnt_sample <= cnt_sample - 8'd1;
endcase
end
RD_HIGH_CH2: begin
rd <= 1'b1;
if(cnt_sample == 8'd0) begin
state <= READ_CH3;
cnt_sample <= 8'd20;
end else
cnt_sample <= cnt_sample - 8'd1;
end
READ_CH3: begin
rd <= 1'b0;
case(cnt_sample)
8'd0: begin
state <= RD_HIGH_CH3;
cnt_sample <= 8'd12;
end
8'd15: begin
cnt_sample <= cnt_sample - 8'd1;
ch3 <= data_in;
end
default:
cnt_sample <= cnt_sample - 8'd1;
endcase
end
RD_HIGH_CH3: begin
rd <= 1'b1;
if(cnt_sample == 8'd0) begin
state <= READ_CH4;
cnt_sample <= 8'd20;
end else
cnt_sample <= cnt_sample - 8'd1;
end
READ_CH4: begin
rd <= 1'b0;
case(cnt_sample)
8'd0: begin
state <= RD_HIGH_CH4;
cnt_sample <= 8'd12;
end
8'd15: begin
cnt_sample <= cnt_sample - 8'd1;
ch4 <= data_in;
end
default:
cnt_sample <= cnt_sample - 8'd1;
endcase
end
RD_HIGH_CH4: begin
rd <= 1'b1;
if(cnt_sample == 8'd0) begin
state <= READ_CH5;
cnt_sample <= 8'd20;
end else
cnt_sample <= cnt_sample - 8'd1;
end
READ_CH5: begin
rd <= 1'b0;
case(cnt_sample)
8'd0: begin
state <= RD_HIGH_CH5;
cnt_sample <= 8'd12;
end
8'd15: begin
cnt_sample <= cnt_sample - 8'd1;
ch5 <= data_in;
end
default:
cnt_sample <= cnt_sample - 8'd1;
endcase
end
RD_HIGH_CH5: begin
rd <= 1'b1;
if(cnt_sample == 8'd0) begin
state <= READ_CH6;
cnt_sample <= 8'd20;
end else
cnt_sample <= cnt_sample - 8'd1;
end
READ_CH6: begin
rd <= 1'b0;
case(cnt_sample)
8'd0: begin
state <= RD_HIGH_CH6;
cnt_sample <= 8'd12;
end
8'd15: begin
cnt_sample <= cnt_sample - 8'd1;
ch6 <= data_in;
end
default:
cnt_sample <= cnt_sample - 8'd1;
endcase
end
RD_HIGH_CH6: begin
rd <= 1'b1;
if(cnt_sample == 8'd0) begin
state <= READ_CH7;
cnt_sample <= 8'd20;
end else
cnt_sample <= cnt_sample - 8'd1;
end
READ_CH7: begin
rd <= 1'b0;
case(cnt_sample)
8'd0: begin
state <= RD_HIGH_CH7;
cnt_sample <= 8'd12;
end
8'd15: begin
cnt_sample <= cnt_sample - 8'd1;
ch7 <= data_in;
end
default:
cnt_sample <= cnt_sample - 8'd1;
endcase
end
RD_HIGH_CH7: begin
rd <= 1'b1;
if(cnt_sample == 8'd0) begin
state <= READ_CH8;
cnt_sample <= 8'd20;
end else
cnt_sample <= cnt_sample - 8'd1;
end
READ_CH8: begin
rd <= 1'b0;
case(cnt_sample)
8'd0: begin
state <= END_CIRCUL;
cnt_sample <= 8'd12;
end
8'd15: begin
cnt_sample <= cnt_sample - 8'd1;
ch8 <= data_in;
end
default:
cnt_sample <= cnt_sample - 8'd1;
endcase
end
END_CIRCUL: begin
rd <= 1'b1;
cs <= 1'b1;
receive_over <= 1'b1;
if(cnt_sample == 8'd0) begin
state <= CONVST_DOWN;
cnt_sample <= 8'd20;
end else
cnt_sample <= cnt_sample - 8'd1;
end
default: state <= CONVST_DOWN;
endcase
end
end
endmodule
仿真程序
`timescale 1ns / 1ps
module AD7606_sim();
reg clk;
reg rst_n;
reg [15:0] data_in;
reg busy;
wire [2:0] os;
wire cs;
wire rd;
wire reset;
wire convstab;
wire [15:0] ch1;
wire [15:0] ch2;
wire [15:0] ch3;
wire [15:0] ch4;
wire [15:0] ch5;
wire [15:0] ch6;
wire [15:0] ch7;
wire [15:0] ch8;
wire receive_over;
AD7606 AD7606(
.clk(clk),
.rst_n(rst_n),
.data_in(data_in),
.busy(busy),
.os(os),
.cs(cs),
.rd(rd),
.reset(reset),
.convstab(convstab),
.ch1(ch1),
.ch2(ch2),
.ch3(ch3),
.ch4(ch4),
.ch5(ch5),
.ch6(ch6),
.ch7(ch7),
.ch8(ch8),
.receive_over(receive_over)
);
reg [15:0] cnt;
initial begin
data_in = 16'd255;
clk = 1'b1;
busy = 1'b0;
end
always #1 clk = ~clk;
always @(posedge convstab) begin
busy <= 1'b1;
#4200 busy <= 1'b0;
end
initial begin
rst_n = 1'b0;
#200;
rst_n = 1'b1;
#200;
#100000000;
end
endmodule
仿真结果
存在的问题及接下来的工作
由于是第一次自己写Verilog程序,思路上还有很多欠缺,比如状态机写成了揉在一起的,而不是推荐的三段式;程序相对复杂,不够精简。接下来我是想做一个64路的采集卡,自上而下的开发思路没有应用导致现在写的代码还需重新修改,还是有待锻炼。