当前位置:   article > 正文

【接口协议】FPGA实现SPI协议基于ADC128S022进行模拟信号采集

adc128s022

0.序言

使用vivado联合modelsim实现SPI协议基于ADC128S022进行模拟信号连续采集。

1.SPI协议简介

(1)结构

SPI是串行外设接口,是一种同步/全双工/主从式接口。通常由四根信号线构成:
CS_N:片选信号,主从式接口,可以有多个从机,用片选信号进行从机选择;
SCLK:串行时钟线,由主机提供给从机;
MISO:主机接收(采集)从机数据信号线;
MOSI:主机发送数据给从机信号线;
在这里插入图片描述

(2)工作模式

CKP:时钟极性,用来配置时钟线SCLK的电平处于何种状态是空闲状态或者有效状态;
CKE:时钟相位,配置发送数据和采集数据分别是在时钟上升沿还是下降沿;
在这里插入图片描述

2.ADC128S022芯片简介

(1)ADC128S022

ADC128S022模数转换芯片有8个通道和12位的分辨率,时钟要求时钟频率范围在1~3.2MHz。可以看出芯片的八个通道(channel),在SPI协议中需要选择采集数据的通道。
在这里插入图片描述

(2)信号时序

ADC128S022的SPI协议时序图如下图所示(由数据手册截出):
在这里插入图片描述

3.实例

(1)结构框图

设计完成ADC128S022控制模块,根据指定的通道产生控制信号,控制ADC128S022进行连续采集模拟信号,最后采集的信号通过Sam_data传出。
在这里插入图片描述

(2)时序图

A 控制信号(截选,因为完整的太长了只截了一部分)
在这里插入图片描述

(a)首先测试给控制模块提供复位(rst_n)和通道选择(channel),通道可以随时变化(8个通道[0:7]);
(b)cs_n:片选信号,低电平有效。只使用一片,在复位信号无效后,直接将片选拉低,让芯片处于工作状态;
©cnt:分频计数器,系统时钟使用的是50MHz,ADC芯片时钟范围是0.8~3.2MHz,进行20分频到2.5MHz;20分频,每10个时钟周期产生一个标志位,时钟状态反转一次。
(d)sclk_cnt:sclk状态计数器,共33个状态[0:32],0为初始状态,其后32个状态为有效循环状态;(为什么有效循环状态是32?由上图2(2)ADC的信号时序图可以看出,数据采集周期sclk共16个周期,因为数据发送和采集分别发送在上升沿和下降沿,所以将每个周期一分为2,对应32个状态)
注意:这只给出了时序图截选,完整的太长了,所以对剩下部分进行了描述:
rst_n:保持不变
channel[2:0]:可以随时进行变化,表示ADC采集的数据的通道,本文只仿真产生了一个通道的数据;
cs_n:后续不变
cnt:一直保持0到9的10状态循环计数;
cnt_flag:当cnt状态为0,cnt_flag为1;
sclk_cnt:第一次循环会有0到32共33个状态,因为多了一个初始状态0,后面循环一直为1到32共32个状态的循环;
注意:sclk这里给出只是为了显示相对时序,后续会说明;
B SPI信号(对照2(2)ADC芯片数据手册给出的时序图)
这里给出了一个周期
在这里插入图片描述
cs_n:片选信号开始已经给出;
sclk:最开始有一个初始状态(空闲状态,及开始的高电平段),其后对应有效循环中的16个周期,每个周期先低后高,数字表示第几个周期,l和h表示高低电平
din:FPGA控制模块输出给AD芯片的数据,前8个状态为控制信号,其中第3到5个状态需要输出通道选择信号(channel[2:])的高到低位;注意:数据一定要保证在sclk上升沿处采集,给值是在下降沿给;
DOUT:FPGA输入的AD芯片采集的数据;在第5到16个状态得到AD采集的数据的高到低位,共12位;注意:ADC模块采集数据是在SCLK下降沿,所以DOUT数据在SCLK上升沿是稳定的
C SPI连续采集有效循环
B中给出了初始状态和连续采集数据的有效循环状态,这里截出有效循环状态,以后一直保持有效循环:
在这里插入图片描述

(3)test_bench

test_bench的核心是模拟ADC采集的数据,并将采集的数据按照要求时序,按位赋给DOUT:
(A) 使用matlab产生模拟的ADC采集的数据
说明:模拟数据为正弦信号,信号频率为10000Hz,以16进制格式保存为txt文件。

clc
clear
close all
%% 模拟采集的正弦信号
fs=2.5e6/16;        %采样频率
f0=10000;           %信号频率
sam_point=100;      %采样点数
t=0:1/fs:1-1/fs;
s=sin(2*pi*f0*t);
s=s.*(2^11);
plot(t(1:100),s(1:100))
s_12bit=zeros(1,sam_point);
for nn=1:sam_point
        if(s(nn)<0) 
            s_12bit(nn) = uint32(2^12+s(nn)); 
        else
            s_12bit(nn) = uint32(s(nn)); 
        end
end
fp = fopen('s_sample.txt','w');
for nn=1:sam_point
    fprintf(fp,'%X\n',s_12bit(nn));
end
fclose(fp);

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

(B) test_ben读取模拟数据给DOUT
a首先:在initial模块读出产生的模拟的正弦波信号;

        $readmemh("D:/graduate_stuty/FPGA/interface/SPI/s_sample.txt",adc_sam_data);
  • 1

我们要保证跨过初始状态,在每个有效循环状态中的sclk的4到15个周期的下降沿把采集的数据的11到0位,依次赋给DOUT,保证控制模块在sclk 5到16个状态采集到采样数据的12位。

在这里插入图片描述

4.完整代码

(1)ADC128S022控制模块

module SPI_interface(
    input                   clk     ,
    input                   rst_n   ,
    input       [2:0]       channel ,
    output reg              ADC_cs_n,
    output reg              ADC_sclk,
    output reg              ADC_din ,   //fpga给adc芯片的输出信号
    input                   ADC_dout,    //adc芯片给fpga的采样数据    
    output reg  [11:0]      Sam_data
    );


    //片选信号产生模块,只有一片,片选信号直接由复位信号产生
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)
            ADC_cs_n <= 1'b1;
        else    
            ADC_cs_n <= 1'b0;
    end 


    //20分频器产生,每10个状态产生一个状态反转标志信号
    reg [3:0]   cnt_10;
    reg         cnt_flag;
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt_10 <= 4'd0;
            cnt_flag <= 1'b0; 
        end
        else if(ADC_cs_n == 1'b0)begin
            if(cnt_10 == 4'd9)begin
                cnt_10 <= 4'd0;
                cnt_flag <= 1'b1;
            end
            else begin
                cnt_10 <= cnt_10 + 1'b1;
                cnt_flag <= 1'b0;
            end           
        end
    end


    //SCLK状态计数器产生,33个状态[0:32],初始状态为0,有效循环状态为[1:32]
    reg [5:0] sclk_cnt;
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)
            sclk_cnt <= 6'd0;
        else if(ADC_cs_n == 1'b0)begin
            if(cnt_flag == 1'b1)
                if(sclk_cnt == 6'd32)
                    sclk_cnt <= 6'd1;
                else 
                    sclk_cnt <= sclk_cnt +  1'b1;
            else
                sclk_cnt <= sclk_cnt;
        end
        else 
            sclk_cnt <=6'd0;
    end

    //SCLK,DIN赋值及DOUT数据采集
    reg [11:0]  Sam_data_r;//输出数据存储临时变量
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            Sam_data <= 12'd0;
            Sam_data_r <= 12'd0;
            ADC_sclk <= 1'd1;
            ADC_din <= 1'd0;
        end
        else begin
            case(sclk_cnt)
                6'd1:begin  ADC_sclk <= 1'd0; end
                6'd2:begin  ADC_sclk <= 1'd1; Sam_data <= Sam_data_r; end
                6'd3:begin  ADC_sclk <= 1'd0; end
                6'd4:begin  ADC_sclk <= 1'd1; end
                6'd5:begin  ADC_sclk <= 1'd0; ADC_din <= channel[2]; end
                6'd6:begin  ADC_sclk <= 1'd1; end
                6'd7:begin  ADC_sclk <= 1'd0; ADC_din <= channel[1];end
                6'd8:begin  ADC_sclk <= 1'd1; end
                6'd9:begin  ADC_sclk <= 1'd0; ADC_din <= channel[0];end
                6'd10:begin ADC_sclk <= 1'd1; Sam_data_r[11] <= ADC_dout; end
                6'd11:begin ADC_sclk <= 1'd0; end
                6'd12:begin ADC_sclk <= 1'd1; Sam_data_r[10] <= ADC_dout;end
                6'd13:begin ADC_sclk <= 1'd0; end
                6'd14:begin ADC_sclk <= 1'd1; Sam_data_r[9] <= ADC_dout;end
                6'd15:begin ADC_sclk <= 1'd0; end
                6'd16:begin ADC_sclk <= 1'd1; Sam_data_r[8] <= ADC_dout;end
                6'd17:begin ADC_sclk <= 1'd0; end
                6'd18:begin ADC_sclk <= 1'd1; Sam_data_r[7] <= ADC_dout;end
                6'd19:begin ADC_sclk <= 1'd0; end
                6'd20:begin ADC_sclk <= 1'd1; Sam_data_r[6] <= ADC_dout;end
                6'd21:begin ADC_sclk <= 1'd0; end
                6'd22:begin ADC_sclk <= 1'd1; Sam_data_r[5] <= ADC_dout;end
                6'd23:begin ADC_sclk <= 1'd0; end
                6'd24:begin ADC_sclk <= 1'd1; Sam_data_r[4] <= ADC_dout;end
                6'd25:begin ADC_sclk <= 1'd0; end
                6'd26:begin ADC_sclk <= 1'd1; Sam_data_r[3] <= ADC_dout;end
                6'd27:begin ADC_sclk <= 1'd0; end
                6'd28:begin ADC_sclk <= 1'd1; Sam_data_r[2] <= ADC_dout;end
                6'd29:begin ADC_sclk <= 1'd0; end
                6'd30:begin ADC_sclk <= 1'd1; Sam_data_r[1] <= ADC_dout;end
                6'd31:begin ADC_sclk <= 1'd0; end
                6'd32:begin ADC_sclk <= 1'd1; Sam_data_r[0] <= ADC_dout;end
                default:begin  Sam_data <= 12'd0; Sam_data_r <= 12'd0; ADC_sclk <= 1'd1; ADC_din <= 1'd0;end
            endcase
        end
    end
endmodule

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109

(2)test_bench

`timescale 1ns / 1ps
module SPI_interface_tb;
    reg                 clk     ;
    reg                 rst_n   ;
    reg       [2:0]     channel ;
    wire                ADC_cs_n;
    wire                ADC_sclk;
    wire                ADC_din ;   //fpga给adc芯片的输出信号
    reg                 ADC_dout;    //adc芯片给fpga的采样数据    
    wire  [11:0]        Sam_data;

    parameter T = 20;
    //ADC采集的模拟信号
    reg [11:0] adc_sam_data [0:99];
    
    //初始化模块
    initial begin
        $readmemh("D:/graduate_stuty/FPGA/interface/SPI/s_sample.txt",adc_sam_data);
        clk = 1'b0;     
        rst_n = 1'b0;  
        channel = 3'd0;
        #(T*4);
        rst_n = 1'b1;  
        channel = 3'd3;
    end

    //时钟信号产生模块
    always#(T/2) clk = ~clk;

    //模块例化
    SPI_interface u_SPI_interface(
        .clk            (clk     ),
        .rst_n          (rst_n   ),
        .channel        (channel ),
        .ADC_cs_n       (ADC_cs_n),
        .ADC_sclk       (ADC_sclk),
        .ADC_din        (ADC_din ),   //fpga给adc芯片的输出信号
        .ADC_dout       (ADC_dout),    //adc芯片给fpga的采样数据    
        .Sam_data       (Sam_data)
    );

    //模拟ADC采集的信号
    //复位信号上升沿检测
    reg rst_n_r;
    reg rst_flag=0;
    always@(posedge clk)begin
        rst_n_r <= rst_n;
    end
    always@(posedge clk)begin
        if((rst_n==1'b1)&&(rst_n_r==1'b0))
            rst_flag <= 1'b1;
        else
            rst_flag <= rst_flag;
    end
    //初始计数器
    reg [3:0] cnt_12;
    always@(posedge clk)begin
        if(!rst_n)
            cnt_12 <= 0;
        else if((rst_flag==1)&&(cnt_12<4'd11))
            cnt_12 <= cnt_12 +1'b1;
        else       
            cnt_12 <= cnt_12;
    end
    //有效循环计数器20*16=320
    reg [8:0] cnt_320;
    reg       cnt_320_flag;
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin 
            cnt_320 <= 9'd0;
            cnt_320_flag <= 1'b0;
        end
        else if(cnt_12 == 4'd11)begin
            if(cnt_320 == 9'd319)begin
                cnt_320 <= 9'd0;
                cnt_320_flag <= 1'b1;
            end
            else begin    
                cnt_320 <= cnt_320 + 1'b1;
                cnt_320_flag <= 1'b0;
            end
        end
        else begin
            cnt_320 <= 9'd0;
            cnt_320_flag <= 1'b0;
        end
    end
    //数据地址
    reg [6:0] addr_99;
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)
            addr_99 <= 7'd0;
        else if(cnt_320_flag == 1)begin
            if(addr_99 == 7'd99)
                addr_99 <= 0;
            else
                addr_99 <= addr_99 + 1'b1;
        end
        else
            addr_99 <= addr_99;
    end
    //状态赋值
    reg [11:0] sam_data_r;
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            ADC_dout <= 1'b0;
        end
        else begin
            case(cnt_320) 
                9'd1:begin sam_data_r <= adc_sam_data[addr_99]; end
                9'd81:begin  ADC_dout <= sam_data_r[11]; end
                9'd101:begin ADC_dout <= sam_data_r[10]; end
                9'd121:begin ADC_dout <= sam_data_r[9]; end
                9'd141:begin ADC_dout <= sam_data_r[8]; end
                9'd161:begin ADC_dout <= sam_data_r[7]; end
                9'd181:begin ADC_dout <= sam_data_r[6]; end
                9'd201:begin ADC_dout <= sam_data_r[5]; end
                9'd221:begin ADC_dout <= sam_data_r[4]; end
                9'd241:begin ADC_dout <= sam_data_r[3]; end
                9'd261:begin ADC_dout <= sam_data_r[2]; end
                9'd281:begin ADC_dout <= sam_data_r[1]; end
                9'd301:begin ADC_dout <= sam_data_r[0]; end
                default:begin ADC_dout <= ADC_dout; end
            endcase
        end 
    end 
endmodule

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128

5.仿真结果

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/459327
推荐阅读
相关标签
  

闽ICP备14008679号

        
cppcmd=keepalive&