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

这篇具有很好参考价值的文章主要介绍了【接口协议】FPGA实现SPI协议基于ADC128S022进行模拟信号采集。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

0.序言

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

1.SPI协议简介

(1)结构

SPI是串行外设接口,是一种同步/全双工/主从式接口。通常由四根信号线构成:
CS_N:片选信号,主从式接口,可以有多个从机,用片选信号进行从机选择;
SCLK:串行时钟线,由主机提供给从机;
MISO:主机接收(采集)从机数据信号线;
MOSI:主机发送数据给从机信号线;
adc128s022,【接口协议】FPGA,fpga开发

(2)工作模式

CKP:时钟极性,用来配置时钟线SCLK的电平处于何种状态是空闲状态或者有效状态;
CKE:时钟相位,配置发送数据和采集数据分别是在时钟上升沿还是下降沿;
adc128s022,【接口协议】FPGA,fpga开发

2.ADC128S022芯片简介

(1)ADC128S022

ADC128S022模数转换芯片有8个通道和12位的分辨率,时钟要求时钟频率范围在1~3.2MHz。可以看出芯片的八个通道(channel),在SPI协议中需要选择采集数据的通道。
adc128s022,【接口协议】FPGA,fpga开发

(2)信号时序

ADC128S022的SPI协议时序图如下图所示(由数据手册截出):
adc128s022,【接口协议】FPGA,fpga开发

3.实例

(1)结构框图

设计完成ADC128S022控制模块,根据指定的通道产生控制信号,控制ADC128S022进行连续采集模拟信号,最后采集的信号通过Sam_data传出。
adc128s022,【接口协议】FPGA,fpga开发

(2)时序图

A 控制信号(截选,因为完整的太长了只截了一部分)
adc128s022,【接口协议】FPGA,fpga开发

(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芯片数据手册给出的时序图)
这里给出了一个周期
adc128s022,【接口协议】FPGA,fpga开发
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中给出了初始状态和连续采集数据的有效循环状态,这里截出有效循环状态,以后一直保持有效循环:
adc128s022,【接口协议】FPGA,fpga开发

(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);

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

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

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

adc128s022,【接口协议】FPGA,fpga开发

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

(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

5.仿真结果

adc128s022,【接口协议】FPGA,fpga开发文章来源地址https://www.toymoban.com/news/detail-730521.html

到了这里,关于【接口协议】FPGA实现SPI协议基于ADC128S022进行模拟信号采集的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FPGA实现SPI接口(1)--什么是SPI接口?

    目录 1、什么是SPI协议 2、SPI协议详述 2.1、SPI协议物理层 2.2、SPI 协议层 2.3、SPI协议通信过程 2.4、SPI协议的特性 2.5、SPI协议的优势、劣势 3、驱动代码的设计实现 3.1、接口定义与整体设计 3.2、Verilog代码 4、Testbench及仿真结果 4.1、单个BYTE的仿真 4.2、多个BYTE的仿真 5、其他  

    2024年02月09日
    浏览(31)
  • 【LabVIEW FPGA入门】LabVIEW FPGA 实现SPI通信协议

            该实现由两个组件组成:在 LabVIEW FPGA 中实现的 SPI 协议以及用于从主机 PC 或实时控制器与 FPGA 进行通信的 LabVIEW 主机接口。该架构允许从单个主机程序控制多个 SPI 端口,同时仍然允许定制 FPGA VI 以进行其他数据采集和处理。该实现不使用任何DMA(直接内存访问

    2024年01月17日
    浏览(38)
  • FPGA常见接口及逻辑实现(三)—— SPI

    一、SPI协议简介         SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种同步串行接口,相对于之前介绍过的UART和I2C,SPI的速率就高出很多,最高能到100M左右,SPI协议比较简单,就不多做介绍,主要介绍下与UART和I2C的不同,SPI是一主多从协议,每个从机通过

    2024年04月26日
    浏览(21)
  • FPGA配置高速ADC篇(2)_4线SPI配置时序分析

    ​ 注:扫码关注小青菜哥哥的weixin公众号,免费获得更多优质的核探测器与电子学资讯~ ​ 本篇将以德州仪器(TI)的高速ADC芯片——ads52j90为例,进行ADC的4线SPI配置时序介绍与分析。 从ads52j90的数据手册我们不难发现,其SPI控制模块主要包含4根信号线,即SEN、SCLK、SDIN以及

    2024年01月24日
    浏览(32)
  • FPGA配置高速ADC篇(8)_AD9249三线SPI配置实战

    ​ 注:扫码关注小青菜哥哥的weixin公众号,免费获得更多优质的核探测器与电子学资讯~ ​ 本篇小青菜哥哥继续以ADI公司的16通道高速ADC—AD9249为实例,向大家演示FPGA是如何通过SPI接口向该ADC读写寄存器配置数据的。如下图所示为AD9249的功能框图,其为16通道、65MSPS、14bit精

    2024年01月25日
    浏览(34)
  • FPGA配置高速ADC篇(6)_AD9639四线SPI配置实战

    ​ 注:扫码关注小青菜哥哥的weixin公众号,免费获得更多优质的核探测器与电子学资讯~ ​ 前段时间小青菜哥哥写过几篇关于FPGA通过SPI接口配置高速ADC的文章,收到了很多朋友的意见和建议,如今在verilog的实现方式上又有了很大改进。因此小青菜哥哥打算再更新几篇关于这

    2024年02月22日
    浏览(35)
  • 基于SPI实现stm32与fpga通信(一)

    SPI通信协议有以下4种模式: 模式0:时钟极性为0,时钟相位为0,数据在时钟下降沿捕获,数据在时钟上升沿改变。 模式1:时钟极性为0,时钟相位为1,数据在时钟上升沿捕获,数据在时钟下降沿改变。 模式2:时钟极性为1,时钟相位为0,数据在时钟上升沿捕获,数据在时钟

    2024年04月16日
    浏览(31)
  • 基于ZYNQ FPGA的8路ADC数据采集与存储实现

    基于ZYNQ FPGA的8路ADC数据采集与存储实现 概述: 在工程设计和科学研究中,数据采集与存储是一个重要的任务。为了满足高速、高精度和大容量的数据采集需求,本文将介绍如何基于ZYNQ FPGA平台实现8路ADC数据采集与存储。通过合理的硬件设计和软件开发,我们可以实现快速而

    2024年02月11日
    浏览(35)
  • 【FPGA】SPI协议

    SPI(Serial Perripheral Interface, 串行外围设备接口)是 Motorola 公司推出的一种同步串行接口技术。SPI 总线在物理上是通过接在外围设备微控制器(PICmicro) 上面的微处理控制单元 (MCU) 上叫作同步串行端口(Synchronous Serial Port) 的模块(Module)来实现的, 它允许 MCU 以全双工的同步串行方式

    2024年02月09日
    浏览(24)
  • STM32模拟SPI协议获取24位模数转换(24bit ADC)芯片AD7791电压采样数据

    STM32大部分芯片只有12位的ADC采样性能,如果要实现更高精度的模数转换如24位ADC采样,则需要连接外部ADC实现。AD7791是亚德诺(ADI)半导体一款用于低功耗、24位Σ-Δ型模数转换器(ADC) ,适合低频测量应用,提供50 Hz/60 Hz同步抑制。 这里介绍基于AD7791的24位ADC采样实现。 AD7791的管脚

    2024年02月09日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包