• 基于MCP331310 ADC 的SPI接口驱动练习


    先了解MCP3313-10的操作步骤:

    看上图可以了解,上电之后先经过tACQ(输入采集,但这个时候并不能对数据进行采集)一段时间,设定为状态0;再进入tCNV(数据转换期间,不能采集数据),设定为状态1;最后在进入tACQ(可以进行数据采集),设定位状态2。状态1-2-1-2之间进行轮回。

    然后查看具体时序:

     CNVST 初始值为低,经过tACQ在上升沿到来时,adc进入数据转换,转换时间需要tCNV,然后进入数据采集,采集时间tACQ,所以需要知道这几个具体时间

    tACQ 有一个最小值290ns,没标最大值,说明这个时间可以更长点

    tCNV有标最大值,没标最小时值,说明这个时间可以短点

     

    OK 确认之后,可以对计数器进行设计了。一共需要两个计数器,一个计数器产生sclk,另外一个计数器需对数据采集个数进行计数。由于转换期间和数据采集期间的时间不一样,不会同时计数,所以可以重复利用一个计数器,引入一个变量x.

    上电时的tACQ ,和数据转换后的tACQ 时间是一样,计数器也可以共用一个。代码如下:

     1 always @(posedge clk or negedge rst_n)begin
     2     if(!rst_n)begin
     3         cnt0 <= 0;
     4     end
     5     else if(add_cnt0)begin
     6         if(end_cnt0)begin
     7             cnt0 <= 0;
     8         end
     9         else begin
    10             cnt0 <= cnt0 + 1;
    11         end
    12     end
    13 end
    14 
    15 assign add_cnt0 = flag_add;
    16 assign end_cnt0 = add_cnt0 && cnt0 == 4 - 1; //输入时钟50M,4分屏,sclk = 50/4 = 12.5M
    17 
    18 always @(posedge clk or negedge rst_n)begin
    19     if(!rst_n)begin
    20         cnt1 <= 0;
    21     end
    22     else if(add_cnt1)begin
    23         if(end_cnt1)begin
    24             cnt1 <= 0;
    25         end
    26         else begin
    27             cnt1 <= cnt1 + 1;
    28         end
    29     end
    30 end
    31 
    32 assign add_cnt1 = end_cnt0;
    33 assign end_cnt1 = add_cnt1 && cnt1 == x - 1;
    34 
    35 always @(*)begin
    36     if(state == 0)begin
    37         x = 16;     //tACQ:等待16 * 80 = 1280 ns
    38     end
    39     else if(state == 1)begin
    40         x = 8;        //tCNV:等待8 * 80 = 640 ns ,
    41     end
    42     else begin
    43         x = 16;        //tACQ:等待16 * 80 = 1280 ns
    44     end
    45 end

    引入了一个状态机,上电时进入state =0 状态,数据转换进入state =1 状态,数据采集进入state =2 状态

     1 always @(posedge clk or negedge rst_n)begin
     2     if(!rst_n)begin
     3         state <= 0;
     4         flag_add <= 0;
     5         adc_cs <= 0;
     6         flag_sel <= 0;
     7         dout_vld <= 0;
     8     end
     9     else case(state)
    10 
    11         0:    begin
    12             flag_add <= 1;
    13             adc_cs <= 0;
    14             flag_sel <= 0;
    15             dout_vld <= 0;
    16             if(end_cnt1)begin //等待16 * 80 = 1280 ns
    17                 state <= 1;
    18                 adc_cs <= 1;
    19                 flag_sel <= 0;
    20                 dout_vld <= 0;
    21             end
    22         end
    23         
    24         1:    begin    //adc数据在转换中
    25             adc_cs <= 1;
    26             flag_sel <= 0;
    27             dout_vld <= 0;
    28             if(end_cnt1)begin //等待8*80 = 640 ns
    29                 state <= 2;
    30                 adc_cs <= 0;
    31                 flag_sel <= 1;
    32                 dout_vld <= 0;
    33             end
    34         end
    35         
    36         2:    begin    // 数据采集期间
    37             adc_cs <= 0;
    38             flag_sel <= 1;
    39             dout_vld <= 0;
    40             if(end_cnt1)begin //等待16 * 80 = 1280 ns,数据采集完进入下一轮转换
    41                 state <= 1;
    42                 adc_cs <= 1;
    43                 flag_sel <= 0;
    44                 dout_vld <= 1;
    45             end
    46         end
    47         
    48         default: state <= 0;
    49     endcase
    50 end

    引入了几个信号:

    flag_add: 计数器启动条件,这里设置为1,计数器一直计数

    flag_sel:将数据采集那段时间进行标注,用于区分哪段时间需要进行采数据

    dout_vld:数据采集完产生一个有效标志,然后对一个完整数据进行锁存

     1 always @(posedge clk or negedge rst_n)begin
     2     if(!rst_n)begin
     3         adc_data_temp <= 0;
     4     end
     5     else if(flag_sel == 1 && add_cnt0 && cnt0 == 2-1 && cnt1 >= 0 && cnt1 < 16)begin //在sclk的下降沿采数据,同时在flag_sel有效期间
     6             adc_data_temp[15-cnt1] <= adc_sdo;
     7     end
     8 end
     9 
    10 always @(posedge clk or negedge rst_n)begin
    11     if(!rst_n)begin
    12         adc_data <= 0;
    13     end
    14     else if(dout_vld)begin //一个完整的16bit数据采集完
    15         adc_data <= adc_data_temp;
    16     end
    17 end

    然后产生sclk时钟:

     1 always @(posedge clk or negedge rst_n)begin
     2     if(!rst_n)begin
     3         adc_sclk <= 1;
     4     end
     5     else if(add_cnt0 && cnt0 == 2-1)begin
     6         adc_sclk <= 0;
     7     end
     8     else if(end_cnt0)begin
     9         adc_sclk <= 1;
    10     end
    11 end

    完整代码:

      1 module mcp3313_10_test(
      2                     clk,
      3                     rst_n,
      4                     adc_sdo,
      5                     
      6                     adc_cs,
      7                     adc_sclk,
      8                     adc_data        
      9 );
     10 
     11 input     clk;
     12 input     rst_n;
     13 input     adc_sdo;
     14 
     15 output                 adc_cs;
     16 output                 adc_sclk;
     17 output     [16-1 : 0]    adc_data;
     18 
     19 reg     [16-1 : 0]     adc_data_temp;
     20 reg     [16-1 : 0]    adc_data;
     21 reg     [3 -1 : 0]     cnt0/* synthesis keep*/;
     22 reg     [6 -1 : 0]     cnt1/* synthesis keep*/;
     23 reg     [6 -1 : 0]     x/* synthesis keep*/;
     24 reg     [2 -1 : 0]     state/* synthesis keep*/;
     25 reg                    flag_add;
     26 reg                    flag_sel;
     27 reg                 dout_vld;
     28 reg                 adc_cs;
     29 reg                 adc_sclk;
     30 
     31 
     32 wire                 add_cnt0;
     33 wire                 end_cnt0;
     34 wire                 add_cnt1;
     35 wire                 end_cnt1;
     36 
     37 always @(posedge clk or negedge rst_n)begin
     38     if(!rst_n)begin
     39         cnt0 <= 0;
     40     end
     41     else if(add_cnt0)begin
     42         if(end_cnt0)begin
     43             cnt0 <= 0;
     44         end
     45         else begin
     46             cnt0 <= cnt0 + 1;
     47         end
     48     end
     49 end
     50 
     51 assign add_cnt0 = flag_add;
     52 assign end_cnt0 = add_cnt0 && cnt0 == 4 - 1; //输入时钟50M,4分屏,sclk = 50/4 = 12.5M
     53 
     54 always @(posedge clk or negedge rst_n)begin
     55     if(!rst_n)begin
     56         cnt1 <= 0;
     57     end
     58     else if(add_cnt1)begin
     59         if(end_cnt1)begin
     60             cnt1 <= 0;
     61         end
     62         else begin
     63             cnt1 <= cnt1 + 1;
     64         end
     65     end
     66 end
     67 
     68 assign add_cnt1 = end_cnt0;
     69 assign end_cnt1 = add_cnt1 && cnt1 == x - 1;
     70 
     71 always @(*)begin
     72     if(state == 0)begin
     73         x = 16;     //tACQ:等待16 * 80 = 1280 ns
     74     end
     75     else if(state == 1)begin
     76         x = 8;        //tCNV:等待8 * 80 = 640 ns ,
     77     end
     78     else begin
     79         x = 16;        //tACQ:等待16 * 80 = 1280 ns
     80     end
     81 end
     82 
     83 always @(posedge clk or negedge rst_n)begin
     84     if(!rst_n)begin
     85         state <= 0;
     86         flag_add <= 0;
     87         adc_cs <= 0;
     88         flag_sel <= 0;
     89         dout_vld <= 0;
     90     end
     91     else case(state)
     92 
     93         0:    begin
     94             flag_add <= 1;
     95             adc_cs <= 0;
     96             flag_sel <= 0;
     97             dout_vld <= 0;
     98             if(end_cnt1)begin //等待16 * 80 = 1280 ns
     99                 state <= 1;
    100                 adc_cs <= 1;
    101                 flag_sel <= 0;
    102                 dout_vld <= 0;
    103             end
    104         end
    105         
    106         1:    begin    //adc数据在转换中
    107             adc_cs <= 1;
    108             flag_sel <= 0;
    109             dout_vld <= 0;
    110             if(end_cnt1)begin //等待8*80 = 640 ns
    111                 state <= 2;
    112                 adc_cs <= 0;
    113                 flag_sel <= 1;
    114                 dout_vld <= 0;
    115             end
    116         end
    117         
    118         2:    begin    // 数据采集期间
    119             adc_cs <= 0;
    120             flag_sel <= 1;
    121             dout_vld <= 0;
    122             if(end_cnt1)begin //等待16 * 80 = 1280 ns,数据采集完进入下一轮转换
    123                 state <= 1;
    124                 adc_cs <= 1;
    125                 flag_sel <= 0;
    126                 dout_vld <= 1;
    127             end
    128         end
    129         
    130         default: state <= 0;
    131     endcase
    132 end
    133 
    134 always @(posedge clk or negedge rst_n)begin
    135     if(!rst_n)begin
    136         adc_data_temp <= 0;
    137     end
    138     else if(flag_sel == 1 && add_cnt0 && cnt0 == 2-1 && cnt1 >= 0 && cnt1 < 16)begin //在sclk的下降沿采数据,同时在flag_sel有效期间
    139             adc_data_temp[15-cnt1] <= adc_sdo;
    140     end
    141 end
    142 
    143 always @(posedge clk or negedge rst_n)begin
    144     if(!rst_n)begin
    145         adc_data <= 0;
    146     end
    147     else if(dout_vld)begin //一个完整的16bit数据采集完
    148         adc_data <= adc_data_temp;
    149     end
    150 end
    151 
    152 always @(posedge clk or negedge rst_n)begin
    153     if(!rst_n)begin
    154         adc_sclk <= 1;
    155     end
    156     else if(add_cnt0 && cnt0 == 2-1)begin
    157         adc_sclk <= 0;
    158     end
    159     else if(end_cnt0)begin
    160         adc_sclk <= 1;
    161     end
    162 end
    163 
    164 endmodule
    View Code

    SignalTap II Logic Analyzer 抓取波形如下:

  • 相关阅读:
    day11 函数的进阶
    day10 文件的补充以及函数
    day9 文件处理
    day8 字典的补充以及集合
    Vue学习下
    前端常用js方法集
    js实现千位符格式化
    Vue项目总结上
    VUE项目github
    http://www.jianshu.com/p/42e11515c10f#
  • 原文地址:https://www.cnblogs.com/wen2376/p/15958363.html
Copyright © 2020-2023  润新知