FPGA的ADC采集部分学习整理

adc采集部分还是很有用的,模数转换在很多地方都用得到。

使用的EDA模块上的ADC芯片是adc128s102。逐次逼近型ADC(一般单片机用的都是逐次逼近型,速度较快,成本低)。8通道以及12位分辨率。

这边手册上说模拟电源的VA输入范围为2.7V~5.25V


ADC芯片,接入8个模拟输入引脚,输入模拟量。IN0~IN7

引脚DIN是串行数据输入引脚,SCLK上升沿采样输入

引脚DOUT转换结果输出脚,SCLK下降沿输出

SCLK时钟输入,频率范围8~16MHZ,这边采用12.5MHZ,因为是50MHZ的四分之一

CS,片选,下降沿开始测量转换,低电平状态为正在转换

12位数据,是DOUT传出来的,4096分量

所以:

电压值与ADC输出值的的关系为   电压U=adc输出值*3.3V /4096 

这边3.3是引脚的实际电压值。


线性序列机方式,我的理解就是找到其中一个电平时间作为参照物。

这边CS片选,高电平下降到低电平,然后开始工作

SCLK时钟,一帧16个上升沿和下降沿。也是先高到低

一开始三个周期是Track,采样模式

后面13个周期是hold保持模式,转换完成并且完成数据输出

这边以SCLK为线性序列机的时钟参照。以SCLK的时间参照,记录在每个SCLK时间单位到来时,其他寄存器的值去对应。

第三个时钟到第五个时钟下降沿开始选择输入通道。这三个值组成8位通道数 000~111  0~7

看到最后的仿真图,更容易理解,

在lsm_cnt在0~34计数的时候,这边序列机的时间单位是40ns

这边就是记录每个lsm_cnt的值达到时候的寄存器的值。(这边lsm_cnt计数满都是最右边在div_cnt两个时候)

这边主要是din中addr值的确定和dout中的12为adc输出数据的写入data(外部模拟量写入)。 

 代码中对addr和data的值,做了寄存处理,防止端口电平变化造成的一些影响(之前遇到的),锁存操作经常用到。

下面是驱动的框图,这边我准备重新设计一下。

这边我重新设计了一下系统框图。

就是FPGA给驱动时钟信号clk,复位信号reset_n,转换使能信号conv_go(可以用按键触发),通道地址信号addr(8通道用3个拨码开关表示)

外部模拟量通过模拟引脚通过adc芯片转换后adc_dout输出给驱动。每次只有一通道可以采集,这个adc_dout是串行数据输入给驱动

然后驱动输出给adc芯片的有,adc时钟sclk作为线性序列机的参考时钟,输出cs片选信号控制转换开始停止,输出adc_din作为选择通道

最后将模拟量的串行输入信号并行传输给FPGA,以及发送转换停止信号告诉主控(fpga)。

FPGA在收到转换后的并行data数据之后,就可以进一步处理,就是通过之前的数码管显示出来。因为data数据之后12位,所以4数据一组16进制,3个数码管就够用了。

通过按键触发conv_go转换开始标志。data数据就留给数码管显示

ADC的上面的data数据转给disp_data[31:0],因为只有12位,所以高20位全部为0,只用低位3个数码管显示

下面就是数码管的知识了(之前的随笔里面写过)

最后还有一个按键触发的转换开始(是按键消除抖动那边的,也不难)

module adc128s102(
    clk,
    reset_n,
    
    conv_go, //使能单次转换,高脉冲使能一次
    addr,    //通道
    
    conv_done,//单次转换结束,转换结束后产生一个时钟的高脉冲
    data,    //ADC转换结果
    
    adc_sclk,
    adc_cs_n,
    adc_din,
    adc_dout
    );
    
    input   clk;
    input   reset_n;
    
    input   conv_go;
    input   [2:0]   addr;
    
    output  reg conv_done;
    output  reg [11:0] data;
    
    output  reg adc_sclk;
    output  reg adc_cs_n;
    output  reg adc_din;
    input    adc_dout;   //输入类型的端口
    
    parameter   CLOCK_FREQ = 50_000_000;
    parameter   SCLK_FREQ = 12_500_000; //8~16mhz 取12.5Mhz 因为好算
    parameter   MCNT_DIV_CNT = CLOCK_FREQ/(SCLK_FREQ * 2) - 1;//1  40ns
    
    reg [7:0]   div_cnt;
    reg [5:0]   lsm_cnt;//计数到34
    
    reg [2:0] r_addr;
    reg [11:0] data_r;
    
    reg conv_en;
    
    always @(posedge clk or negedge reset_n)
        if(!reset_n)
            conv_en <= 1'd0;
        else if(conv_go)
            conv_en <= 1'd1;  //分频计数器使能
//        else if((lsm_cnt <= 6'd34) && (div_cnt == MCNT_DIV_CNT))
        else if(conv_done)
            conv_en <= 1'd0;
        else
            conv_en <= conv_en;
    
    //确保端口传输时电平的变化造成的传输没完成会影响最后传输
    //先锁存一下
//    always @(posedge clk)
//        if(conv_go)
//            r_addr <= addr;
//        else
//            r_addr <= r_addr;
    always @(posedge clk or negedge reset_n)
        if(!reset_n)
            r_addr <= 3'd0;
        else if(conv_go)
            r_addr <= addr;
        else
            r_addr <= r_addr;
    
    //最小单位计数器
    //40ns
    always @(posedge clk or negedge reset_n)
        if(!reset_n)
            div_cnt <= 0;
        else if(conv_en) begin    //计数使能信号
            if(div_cnt == MCNT_DIV_CNT)
                div_cnt <= 0;
            else
                div_cnt <= div_cnt + 1'd1;
        end
        else
            div_cnt <= 0;
    
    //序列计数器,线性序列击0~34
    always @(posedge clk or negedge reset_n)
        if(!reset_n)
           lsm_cnt <= 6'd0;
        else if(div_cnt == MCNT_DIV_CNT) begin
            if(lsm_cnt == 6'd34)
                lsm_cnt <= 6'd0;
            else
                lsm_cnt <= lsm_cnt + 1'd1;
        end
        else
            lsm_cnt <= lsm_cnt; 
            
     always @(posedge clk or negedge reset_n)
        if(!reset_n)begin
            data_r <= 12'd0;
            adc_sclk <= 1'd1;
            adc_din <= 1'd1;
            adc_cs_n <= 1'd1;
            //adc_dout <= 1'd1;
        end
        else if(div_cnt == MCNT_DIV_CNT)begin
            case(lsm_cnt)
                 0 : begin adc_cs_n <= 1'd1; adc_sclk <= 1'd1; end
                 1 : begin adc_cs_n <= 1'd0; end
                 2 : begin adc_sclk <= 1'd0; end
                 3 : begin adc_sclk <= 1'd1; end
                 4 : begin adc_sclk <= 1'd0; end
                 5 : begin adc_sclk <= 1'd1; end
                 6 : begin adc_sclk <= 1'd0; adc_din <= r_addr[2]; end
                 7 : begin adc_sclk <= 1'd1; end
                 8 : begin adc_sclk <= 1'd0; adc_din <= r_addr[1]; end
                 9 : begin adc_sclk <= 1'd1; end
                 10 : begin adc_sclk <= 1'd0; adc_din <= r_addr[0]; end
                 11 : begin adc_sclk <= 1'd1; data_r[11] <= adc_dout; end
                 12 : begin adc_sclk <= 1'd0; end
                 13 : begin adc_sclk <= 1'd1; data_r[10] <= adc_dout; end
                 14 : begin adc_sclk <= 1'd0; end
                 15 : begin adc_sclk <= 1'd1; data_r[9] <= adc_dout; end
                 16 : begin adc_sclk <= 1'd0; end
                 17 : begin adc_sclk <= 1'd1; data_r[8] <= adc_dout; end
                 18 : begin adc_sclk <= 1'd0; end
                 19 : begin adc_sclk <= 1'd1; data_r[7] <= adc_dout; end
                 20 : begin adc_sclk <= 1'd0; end
                 21 : begin adc_sclk <= 1'd1; data_r[6] <= adc_dout; end
                 22 : begin adc_sclk <= 1'd0; end
                 23 : begin adc_sclk <= 1'd1; data_r[5] <= adc_dout; end
                 24 : begin adc_sclk <= 1'd0; end
                 25 : begin adc_sclk <= 1'd1; data_r[4] <= adc_dout; end
                 26 : begin adc_sclk <= 1'd0; end
                 27 : begin adc_sclk <= 1'd1; data_r[3] <= adc_dout; end
                 28 : begin adc_sclk <= 1'd0; end
                 29 : begin adc_sclk <= 1'd1; data_r[2] <= adc_dout; end
                 30 : begin adc_sclk <= 1'd0; end
                 31 : begin adc_sclk <= 1'd1; data_r[1] <= adc_dout; end
                 32 : begin adc_sclk <= 1'd0; end
                 33 : begin adc_sclk <= 1'd1; data_r[0] <= adc_dout; end
                 34 : begin adc_cs_n <= 1'd1; end
                 default : adc_cs_n <= 1'd1;
             endcase
             end
                      
       always @(posedge clk or negedge reset_n)
            if(!reset_n)begin
                data <= 12'd0;
                conv_done <= 0;
            end
            else if((lsm_cnt == 34) && (div_cnt == MCNT_DIV_CNT))begin
                conv_done <= 1'd1;  //转换完成标志信号
                data <= data_r;
            end
            else begin
                conv_done <= 1'd0;
                data <= data;
            end    
            
endmodule

 

posted @ 2024-03-07 14:28  祈愿树下  阅读(731)  评论(0编辑  收藏  举报
// 侧边栏目录 // https://blog-static.cnblogs.com/files/douzujun/marvin.nav.my1502.css