基于FPGA的AD7928驱动

ADC简介

位数n:一定范围的模拟量能分成2^n份;其中某个模拟量由n位二进制数表示。n越大,精度越高。
采样率:

原理分析

  1. FPGA通过什么管脚控制ADC
    在这里插入图片描述

  2. 用什么时序来控制管脚?
    依据SPI时序图
    在这里插入图片描述
    DOUT输出这一次通道转换的数据,DIN控制下一次要转换数据的通道和其他设置( ADD控制通道序数)
    留意时序规格:结合两张图,可以看出sclk下降沿ADC自动采集DIN,所以需要在上升沿送入DIN。同样sclk下降沿读取DOUT。
    在这里插入图片描述

  3. 控制通道与工作模式
    通过控制寄存器DIN,根据需求选择;如果不懂,或者没有要求,就用默认,最简单的形式。
    在这里插入图片描述
    在这里插入图片描述
    50分频;PM=11; SEO SHADOW=00; RANGE=0,响应0-5V; CODING=1;

代码实现

实现步骤:
1、根据功能要求,在给出clk的波形图上,画出输入和输出的波形;(时序图已给出)
2、标出需要计数的信号,在给出clk的波形图上画出计数器
3、确认各计数器的加1条件、结束条件(加1条件:计数器数什么;结束条件:计数器周期数多少个,不同值时用变量法。 )。
4、其它信号变化点条件(其他信号即输出或内容信号;变化点∶ 0变 1、1变0的点,何时写入/读取)。
5、写出计数器代码和其他信号变量代码

参数声明部分

module ad7928_ctr(
    input clk         , //系统时钟
    input rst_n       , //复位信号
	input adc_dout //adc输出给fpga的串行信号	,
	output reg adc_cs       , //控制adc是否工作的片选信号
    output reg adc_sclk     , //adc的工作时钟
    output reg adc_din      , //fpga控制adc工作参数的信号,勿混淆成要转换的信号
    output reg [15:0] dout      ,	//fpga最终输出的16位并行信号
    output reg dout_vld    ,//转换数据有效指示信号
  
      
);

parameter        WRITE = 1'b1;
parameter        PM    = 2'b11;
parameter        SEQ   = 1'b0;
parameter        SHADOW= 1'b0;
parameter        RANGE = 1'b0;
parameter        CODING= 1'b1;

计数器部分

这三个计数器的时钟(周期)实际上是不同的,但这里并没有用不同频率的时钟信号分别生成计数器,而是统一在系统时钟下,保证了时序上不出问题

1条件结束条件用途
cnt0一个sysclk计数到50-1计数系统时钟个数,50分频产生sclk
cnt1一个sclk=cnt0结束条件计数到18-1计数sclk个数,18sclk对应一整段片选信号CS
cnt2一整段转换过程(CS)=cnt1结束条件计数到8-1计数ADC转换次数,1cnt2的值控制一次ADD通道
reg   [  7:0] cnt0       ;
wire          add_cnt0   ;
wire          end_cnt0   ;
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt0 <= 0;
    end
    else if(add_cnt0)begin
        if(end_cnt0)
            cnt0 <= 0;
        else
            cnt0 <= cnt0 + 1;
    end
end
assign add_cnt0 = 1; //计数器加1条件;可以不要
assign end_cnt0 = cnt0==50-1; //计数器中止条件
//assign end_cnt0 = add_cnt0 && (cnt0==50-1) ;

reg   [ 5:0]  cnt1       ;
wire          add_cnt1   ;
wire          end_cnt1   ;
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        cnt1 <= 0;
    end
    else if(add_cnt1)begin
        if(end_cnt1)
            cnt1 <= 0;
        else
            cnt1 <= cnt1 + 1;
    end
end
assign add_cnt1 = end_cnt0; //cnt0 ==49
assign end_cnt1 = add_cnt1 && (cnt1==18-1) ; //不用前半部分add_cnt1也行,因为就是在add_cnt1=1的条件下才做的end_cnt1判断

reg   [  3:0] cnt2       ;
wire          add_cnt2   ;
wire          end_cnt2   ;
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt2 <= 0;
    end
    else if(add_cnt2)begin
        if(end_cnt2)
            cnt2 <= 0;
        else
            cnt2 <= cnt2 + 1;
    end
end
assign add_cnt2 = end_cnt1;
assign end_cnt2 = add_cnt2 && cnt2==8-1 ;

在这里插入图片描述

其他信号

按照时序要求来,1MHz的sclk,无论是半个还是一个周期,都完全满足时序要求。

复位1变00变1
Sclk11~16cnt1,cnt0的一半1~16cnt1, Cnt0的结束;
0,17cnt1一直保持1
CS1Cnt1=0,cnt0的一半Cnt1=17,cnt0的一半

DIN 16
时序逻辑:前一个sclk结束时写入,中间ADC自动采。因此0~15cnt1,cnt0结束时写入
注意:对每个sclk单独弄,很麻烦,先拼出一个data,每次讲data的一位写给DIN;cnt1=0写入的WRITE是data最高位[15]

DOUT 16
时序逻辑:ADC自动发送,sclk一半时FPGA采集,因此1~16cnt1,cnt0一半时读取

assign middle = add_cnt0 && cnt0==25-1;
//生成sclk
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        adc_sclk <= 1; //注意这里复位是1不是0
    end
    else if(middle && (cnt1>=1) &&(cnt1<17))begin
        adc_sclk <= 0;
    end //每个cnt0计数周期的一半变低,除了sclk第0个,第17个周期
    else if(end_cnt0)begin
        adc_sclk <= 1;
    end //每个cnt0计数周期结束变高,sclk第0,第17个周期保持高位
end

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        adc_cs <= 1;
    end
    else if(middle && cnt1==0)begin
        adc_cs <= 0;
    end //sclk第0个周期的一半拉低,比时序图的要求更宽松
    else if(middle && cnt1==17 )begin
        adc_cs <= 1;
    end 
end

//生成din
wire[15:0]   data;
assign data = {WRITE,SEQ,1'b0,cnt2,PM,SHADOW,1'b0,RANGE,CODING,4'b0};
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        adc_din <= 0;
    end
    else if(end_cnt0 && cnt1<16)begin
        adc_din <= data[15-cnt1]; 
    end
end

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        dout <= 0;
    end
    else if(middle && cnt1>=1 && cnt1<17)begin
        dout[16-cnt1] <= adc_dout; 
    end
end

//8个通道循环一次,输出一个有效位
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        dout_vld <= 0;
    end
    else begin
        dout_vld <= end_cnt2;
    end
end

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:1024 设计师:我叫白小胖 返回首页
评论

打赏作者

luoai_2666

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值