AD7606 ADC的SPI驱动——FPGA学习笔记17

news2024/11/26 15:30:04

                                                                                                                素材来源             米联客

一、AD7606简介

功能框图:

转换控制时序:

        AD7606 支持 2 种时序转换, 由于我们采用的时串行 SPI 模式, 本身 SPI 读取数据就会耽误很多时间, 所以必须采用第二种工作时序, 才能确保 200Kbps 的采样率。

         空闲SCLK为高,CPOL为1       在SCLK第一个上升沿进行数据读取,CPHA为1

         当数据完成转换后, CS 为电平期间, 每个时钟的上升沿完成数据的采样, 表中 FRSTDATA 代表每次转换的第一个数据, 这里实际可以不用这个信号, 因为每次 CS 之后的第一个上升沿时钟我们就是开始采样数据, 每 16 次完成一路 ADC 的采集。        

        对于 AD7606 具有 8 路 ADC, DOUTA 和 DOUTB 各 4 路。 DOUTA 对于 1~4 路 ADC 通道, DOUTB 对应 5~8路 ADC 通道。而且 ADC 的 2 组 4 个通道可以一次性完成采集, 也就是说 16X4 共计 64 个时钟上升沿完成 4 路 ADC采集

引脚说明:

 

模式选择:

参数说明:

 

 

串行读取:

二、硬件设计

                                                                                                                        来源:中瑞科技

 

三、代码编写

SPI驱动部分:

`timescale 1ns / 1ps

module spi7606( 
input        	    I_ad_clk        ,           //系统时钟输入   
input               I_ad_rst        ,           //系统复位输入
input        	    I_ad_busy       ,           //ad7606 忙标志位
output      [2:0] 	O_ad_os         ,           //ad7606 过采样倍率选择,本驱动不使用
output    	        O_ad_cs         ,           //ad7606 CS信号输出,低电平SPI数据线输出AD7606寄存器数据
output reg     	    O_ad_sclk       ,           //ad7606 SCLK时钟输出
output       	    O_ad_rst        ,           //ad7606 复位输出
output    	        O_ad_convsta    ,           //ad7606 A组通道转换
output    	        O_ad_convstb    ,           //ad7606 B组通道转换
output              O_ad_range      ,           //ad7606 模拟输入范围,设置1范围:±10V,设置0范围±5V
input               I_ad_out_a      ,           //串行A组通道采集输入,V1,V2,V3,V4  
input               I_ad_out_b      ,           //串行B组通道采集输入, V5,V6,V7,V8  
output reg  [63:0]  O_ad_out_a      ,           //A组通道采集有效数据输出
output reg  [63:0]  O_ad_out_b      ,           //B组通道采集有效数据输出
output              O_ad_cap_en                 //采集完成使能
);
localparam  CLK_FREQ =  100000000                   ;
localparam  SET_5US  =  5                           ; //5us周期
localparam  T5US_DIV =  CLK_FREQ*SET_5US/1000000 -1 ; //计算5us 分频系数
localparam  SPI_DIV  =  CLK_FREQ/16666666 -1        ; //产生SPI时钟,设置最高16.666667MHZ


assign O_ad_range = 1'b1;    //±10V真直流输入范围
assign O_ad_os    = 3'b000;  //无过采样

//ad复位时间高电平,复位时间最少50ns
reg [23: 0] rst_cnt;
assign O_ad_rst   = !rst_cnt[23];
always@(posedge I_ad_clk or posedge I_ad_rst)begin        
    if(I_ad_rst) begin
        rst_cnt  <= 24'd0;
    end
    else if(!rst_cnt[23]) begin
        rst_cnt  <= rst_cnt + 1'b1;
    end
end       
	
//设置采样频率,AD7606为8通道可以工作于200Kbps采样率,因此采样周期为5us  1KSPS=1KHz,1MSPS=1MHz
reg [9:0] tcnt5us;
wire cycle_end = (tcnt5us == T5US_DIV);
always@ (posedge I_ad_clk)begin   
    if(O_ad_rst) begin
        tcnt5us <= 10'd0;
    end
    else if(tcnt5us < T5US_DIV) begin
        tcnt5us <= tcnt5us + 1'b1;
    end
    else begin
         tcnt5us <= 10'd0;
    end
end

localparam [9:0] SPI_DIV1    = SPI_DIV/2; //半周期分频
reg [9:0] clk_div = 10'd0;//分频计数器	

//SPI 时钟分频,对于200K采样,设置20M时钟
always@(posedge I_ad_clk)begin
    if(clk_div < SPI_DIV) begin
        clk_div <= clk_div + 1'b1;
    end
    else begin
        clk_div <= 10'd0;
    end
end

//产生SPI时钟
wire clk_en1 = (clk_div == SPI_DIV1);   // 半周期使能,控制输出0
wire clk_en2 = (clk_div == SPI_DIV);    //  半周期使能,控制输出1

always@(posedge I_ad_clk)begin
    if(clk_en2) begin
        O_ad_sclk <= 1'b1; //输出高电平
    end
    else if(clk_en1) begin
        O_ad_sclk <= 1'b0; //输出高低平
    end
end

//AD转换状态机
reg [1:0] AD_S;
reg ad_convst; //ADC转换控制信号,A组通道和B组通道采用同一通道
//ADC工作于SPI模式,以及边读边转换模式,本次数据转换的同时,可以读出前一次转换的结果
assign O_ad_cs = ~((AD_S == 2'd3)&&I_ad_busy);//当AD7606工作在串行模式,cs=0代表输出通过SPI数据总线,输出之前的采样数据
assign O_ad_convsta = ad_convst; //ADC转换控制信号,A组通道和B组通道采用同一通道
assign O_ad_convstb = ad_convst; //ADC转换控制信号,A组通道和B组通道采用同一通道

always @(posedge I_ad_clk) 
begin
    if(O_ad_rst||I_ad_rst)begin
        ad_convst <= 1'b1;     
        AD_S <= 2'd0; 
    end
    else begin
        case(AD_S)  
        2'd0:if(clk_en2)begin    //clk_en2,控制SCLK输出1,因此这里相当于SCLK上升沿
            ad_convst <= 1'b0;  //设置ad_convst=0
            AD_S <= 2'd1;//下一状态
        end           
        2'd1:if(clk_en2)begin //延迟50ns,ad_convst低电平至少25ns,对于SCLK时钟是20MHZ,所以这里ad_convst低电平是50ns
            ad_convst <= 1'b1;//ad_convst 低电平50ns后拉高为高电平
            AD_S <= 2'd2;//下一状态
        end 
        2'd2:if(clk_en2&&I_ad_busy)begin//延迟50ns,并且等待busy高,只要ADC正常工作,busy必然为高,说明ADC处于采样转换下
            AD_S <= 2'd3;   
        end         
        2'd3:if(cycle_end)begin//ADC转换时间最大4.2us,5us周期结束,代表本次8通道完全采集结束
            AD_S <= 2'd0; //回到初始状态,进行下一次采样   
        end
        endcase    
     end             
end

//SPI采样
reg [7 : 0] nbits; // bits计数器,用于计数完成了从AD7606 SPI总线读出的bits数
wire ad_cap_en_r1 = (nbits==8'd64); //数据同步输出使能
reg  ad_cap_en_r2 = 1'b0; //数据同步输出使能寄存一次
assign O_ad_cap_en = ({ad_cap_en_r1,ad_cap_en_r2}==2'b10);//高电平输出系统时钟的一个周期

always@(posedge I_ad_clk) begin //寄存一次ad_cap_en_r1
    ad_cap_en_r2 <= ad_cap_en_r1;
end

//当CS信号为低电平,每个SCL的下降沿输出采样数据,每组通道采样64bits
wire cap_en  = (!O_ad_cs)&&clk_en1&&(nbits<8'd64);

//ADC 串并转换模块
always@(posedge I_ad_clk) begin
    if(O_ad_rst) begin //ADC复位期间重置相关寄存器
        O_ad_out_a  <= 64'd0; 
        O_ad_out_b  <= 64'd0; 
        nbits       <= 8'd0;
    end 
    else if(O_ad_cs)begin //高电平,重置  nbits   
        nbits   <= 8'd0;                       
    end   
    else if(cap_en)begin//当CS信号为低电平,每个SCL的下降沿采样数据,每组通道采样64bits
        nbits    <= nbits + 1'b1; //
        O_ad_out_a <= {O_ad_out_a[62:0],I_ad_out_a};//保存A组通道数据,V1~V4,每个通道16bits
        O_ad_out_b <= {O_ad_out_b[62:0],I_ad_out_b};//保存B组通道数据,V5~V8,每个通道16bits
    end                                                                                                                                    
end
                  
endmodule

顶层状态转移:

`timescale 1ns / 1ns
module ad7606_top
(
input           I_sysclk,         //系统时钟输入   
input        	I_ad_busy,        //ad7606 忙标志位
output    	    O_ad_cs,          //ad7606 CS信号输出,低电平SPI数据线输出AD7606寄存器数据
output      	O_ad_sclk,        //ad7606 SCLK时钟输出
output       	O_ad_rst,         //ad7606 复位输出
output    	    O_ad_convsta,     //ad7606 A组通道转换
output    	    O_ad_convstb,     //ad7606 B组通道转换
output          O_ad_range,       //ad7606 模拟输入范围,设置1范围:±10V,设置0范围±5V
input           I_ad_out_a,       //串行A组通道采集输入,V1,V2,V3,V4  
input           I_ad_out_b,       //串行B组通道采集输入, V5,V6,V7,V8  
output          O_card_power_en     //子卡电源使能
);

assign O_card_power_en = 1'b1; //子卡上电

wire clk100M,locked;
clk_wiz_0  clk_7606_inst(.clk_out1(clk100M),.locked(locked),.clk_in1(I_sysclk)); 

//ad7606 ip相关信号
wire ad_clk_i,ad_rst_i,ad_cap_en; 
wire [63:0] ad_out_a,ad_out_b;
assign ad_clk_i      = clk100M; //ADC时钟
assign ad_rst_i      = !locked; //ADC IP内部代码复位


spi7606 spi7606_inst
(
.I_ad_clk(ad_clk_i),    //系统时钟输入               
.I_ad_rst(ad_rst_i),    //系统复位输入
.I_ad_busy(I_ad_busy),  //ad7606 忙标志位               
.O_ad_cs(O_ad_cs),      //ad7606 CS信号输出,低电平SPI数据线输出AD7606寄存器数据
.O_ad_sclk(O_ad_sclk),  //ad7606 SCLK时钟输出    
.O_ad_rst(O_ad_rst),    //ad7606 复位输出        
.O_ad_convsta(O_ad_convsta), //ad7606 A组通道转换     
.O_ad_convstb(O_ad_convstb), //ad7606 B组通道转换  
.O_ad_range(O_ad_range),     //ad7606 模拟输入范围,设置1范围:±10V,设置0范围±5V 
.I_ad_out_a(I_ad_out_a),     //串行A组通道采集输入,V1,V2,V3,V4  
.I_ad_out_b(I_ad_out_b),     //串行B组通道采集输入, V5,V6,V7,V8  
.O_ad_out_a(ad_out_a),         //A组通道采集有效数据输出
.O_ad_out_b(ad_out_b),         //B组通道采集有效数据输出
.O_ad_cap_en(ad_cap_en)        //采集完成使能
 );


wire [15:0] ad_ch1 = ad_out_a[63:48];//ADC 通道1
wire [15:0] ad_ch2 = ad_out_a[47:32];//ADC 通道2
wire [15:0] ad_ch3 = ad_out_a[31:16];//ADC 通道3
wire [15:0] ad_ch4 = ad_out_a[15: 0];//ADC 通道4
wire [15:0] ad_ch5 = ad_out_b[63:48];//ADC 通道5
wire [15:0] ad_ch6 = ad_out_b[47:32];//ADC 通道6
wire [15:0] ad_ch7 = ad_out_b[31:16];//ADC 通道7
wire [15:0] ad_ch8 = ad_out_b[15: 0];//ADC 通道8

//例化 ILA IP 用于在线逻辑分仪观察ADC采样值
ila_0 ila_debug 
(
.clk(ad_clk_i), //系统时钟
.probe0(ad_cap_en), //AD采集数据使能,ILA中使用capture功能,可以观察到更多有效数据
.probe1(ad_ch1),//ADC 通道1
.probe2(ad_ch2),//ADC 通道2
.probe3(ad_ch3),//ADC 通道3
.probe4(ad_ch4),//ADC 通道4
.probe5(ad_ch5),//ADC 通道5
.probe6(ad_ch6),//ADC 通道6
.probe7(ad_ch7),//ADC 通道7
.probe8(ad_ch8), //ADC 通道8       
.probe9(O_ad_cs),//ADC 通道7
.probe10(O_ad_sclk),//ADC 通道7
.probe11(O_ad_rst), //ADC 通道8     
.probe12(O_ad_convsta),//ADC 通道7
.probe13(I_ad_out_a), //ADC 通道8   
.probe14(O_ad_convstb),//ADC 通道7
.probe15(I_ad_out_b) //ADC 通道8                
); 

endmodule

四、仿真波形

五、上板验证

 

触发方式:

六、Bug解决

问题描述:

vivado 仿真时出现 boost::filesystem::remove: 另一个程序正在使用此文件,进程无法访问。

使用了外部编译器VScode,关闭外部编译器即可正常仿真 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2190199.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

呆仔君最新可用版本及作废版本说明截止日期10.5

呆仔君最新可用版本及作废版本说明 当前最新可用版本为 5.0 及以上版本。 截至今天&#xff0c;最新版本为 5.3。本次 5.3 版本的更新功能如下&#xff1a; 新增清悦阁&#xff08;用于搜歌&#xff09;新增手绘功能新增语音合成&#xff08;包括卢本伟、刘华强、特朗普等人物…

[大语言模型-论文精读] 更大且更可指导的语言模型变得不那么可靠

[大语言模型-论文精读] 更大且更可指导的语言模型变得不那么可靠 目录 文章目录 [大语言模型-论文精读] 更大且更可指导的语言模型变得不那么可靠目录0. 摘要1. 核心内容3. 创新点4. 算法模型5. 实验效果6. 重要数据与实验结论7. 推荐阅读指数&#xff1a;8. 推荐理由 后记 论文…

【Java】—— 集合框架:Collections工具类的使用

目录 7. Collections工具类 7.1 常用方法 7.2 举例 7.3 练习 7. Collections工具类 参考操作数组的工具类&#xff1a;Arrays&#xff0c;Collections 是一个操作 Set、List 和 Map 等集合的工具类。 7.1 常用方法 Collections 中提供了一系列静态的方法对集合元素进行排序…

超声波清洗机什么牌子值得入手?推荐四款入手不亏的眼镜清洗机

在当今这个注重细节完美的时代&#xff0c;超声波清洗机凭借其卓越的清洁效率、深层渗透力及细腻的清洗效果&#xff0c;迅速赢得了家庭与专业场景的青睐。无论是精细的珠宝、眼镜框&#xff0c;还是金属装饰品、电子设备乃至医疗器具&#xff0c;超声波技术都能精准祛除隐秘处…

汇编语言笔记2

7.MASM,NASM,ATT,ARM的介绍 MASM:Windows下编译汇编指令的软件,可以在DOSBox下运行 NASM:优化版的MASM,主要用于Linux操作系统 ATT:Linux默认的汇编风格(但不友好) ARM:非PC(IOT设备)的汇编,比如写51单片机打开keil4的界面可以看到ARM 8.汇编 C语言 C 之间的关系 发展历程…

45集 ESP32 ADC按键程序编写

45集 ESP32 ADC按键程序编写 参考例程是 D:\Espressif\esp-adf\examples\checks\check_board_buttons 1、config 这个向下兼容的要加上&#xff0c;如果不加会有错误。 CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITYy 2、程序里面引入如下头文件 #include “periph_adc_but…

Python+Matplotlib可视化y = e^(1/x)函数

可视化y e^(1/x)函数&#xff1a; import numpy as np import matplotlib.pyplot as pltplt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] Falsefig, ax plt.subplots(figsize(12, 8))def e_to_1_over_x(x):return np.where(x ! 0, np.exp(1/x), …

C初阶(十二)do - while循环 --- 致敬革命烈士

大家国庆看阅兵仪式和天安门升旗仪式了吗&#xff1f;岁月安好&#xff0c;只因有人负重前行。 ————山那边是什么 ————是烈士的英魄 ————是他们拼死保卫的新中国 ————河那边是什么 ————是绵延的战火 ————她望着远方泪一滴滴的落 ————和平来了 ——…

Arduino UNO R3自学笔记20 之 Arduino如何测定电机速度?

注意&#xff1a;学习和写作过程中&#xff0c;部分资料搜集于互联网&#xff0c;如有侵权请联系删除。 前言&#xff1a;在学习了Arduino的相关基础知识后&#xff0c;现在做个综合应用&#xff0c;给旋转的电机测速。 1.实验目的 测定旋转电机的转速。 2.实验器材-编码器 …

比较搜索难度曲线5s1-4和4s1

在行列可自由变换的条件下&#xff0c;平面上的5点结构只有34个,4点结构有16个 (A,B)---6*n*2---(0,1)(1,0) 让B全是0。当收敛误差为7e-4&#xff0c;收敛199次取迭代次数平均值。让隐藏层节点数n分别为10&#xff0c;15&#xff0c;20&#xff0c;25&#xff0c;30&#xff…

一些关于上传数据-p7zip-full-压缩包的经验

目录 前言 7z 简介 Windows如何压缩tar.gz格式 一、下载7-ZIP 二、tar文件进一步压缩 说明&#xff1a; 前言 本人每次在linux服务器上执行apt-get install p7zip-full命令&#xff0c;都会有复杂依赖报错&#xff08;因为实验过程中用到的依赖包太多了&#xff09;&…

今日指数项目个股周K线功能实现

个股周K线功能实现 1 个股周K线功能实现功能分析 1&#xff09;个股周K线功能原型分析 2&#xff09;个股周K线功能接口分析 个股周K线数据主要包含&#xff1a; 股票ID、 一周内最高价、 一周内最低价 、周1开盘价、周5的收盘价、 整周均价、以及一周内最大交易日期&#x…

【Linux】-----进程第二弹(优先级,环境变量)

目录 一、进程优先级 是什么 为什么要有&#xff1f; 查看进程优先级 修改进程优先级 二、环境变量 命令行参数 概念 常见环境变量 查看环境变量 配置环境变量 内存级别修改&#xff08;命令行修改&#xff0c;暂时&#xff09; ①拷贝到系统路径下 ② 路径添加…

【Android】中级控件

其他布局 相对布局RelativeLayout RelativeLayout下级视图的位置是相对位置&#xff0c;得有具体的参照物才能确定最终位置。如果不设定下级视图的参照物&#xff0c;那么下级视图默认显示在RelativeLayout内部的左上角。用于确定视图位置的参照物分两种&#xff0c;一种是与…

算法专题三: 二分查找

目录 1. 朴素版: 二分查找2. 查找排序数组元素第一个和最后一个位置3. 搜索插入位置4. x的平方根5. 山脉数组的峰顶索引6. 寻找旋转数组中的最小值7. 点名 博客主页: 酷酷学!!! 感谢您的关注~ 正文开始 1. 朴素版: 二分查找 题目思路: 仅需根据题意, 找出二段性, 正确更新下标…

Python编码系列—Python访问者模式:为对象结构添加新功能的艺术

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

LLM端侧部署系列 | PowerInfer-2助力AI手机端侧部署47B大模型 (论文解读)

引言 简介 PowerInfer-2 概述 神经元感知的运行时推理 多态神经元引擎 内存中的神经元缓存 灵活的神经元加载 Neuron-Cluster-Level Pipeline 生成执行计划 执行 总结 0. 引言 一雨池塘水面平&#xff0c;淡磨明镜照檐楹。东风忽起垂杨舞&#xff0c;更作荷心万点声…

2024年liunx安装openvino非源码编译版(比源码编译简单!)

前言 真的要感慨一句&#xff0c;openvino源码编译真的麻烦&#xff01;由于2023年之后openvino官网的之间下载取消之后&#xff0c;很多人只能选择源码编译&#xff0c;我也是研究了好几天&#xff0c;又是clone改变的库&#xff0c;又是安装什么&#xff0c;搞了三四天都没有…

已解决:TypeError: ‘int‘ object is not iterable

已解决&#xff1a;TypeError: ‘int’ object is not iterable 文章目录 写在前面问题描述报错原因分析 解决思路解决办法1. 检查代码中的 for 循环2. 检查函数返回值是否为可迭代对象3. 确认变量类型4. 使用 map() 或 list comprehension 处理整数5. 防止不必要的迭代 总结 写…

Leetcode—148. 排序链表【中等】

2024每日刷题&#xff08;171&#xff09; Leetcode—148. 排序链表 C实现代码 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr…