Intel FPGA (7):adc adc128s102

Intel FPGA (7):adc adc128s102

前提摘要

  1. 个人说明:

    • 限于时间紧迫以及作者水平有限,本文错误、疏漏之处恐不在少数,恳请读者批评指正。意见请留言或者发送邮件至:“Email:noahpanzzz@gmail.com”
    • 本博客的工程文件均存放在:GitHub:https://github.com/panziping。
    • 本博客的地址:CSDN:https://blog.csdn.net/ZipingPan
  2. 参考:

    • 芯片型号:Intel EP4CE10F17C8(Cyclone IV E)
    • 《数字电子技术基础》-阎石
    • 《FPGA自学笔记—设计与验证》袁玉卓,曾凯锋,梅雪松
    • 《Verilog 数字系统设计教程》夏宇闻
    • 《Verilog HDL 高级数字设计》Michael D.Ciletti
    • 《Intel FPGA/CPLD设计》(基础篇)王欣 王江宏等
    • 《Intel FPGA/CPLD设计》(高级篇)王江宏 蔡海宁等
    • 《综合与时序分析的设计约束 Synopsys设计约束(SDC)实用指南》Sridhar Gangadharan
  3. 日期:

    • 2024-01-01

正文

模数转换器(英语:analog to Digital converter,英文缩写:ADC)是一种将模拟信号(例如麦克风拾取的声音或进入数码相机的光线)转换为数字信号的系统。

SPS(sample per second,每秒采样次数),是衡量模数转换(ADC)时采样速率的单位。采样率定义为对输入信号输入信的采样频率,采样率不仅表示模数转换器的转换速度,同时也决定了系统可处理信号的带宽范围。

比如这款ADC芯片adc128s102,使用的是SPI进行传输,完整传输一次数据所需要花费的时间是16个sclk周期。

这里假设sclk的时钟频率为8MHz
一次采样周期 = 16 ∗ 1 f s c l k = 16 ∗ 125 n s = 2000 n s S P S = 1 s 2000 n s = 500000 = 500 K S P S 一次采样周期=16*\frac{1}{f_{sclk}}=16*125ns = 2000ns\\ SPS = \frac{1 s}{2000 ns} = 500 000 = 500KSPS 一次采样周期=16fsclk1=16125ns=2000nsSPS=2000ns1s=500000=500KSPS
本篇采用的ADC芯片是adc128s102。这是一款8通道,500KSPS到1MSPS,12bit的ADC。

硬件电路

在这里插入图片描述

ADC128S102

这里截取了AC128S102的部分数据手册,读者自行阅读。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

ADC为12位分辨率,因此1bit代表的电压值为 V A V_{A} VA/4096。当模拟输入电压低于 V A V_{A} VA/8192时,输出数据为0000_0000_0000b。同理由于芯片本身内部构造当输出数据0000_0000_0000b变为0000_0000_0001b时,实际输入电压变化为 V A V_{A} VA/8192而不是 V A V_{A} VA/4096。当输入电压大于或等于 V A V_{A} VA-1.5 V A V_{A} VA/4096时,输出数据即为1111_1111_1111b。*

代码展示

adc128s102底层驱动代码:

module adc128s102_driver(clk,rst_n,adc_addr,adc_convert_en_go,adc_cs_n,adc_sclk,adc_dout,adc_din,adc_convert_busy,adc_data,adc_data_convert_valid_go
);input clk;input rst_n;input [2:0] adc_addr;input adc_convert_en_go;output adc_cs_n;output adc_sclk;input  adc_dout;output adc_din;output adc_convert_busy;output [11:0] adc_data;output adc_data_convert_valid_go;reg [2:0] r_adc_addr;always@(posedge clk or negedge rst_n) beginif(!rst_n)r_adc_addr <= 3'd0;else if(adc_convert_en_go == 1'b1)r_adc_addr <= adc_addr;elser_adc_addr <= r_adc_addr;endreg r_adc_convert_en;wire w_adc_convert_end;always@(posedge clk or negedge rst_n) beginif(!rst_n)r_adc_convert_en <= 1'b0;else if(adc_convert_en_go == 1'b1)r_adc_convert_en <= 1'b1;else if(w_adc_convert_end == 1'b1)r_adc_convert_en <= 1'b0;elser_adc_convert_en <= r_adc_convert_en;endassign adc_convert_busy = r_adc_convert_en;localparam SYSCLK = 50_000_000;localparam SPI_CLK = 6_250_000;localparam SPI_CLK_DR = SYSCLK / SPI_CLK;reg [$clog2(SPI_CLK_DR)-1:0] r_div_cnt;always@(posedge clk or negedge rst_n) beginif(!rst_n)r_div_cnt <= 'd0;else if(r_adc_convert_en == 1'b1) beginif(r_div_cnt == SPI_CLK_DR - 1'b1)r_div_cnt <= 'd0;elser_div_cnt <= r_div_cnt + 1'b1;endelser_div_cnt <= 'd0;endwire w_sclk_pluse;assign w_sclk_pluse = (r_div_cnt == 'd1) ? 1'b1 : 1'b0; reg [5:0] r_bit_cnt;always@(posedge clk or negedge rst_n) beginif(!rst_n)r_bit_cnt <= 6'd0;else if(r_adc_convert_en == 1'b1) beginif(w_sclk_pluse == 1'b1)r_bit_cnt <= r_bit_cnt + 1'b1;elser_bit_cnt <= r_bit_cnt;endelser_bit_cnt <= 6'd0;endassign w_adc_convert_end = (r_bit_cnt == 6'd35) ? 1'b1 : 1'b0;//fpga negedge output addr data,adc posedge collect addr data//fpga posedge collect adc data,adc negedge output adc datareg r_adc_cs_n;reg [11:0] r_adc_data;reg r_adc_sclk;reg r_adc_din;always@(posedge clk or negedge rst_n) beginif(!rst_n) beginr_adc_data <= 12'd0;r_adc_cs_n <= 1'd1;r_adc_sclk <= 1'b1;r_adc_din <= 1'b1;endelse begincase(r_bit_cnt)6'd0: begin r_adc_cs_n <= 1'b1; r_adc_sclk <= 1'b1; r_adc_din <= 1'b1; end6'd1: begin r_adc_cs_n <= 1'b0; r_adc_sclk <= 1'b1; r_adc_din <= 1'b1; end6'd2: begin r_adc_sclk <= 1'b0; r_adc_din <= 1'b1; end6'd3: begin r_adc_sclk <= 1'b1; r_adc_din <= 1'b1; end6'd4: begin r_adc_sclk <= 1'b0; r_adc_din <= 1'b1; end6'd5: begin r_adc_sclk <= 1'b1; r_adc_din <= 1'b1; end6'd6: begin r_adc_sclk <= 1'b0; r_adc_din <= r_adc_addr[2]; end6'd7: begin r_adc_sclk <= 1'b1; end6'd8: begin r_adc_sclk <= 1'b0; r_adc_din <= r_adc_addr[1]; end6'd9: begin r_adc_sclk <= 1'b1; end	6'd10: begin r_adc_sclk <= 1'b0; r_adc_din <= r_adc_addr[0]; end6'd11: begin r_adc_sclk <= 1'b1; r_adc_data[11] <= adc_dout; end6'd12: begin r_adc_sclk <= 1'b0; end6'd13: begin r_adc_sclk <= 1'b1; r_adc_data[10] <= adc_dout; end		6'd14: begin r_adc_sclk <= 1'b0; end6'd15: begin r_adc_sclk <= 1'b1; r_adc_data[9] <= adc_dout; end	6'd16: begin r_adc_sclk <= 1'b0; end6'd17: begin r_adc_sclk <= 1'b1; r_adc_data[8] <= adc_dout; end	6'd18: begin r_adc_sclk <= 1'b0; end6'd19: begin r_adc_sclk <= 1'b1; r_adc_data[7] <= adc_dout; end				6'd20: begin r_adc_sclk <= 1'b0; end6'd21: begin r_adc_sclk <= 1'b1; r_adc_data[6] <= adc_dout; end			6'd22: begin r_adc_sclk <= 1'b0; end6'd23: begin r_adc_sclk <= 1'b1; r_adc_data[5] <= adc_dout; end			6'd24: begin r_adc_sclk <= 1'b0; end6'd25: begin r_adc_sclk <= 1'b1; r_adc_data[4] <= adc_dout; end				6'd26: begin r_adc_sclk <= 1'b0; end6'd27: begin r_adc_sclk <= 1'b1; r_adc_data[3] <= adc_dout; end			6'd28: begin r_adc_sclk <= 1'b0; end6'd29: begin r_adc_sclk <= 1'b1; r_adc_data[2] <= adc_dout; end	6'd30: begin r_adc_sclk <= 1'b0; end6'd31: begin r_adc_sclk <= 1'b1; r_adc_data[1] <= adc_dout; end		6'd32: begin r_adc_sclk <= 1'b0; end6'd33: begin r_adc_sclk <= 1'b1; r_adc_data[0] <= adc_dout; end	6'd34: begin r_adc_cs_n <= 1'b1; r_adc_sclk <= 1'b1; r_adc_din <= 1'b1;end	6'd35: begin r_adc_cs_n <= 1'b1; r_adc_sclk <= 1'b1; r_adc_din <= 1'b1;end		default:begin r_adc_cs_n <= 1'd1;r_adc_sclk <= 1'b1; r_adc_din <= 1'b1;endendcaseendendassign adc_cs_n = r_adc_cs_n;assign adc_sclk = r_adc_sclk;assign adc_din = r_adc_din;assign adc_data = r_adc_data;reg r_adc_data_valid_go;always@(posedge clk or negedge rst_n) beginif(!rst_n)r_adc_data_valid_go <= 1'b0;else if(r_bit_cnt == 6'd34 && r_div_cnt == SPI_CLK_DR - 1'b1) r_adc_data_valid_go <= 1'b1;elser_adc_data_valid_go <= 1'b0;endassign adc_data_convert_valid_go = r_adc_data_valid_go;endmodule

ADC控制代码:

module adc_ctrl(clk,rst_n,adc_addr,key_press_valid_go,adc_cs_n,adc_sclk,adc_dout,adc_din,adc_data,adc_data_valid_go
);input 			clk;input 			rst_n;input 			key_press_valid_go;input [2:0]		adc_addr;output 			adc_cs_n;output 			adc_sclk;input  			adc_dout;output 			adc_din;output [11:0] adc_data;output 		  adc_data_valid_go;wire w_adc_convert_en_go;wire w_adc_convert_busy;wire [11:0] w_adc_data;wire w_adc_data_convert_valid_go;
adc128s102_driver adc_driver(.clk(clk),.rst_n(rst_n),.adc_addr(adc_addr),.adc_convert_en_go(w_adc_convert_en_go),.adc_cs_n(adc_cs_n),.adc_sclk(adc_sclk),.adc_dout(adc_dout),.adc_din(adc_din),.adc_convert_busy(w_adc_convert_busy),.adc_data(w_adc_data),.adc_data_convert_valid_go(w_adc_data_convert_valid_go)
);reg [1:0]r_adc_convert_busy_sync;//wire w_adc_convert_busy_pedge;wire w_adc_convert_busy_nedge;always@(posedge clk or negedge rst_n) beginif(!rst_n)r_adc_convert_busy_sync <= 2'd0;elser_adc_convert_busy_sync <= {r_adc_convert_busy_sync[0],w_adc_convert_busy};end//assign w_adc_convert_busy_pedge = (r_adc_convert_busy_sync == 2'b01) ? 1'b1 : 1'b0;assign w_adc_convert_busy_nedge = (r_adc_convert_busy_sync == 2'b10) ? 1'b1 : 1'b0; localparam ADC_COLLECT_TIMES = 100;localparam S_IDLE  	= 4'b0001;localparam S_START 	= 4'b0010;localparam S_SAMPLE = 4'b0100;localparam S_DONE 	= 4'b1000;reg [3:0] r_current_state;reg [3:0] r_next_state;reg [$clog2(ADC_COLLECT_TIMES)-1:0] r_sample_cnt;always@(posedge clk or negedge rst_n) beginif(!rst_n)r_current_state <= S_IDLE;elser_current_state <= r_next_state;endalways@(*) begincase(r_current_state)S_IDLE: beginif(key_press_valid_go == 1'b1)r_next_state = S_START;elser_next_state = S_IDLE;endS_START: beginif(w_adc_data_convert_valid_go == 1'b1)r_next_state = S_SAMPLE;elser_next_state = S_START;endS_SAMPLE: beginif(r_sample_cnt == ADC_COLLECT_TIMES)r_next_state = S_DONE;elser_next_state = S_SAMPLE;endS_DONE: beginif(w_adc_convert_busy_nedge == 1'b1)r_next_state = S_IDLE;elser_next_state = S_DONE;enddefault: r_next_state = S_IDLE;endcaseendalways@(posedge clk or negedge rst_n) beginif(!rst_n)r_sample_cnt <= 'd0;else if(r_current_state == S_SAMPLE) beginif(w_adc_data_convert_valid_go == 1)r_sample_cnt <= r_sample_cnt + 1'b1;elser_sample_cnt <= r_sample_cnt;endelser_sample_cnt <= 'd0;endreg r_adc_convert_en_go;always@(posedge clk or negedge rst_n) beginif(!rst_n)r_adc_convert_en_go <= 1'b0;else if(r_current_state == S_IDLE && r_next_state == S_START)r_adc_convert_en_go <= 1'b1;else if(r_current_state == S_SAMPLE && w_adc_convert_busy_nedge == 1'b1)r_adc_convert_en_go <= 1'b1;elser_adc_convert_en_go <= 1'b0;endassign w_adc_convert_en_go = r_adc_convert_en_go;reg r_adc_data_valid_go;always@(posedge clk or negedge rst_n) beginif(!rst_n) beginr_adc_data_valid_go <= 'd0;endelse if(r_current_state == S_SAMPLE && w_adc_data_convert_valid_go == 1 'b1)r_adc_data_valid_go <= 1'b1;elser_adc_data_valid_go <= 1'b0;endassign adc_data_valid_go = r_adc_data_valid_go;assign adc_data = w_adc_data;endmodule

ADC控制代码有几点需要注意:

  1. adc128s102在输入地址之后,adc输出的数据并不是当前地址通道的数据,而是在下一次输入地址之后得到想要的数据。所以状态机中START跳转到SAMPLE的条件是第一次转换完成脉冲信号(脉冲信号的宽度必须是一个时钟周期!!!)。那么在SAMPLE状态中就能得到想要的数据。

总结

本文后续将AD采集到数据通过uart输出。具体工程为adda,如有需要请至github仓库查看!!!


本文均为原创,欢迎转载,请注明文章出处:CSDN:https://blog.csdn.net/ZipingPan。百度和各类采集站皆不可信,搜索请谨慎鉴别。技术类文章一般都有时效性,本人习惯不定期对自己的博文进行修正和更新,因此请访问出处以查看本文的最新版本。

非原创博客会在文末标注出处,由于时效原因,可能并不是原创作者地址(已经无法溯源)。

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

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

相关文章

ApiFox 使用教程

ApiFox 使用教程 目录概述需求&#xff1a; 设计思路实现思路分析1.基本使用教程&#xff08;Apifox 使用 Postman&#xff09;Apifox 下使用 mockapifox 下 swaggerApifox 下使用 Jmeter 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show…

用Python实现办公自动化(自动化处理PDF文件)

自动化处理 PDF 文件 目录 自动化处理 PDF 文件 谷歌浏览器 Chrome与浏览器驱动ChromeDriver安装 &#xff08;一&#xff09;批量下载 PDF 文件 1.使用Selenium模块爬取多页内容 2.使用Selenium模块下载PDF文件 3.使用urllib模块来进行网页的下载和保存 4.使用urllib…

关于OcenaBase v4.2中,分区转移和负载均衡的技术解读

OceanBase​​​​​​​​​​​​​​作为一款原生分布式数据库&#xff0c;其核心的技术特性之一是高可扩展性&#xff0c;其具体表现在两个方面&#xff1a; 首先&#xff0c;是灵活的扩缩容能力&#xff0c;包括垂直扩缩容和水平扩缩容&#xff1a; 垂直扩缩容&#xff…

Dubbo 3.x源码(18)—Dubbo服务引用源码(1)

基于Dubbo 3.1&#xff0c;详细介绍了Dubbo服务的发布与引用的源码。 此前我们学习了Dubbo的服务导出的源码&#xff0c;在DubboBootstrapApplicationListener#startSync方法中&#xff0c;在调用了exportServices方法进行服务导出之后&#xff0c;立即调用了referServices方法…

【ZZULIOJ】1025: 最大字符(Java)

目录 题目描述 输入 输出 样例输入 Copy 样例输出 Copy code 题目描述 给你三个ASCII字符(不含空白字符:包括空格、制表符\t、回车换行符\n)&#xff0c;找出其中最大的那个 输入 输入包含三个字符&#xff0c;之间有一个空格隔开。 输出 输出ASCII码最大的那个字符…

神经网络汇聚层

文章目录 最大汇聚层平均汇聚层自适应平均池化层 最大汇聚层 汇聚窗口从输入张量的左上角开始&#xff0c;从左往右、从上往下的在输入张量内滑动。在汇聚窗口到达的每个位置&#xff0c;它计算该窗口中输入子张量的最大值或平均值。计算最大值或平均值是取决于使用了最大汇聚…

RISC-V/ARM mcu OpenOCD 调试架构解析

Risc-v/ARM mcu OpenOCD 调试架构解析 最近有使用到risc-v的单片机&#xff0c;所以了解了下risc-v单片机的编译与调试环境的搭建&#xff0c;面试时问到risc-v的调试可参看以下内容。 risc-v根据官方的推荐&#xff0c;调试器服务是选择OpenOCD&#xff0c;DopenOCD(开放片上…

【ROS笔记3】节点 和 命名空间 (通俗理解运用)

1. 前言 在ROS中,节点、话题、服务、参数等都可以有自己的命名空间(namespace)。命名空间是ROS用来组织和隔离不同资源的一种方式,确保了系统中的名字是唯一的,并允许同样的结构在不同的上下文中被重用。这就像在真实世界中的邮政系统,同一个城市里可以有多条同名的“梅花…

Python反爬案例——验证码的识别

验证码的识别 使用打码平台识别验证码 利用打码平台可以轻松识别各种各样的验证码&#xff0c;图形验证码、滑动验证码、点选验证码和逻辑推理验证码。打码平台提供了一系列API&#xff0c;只需要向API上传验证码图片&#xff0c;它便会返回对应的识别结果。 使用超级鹰平台…

深入理解指针1:指针变量、指针运算、野指针、const修饰指针

生活中我们把门牌号也叫地址&#xff0c;在计算机中我们把内存单元的编号也称为地址。C语⾔中给地址起 了新的名字叫&#xff1a;指针。 所以我们可以理解为&#xff1a;内存单元的编号地址指针 1、指针变量 我们知道的是&#xff1a;数组名是数组首元素的地址。也就是说&…

构建高可用性数据库架构:深入探索Oracle Active Data Guard(ADG)

随着企业数据规模的不断增长和业务的复杂化&#xff0c;数据库的高可用性和可靠性变得尤为重要。Oracle Active Data Guard&#xff08;ADG&#xff09;作为Oracle数据库提供的一种高可用性解决方案&#xff0c;在实时备份和灾难恢复方面发挥着重要作用。本文将深入探讨ADG的原…

中断服务程序模板

通常定时器初始化过程如下: ①对 TMOD赋值,以确定TO和T1的工作方式。 ②计算初值,并将初值写入THO、TLO或TH1、TL1。 ③中断方式时&#xff0c;则对IE赋值&#xff0c;开放中断。 ④使TRO或TR1置位&#xff0c;启动定时器/计数器定时或计数。 代码 利用定时器0工作方式1&…

轻松设置Facebook自动隐藏评论和删除评论功能

Facebook作为海外营销的最大流量平台之一&#xff0c;是很多跨境卖家争夺的市场&#xff0c;希望可以通过Facebook这个全球性的平台来推广自己的产品或服务。身处这个竞争激烈的市场&#xff0c;任何一条负面评论或不当言论出现在你的品牌页面上都可能影响到品牌形象&#xff0…

臻奶惠无人售货机:新零售时代的便捷消费革命

臻奶惠无人售货机&#xff1a;新零售时代的便捷消费革命 在新零售的浪潮中&#xff0c;智能无人售货机作为一个创新的消费模式&#xff0c;已经成为距离消费者最近的便捷购物点之一。这种模式不仅能够满足居民对消费升级的需求&#xff0c;还能通过建立多样化和多层次的消费体…

k8s练习-创建一个Deployment

创建Deployment 创建一个nginx deployment [rootk8s-master home]# cat nginx-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata:name: nginx-deployment spec:selector:matchLabels:app: nginx # 配置pod的labelsreplicas: 2 # 声明2个副本template:metada…

spring boot自动配置原理-怎样回答这个问题

首先我们说一下自动配置的概念。 自动配置&#xff1a;遵循约定大约配置的原则&#xff0c;在boot程序启动后&#xff0c;起步依赖中的一些bean对象会自动注入到ioc容器 例子 程序引入spring-boot-starter-web 起步依赖&#xff0c;启动后&#xff0c;会自动往ioc容器中注入…

记一次 pdfplumber 内存泄漏导致的服务器宕机

有一个项目需求&#xff0c;要在每天凌晨5点的时候执行一个任务&#xff0c;获取一系列的PDF文件并解析。 后端是Django框架&#xff0c;定时任务用Celery来实现的。 本地跑没什么问题&#xff0c;但是一放到服务器上跑就会宕机&#xff0c;而且是毫无征兆的宕机&#xff0c;…

黑马HTMLCSS基础

黑马的笔记和资料都是提供好了的&#xff0c;这个文档非常适合回顾复习。我在黑马提供的笔记上做了一些微不足道的补充&#xff0c;以便自己复习查阅。该笔记比较重要的部分是 表单&#xff0c;http请求 第一章. HTML 与 CSS HTML 是什么&#xff1a;即 HyperText Markup lan…

WPF —— 动画

wpf动画类型 1<类型>Animation这些动画称为from/to/by动画或者叫基本动画&#xff0c;他们会在起始值或者结束值进行动画处理&#xff0c;常用的例如 <DoubleAnimation> 2 <类型>AnimationUsingKeyFrames: 关键帧动画&#xff0c;功能要比from/to这些动画功…

正则表达式 (regex) 简介和基本用法

正则表达式 (regex) 是用于模式匹配和文本操作的强大工具。 它们广泛应用于编程、文本处理、数据验证等领域。 以下是正则表达式的一些常见用例&#xff1a; 模式匹配&#xff1a;正则表达式可用于搜索文本中的特定模式。 例如&#xff0c;在文档中查找电子邮件地址、URL、电话…