11 FPGA_简易电压表设计与验证(附代码)

news2024/11/18 21:52:17

1. 模数转换理论

      模数转换器又称(A/D转换器),通常是指一个将模拟信号转变为数字信号的电子元件或电路。常见的转换方式使将模拟量与基准量比对得到便于传输的二进制信号。生活中常见的模拟量有温湿度、图像、声音等。模拟信号与数字信号的转换过程一般分为四个步骤:采样、保持、量化、编码。前两个步骤在采样-保持电路中完成,后两步则在 ADC 芯片中完成。ADC 的主要技术指标包括:分辨率、转换速率、量化误差、满刻度误差、线性度。

     以上内容了解即可,重点学习如何将8位二进制数转换为常见的数字电压表。

2. 实验

实验目标:外部挂载的高速 AD/DA 板卡的 A/D 部分将输入其中的模拟信号(电压范围 -5V~5V)转换为数字量,将数字量传入 FPGA, FPGA 将传入的数字量通过计数转化为电压数值。通过数码管显示转化后的电压值,实现模拟信号的电压测量。

2.1 硬件资源介绍

板载AD/DA板卡:AD部分

对应电路图: 

      外载 AD/DA 板卡的 AD 部分使用高速 AD 芯片 AD9280, AD9280 由 ANALOG 公司生产, 是一款单芯片、 8 位、 32 MSPS 模数转换器(ADC),采用单电源供电,内置一个片内采样保持放大器和基准电压源。
      AD9280 采用+2.7 V 至+5.5 V 电源供电,非常适合高速应用中的低功耗操作,额定温度范围为-40°C 至+85°C 工业温度范围。 AD9280 的输入经过设计,用户可以选择各种输入范围和偏移,并可通过单端或差分方式驱动输入。
      AD9280 具有一个片上可编程基准电压源。也可以选用外部基准电压,以满足应用的直流精度与温度漂移要求。采用一个单时钟输入来控制所有内部转换周期。数字输出数据格式为标准二进制。

2.2 程序设计

2.2.1 整体模块设计

       将待测量的模拟信号接入外部挂载的高速 AD/DA 板卡的模拟信号输入端,板卡会将输入的模拟信号进行采样、量化、编码后,将模拟电压转换为数字值传给 FPGA;内部 adc模块接收到传入的电压数字值,经过运算,转换为可用于显示的电压值输入给数码管显示模块,将电压值显示在数码管上。

2.2.2 ADC 模块

1.  模块框图

       输入的ad_data信号模拟信号电压数字值,输出volt 信号则是经过运算处理后的电压值,传入数码管显示模块用于电压显示,ad_clk 信号为传入外载板卡的时钟信号,频率为 12.5MH 中,由系统时钟信号 4 分频得到,供ADC 芯片在此时钟同步下进行模拟信号 的采样、量化和编码。
       由上可知,难点在于如何将ad_data转换为我们便于识别的电压值。
本质算法:
       已知ADC 芯片位宽为 8 位,板卡模拟电压输入范围为-5v~+5v,即电压表测量范围, 最大值和最小值压降为 10v,分辨率为 10/2^8

       当 ADC 芯片采集后的电压数值 ad_data 位于 0 - 127 范围内,表示测量电压位于-5V ~0V 范围内,换算为电压值: Vin = - (10 / 2^8 * (127 - ad_data  ))
       当 ADC 芯片采集后的电压数值 ad_data 位于 127 - 255 范围内,表示测量电压位于 0V ~ 5V 范围内,换算为电压值: Vin= (10 / 2^8 * (ad_data - 127))
  改良算法:  
     
    为了提高测量结果的精确性和实现均值滤波,使用定义中值的测量方法。采样累计采集1024次数据,计算一次平均值方法,这样采样结果显示准确、稳定,常用电压表中往往也采用这种方式。

       在电压表上电后未接入测量电压时,取 ADC 芯片采集的最初的若干测量值(8位二进制数)(我觉得时电路噪声干扰产生的),对其取平均,作为测量中值M,与实际测量值 0V 对应。
       当 ADC 芯片采集后的电压数值 ad_data 位于 0 - M范围内,表示测量电压位于-5V ~ 0V 范围内,分辨率为 10/((M + 1) *2),换算为电压值:Vin = - ((10 /((M + 1) * 2)) * (M - ad_data))
       当 ADC芯片采集后的电压数值 ad_data 位于M - 255 范围内,表示测量电压位于 0V ~ 5V范围内,分辨率为 10/((255 -(M + 1) * 2),换算为电压值: Vin = ((10 /((255 -(M + 1) * 2)) * (ad_data - M))。下文的中值用data_median替代。

2. 波形图分析

       首先分析data_median的由来。cnt_median对AD板卡模块的时钟进行1024次的计数,在每一次计数中完成对ad_data数据的累加,当计数1025次时拉高median_en信号,对数次的累加求中值。

       中值 data_median 确定后,开始测量电压的计算。当 ADC 芯片采集后的电压数值ad_data 位于 0 - data_median 范围内,表示测量电压位于-5V ~ 0V 范围内,分辨率为10/((data_median + 1) * 2),换算为电压值: Vin = - ((10 /((data_median + 1) * 2)) *(data_median - ad_data));当 ADC 芯片采集后的电压数值 ad_data 位于 data_median - 255 范围内,表示测量电压位于 0V ~ 5V 范围内,分辨率为 10/((255 - data_median + 1) * 2),换算为电压值: Vin = ((10 /((255 - data_median + 1) * 2)) * (ad_data - data_median))。之所以放大倍数为(2^13 * 1000)倍方便电压值的计算(整数计算比小数简单)。

       使用 “>> 13”对计算值进行右移 13 位,由于抵消分辨率放大的 2^13 倍,分辨率中放大的 1000 倍,可以通过将数码管显示值小数点左移 3 位来抵消;正负号通过 ad_data 与中值 data_median 的比较来确定, sign = (ad_data < data_median) ? 1'b1 : 1'b0, sign 为高电平,代表测量结果为负向电压,反之为正向电压。

3. 编写RTL代码
`timescale  1ns/1ns

module  adc
(
    input   wire            sys_clk         ,   //时钟
    input   wire            sys_rst_n       ,   //复位信号,低电平有效
    input   wire    [7:0]   ad_data         ,   //AD输入数据

    output  wire            ad_clk          ,   //AD驱动时钟,最大支持20Mhz时钟
    output  wire            sign            ,   //正负符号位
    output  wire    [15:0]  volt                //数据转换后的电压值
);

//parameter define
parameter   CNT_DATA_MAX = 11'd1024;    //数据累加次数

//wire  define
wire    [27:0]  data_p      ;   //根据中值计算出的正向电压AD分辨率
wire    [27:0]  data_n      ;   //根据中值计算出的负向电压AD分辨率

//reg define
reg             median_en   ;   //中值使能
reg     [10:0]  cnt_median  ;   //中值数据累加计数器
reg     [18:0]  data_sum_m  ;   //1024次中值数据累加总和
reg     [7:0]   data_median ;   //中值数据
reg     [1:0]   cnt_sys_clk ;   //时钟分频计数器
reg             clk_sample  ;   //采样数据时钟
reg     [27:0]  volt_reg    ;   //电压值寄存

//数据ad_data是在ad_sys_clk的上升沿更新
//所以在ad_sys_clk的下降沿采集数据是数据稳定的时刻
//FPGA内部一般使用上升沿锁存数据,所以时钟取反
//这样ad_sys_clk的下降沿相当于sample_sys_clk的上升沿

assign  ad_clk = ~clk_sample;

//sign:正负符号位
assign  sign = (ad_data < data_median) ? 1'b1 : 1'b0;   

//时钟分频(4分频,时钟频率为12.5Mhz),产生采样AD数据时钟
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        begin
            cnt_sys_clk <=  2'd0;
            clk_sample  <=  1'b0;
        end
        else
        begin
            cnt_sys_clk <=  cnt_sys_clk + 2'd1;
        if(cnt_sys_clk == 2'd1)
            begin
            cnt_sys_clk <=  2'd0;
            clk_sample  <=  ~clk_sample;
            end
        end

//中值使能信号
always@(posedge clk_sample or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        median_en   <=  1'b0;
    else    if(cnt_median == CNT_DATA_MAX)
        median_en   <=  1'b1;
    else
        median_en   <=  median_en;

//cnt_median:中值数据累加计数器
always@(posedge clk_sample or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_median    <=  11'd0;
    else    if(median_en == 1'b0)
        cnt_median    <=  cnt_median + 1'b1;

//data_sum_m:1024次中值数据累加总和
always@(posedge clk_sample or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        data_sum_m  <=  19'd0;
    else    if(cnt_median == CNT_DATA_MAX)
        data_sum_m    <=  19'd0;
    else
        data_sum_m    <=  data_sum_m + ad_data;

//data_median:中值数据
always@(posedge clk_sample or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        data_median    <=  8'd0;
    else    if(cnt_median == CNT_DATA_MAX)
        data_median    <=  data_sum_m / CNT_DATA_MAX;
    else
        data_median    <=  data_median;

//data_p:根据中值计算出的正向电压AD分辨率(放大2^13*1000倍)
//data_n:根据中值计算出的负向电压AD分辨率(放大2^13*1000倍)
assign  data_p = (median_en == 1'b1) ? 8192_0000 / ((255 - data_median) * 2) : 0;
assign  data_n = (median_en == 1'b1) ? 8192_0000 / ((data_median + 1) * 2) : 0;

//volt_reg:处理后的稳定数据
always@(posedge clk_sample or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        volt_reg    <= 'd0;
    else    if(median_en == 1'b1)
        if((ad_data > (data_median - 3))&&(ad_data < (data_median + 3)))
            volt_reg    <= 'd0;
        else    if(ad_data < data_median)    //负值
            volt_reg <= (data_n *(data_median - ad_data)) >> 13;  
        else    if(ad_data > data_median)    //正值
            volt_reg <= (data_p *(ad_data - data_median)) >> 13;
    else
        volt_reg    <= 'd0;

//volt:数据转换后的电压值
assign  volt    =   volt_reg;

endmodule

2.2.3 顶层模块

1. 编写RTL代码
`timescale  1ns/1ns

module  dig_volt
(
    input   wire            sys_clk     ,   //系统时钟,50MHz
    input   wire            sys_rst_n   ,   //复位信号,低有效
    input   wire    [7:0]   ad_data     ,   //AD输入数据

    output  wire            ad_clk      ,   //AD驱动时钟,最大支持20Mhz时钟
    output  wire    [5:0]   sel         ,   //串行数据输入
    output  wire    [7:0]   seg             //使能信号
);
//wire  define
wire    [15:0]  volt    ;   //数据转换后的电压值
wire            sign    ;   //正负符号位

//------------- adc_inst -------------
adc     adc_inst
(
    .sys_clk    (sys_clk    ),  //时钟
    .sys_rst_n  (sys_rst_n  ),  //复位信号,低电平有效
    .ad_data    (ad_data    ),  //AD输入数据

    .ad_clk     (ad_clk     ),  //AD驱动时钟,最大支持20Mhz时钟
    .sign       (sign       ),  //正负符号位
    .volt       (volt       )   //数据转换后的电压值
);

//------------- seg_dynamic_inst --------------
seg_dynamic     seg_dynamic_inst
(
    .sys_clk    (sys_clk    ),  //系统时钟,频率50MHz
    .sys_rst_n  (sys_rst_n  ),  //复位信号,低有效
    .data       ({4'b0,volt}),  //数码管要显示的值
    .point      (6'b001000  ),  //小数点显示,高电平有效
    .seg_en     (1'b1       ),  //数码管使能信号,高电平有效
    .sign       (sign       ),  //符号位,高电平显示负号

    .sel        (sel        ),  //串行数据输入
    .seg        (seg        )   //输出使能信号
);

endmodule

2.2.4 仿真验证

1. 仿真代码
`timescale  1ns/1ns

module  tb_dig_volt();
//wire  define
wire                 ad_clk  ;
wire      [5:0]      sel    ;
wire      [7:0]      seg    ;


//reg   define
reg             sys_clk     ;
reg             sys_rst_n   ;
reg     [7:0]   ad_data     ;

reg             clk_sample  ;
reg             data_en     ;
reg     [7:0]   ad_data_reg ;

//sys_rst_n,sys_clk,ad_data
initial
    begin
        sys_clk     =   1'b1;
        clk_sample  =   1'b1;
        sys_rst_n   =   1'b0;
        #200;
        sys_rst_n   =   1'b1;
        data_en     =   1'b0;
        #499990;
        data_en     =   1'b1; 
    end

always #10 sys_clk = ~sys_clk;
always #40 clk_sample = ~clk_sample;

always@(posedge clk_sample or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        ad_data_reg <=  8'd0;
    else    if(data_en == 1'b1)
        ad_data_reg <=  ad_data_reg + 1'b1;
    else
        ad_data_reg <=  8'd0;

always@(posedge clk_sample or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        ad_data <=  8'd0;
    else    if(data_en == 1'b0)
        ad_data <=  8'd125;
    else    if(data_en == 1'b1)
        ad_data <=  ad_data_reg;
    else
        ad_data <=  ad_data;

//------------- dig_volt_inst -------------
dig_volt    dig_volt_inst
(
    .sys_clk     (sys_clk   ),
    .sys_rst_n   (sys_rst_n ),
    .ad_data     (ad_data   ),

    .ad_clk      (ad_clk    ),
    .sel        (sel      ),
    .seg        (seg      )
);

endmodule
2. 仿真结果

子模块的仿真我就不展示了,结果是正确的。

2.3 上板验证

缺少AD板卡,这里展示网上的结果,供大家参考。

总结:重点掌握如何计算电压的公式。

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

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

相关文章

Spring学习笔记4 Bean的作用域

Spring学习笔记3 Spring对IOC的实现_biubiubiu0706的博客-CSDN博客 新建模块 spring-004 引入依赖 <dependencies><!--Spring依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId>&…

企业申报“专精特新”,对知识产权有哪些要求?

深科信从深圳市中小企业服务局发布的公开数据中了解到&#xff0c;2022年深圳市专精特新中小企业申报8000余家&#xff0c;通过4800余家。2023年深圳市专精特新企业认定火热申报中&#xff0c;10月15日截止。 成为“专精特新”企业有什么好处? 1.国家级资质强力背书&#xff…

WordPress主题DUX v8.2源码下载

新增产品分类左侧多级分类折叠显示 新增网站默认字体对 MiSans 和 HarmonyOS Sans 的支持 新增顶部左上角显示登录注册的模块开关&#xff0c;且支持原生登录方式 新增手机端导航菜单的关闭按钮 新增文章内容中标题二的强化展示 新增全站禁止复制、右键和选择的操作 新增文章内…

fastjson反序列化漏洞(CVE-2017-18349)

文章目录 fastjson序列化FastJson 序列化操作反序列化漏洞原理漏洞复现&#xff08;CVE-2017-18349&#xff09; fastjson fastjson 是阿里巴巴开发的 java语言编写的高性能 JSON 库,用于将数据在 Json 和 Java Object之间相互转换。它没有用java的序列化机制,而是自定义了一套…

分类预测 | MATLAB实现WOA-CNN-BiGRU-Attention数据分类预测(SE注意力机制)

分类预测 | MATLAB实现WOA-CNN-BiGRU-Attention数据分类预测&#xff08;SE注意力机制&#xff09; 目录 分类预测 | MATLAB实现WOA-CNN-BiGRU-Attention数据分类预测&#xff08;SE注意力机制&#xff09;分类效果基本描述模型描述程序设计参考资料 分类效果 基本描述 1.MATLA…

Quartus出租车计费器verilog计价器

名称&#xff1a;出租车计费器verilog计价器 软件&#xff1a;Quartus 语言&#xff1a;Verilog 要求&#xff1a; 出租车计费器&#xff0c;起步价10元&#xff0c;3公里内起步价&#xff0c;可以切换白天和夜晚计费&#xff0c;白天时超过3公里后2.4元每公里&#xff0c;停…

RabbitMQ实现秒杀场景示例

本文章通过MQ队列来实现秒杀场景 整体的设计如下图&#xff0c;整个流程中对于发送发MQ失败和发送到死信队列的数据未做后续处理 1、首先先创建MQ的配置文件 Configuration public class RabbitConfig {public static final String DEAD_LETTER_EXCHANGE "deadLetterE…

springcloud3 分布式事务-seata的四种模式总结以及异地容灾

一 seata四种模式比较 1.1 seata的4种模式比较 二 seata的高可用 2.1架构 1.建TC服务集群非常简单&#xff0c;启动多个TC服务&#xff0c;注册到nacos即可。 2.做异地多机房容灾&#xff0c;比如一个TC集群在上海&#xff0c;另一个TC集群在杭州&#xff0c; 3.微服务基…

趣解设计模式之《小王的糖果售卖机》

〇、小故事 小王最近一直在寻找商机&#xff0c;他发现商场儿童乐园或者中小学校周围&#xff0c;会有很多小朋友喜欢吃糖果&#xff0c;那么他想设计一款糖果售卖机&#xff0c;让后将这些糖果售卖机布置到商场和学校旁边&#xff0c;这样就能获得源源不断的收益了。 想到这里…

20 个实例玩转 Java 8 Stream

20 个实例玩转 Java 8 Stream 1、Stream概述 Java 8 是一个非常成功的版本&#xff0c;这个版本新增的 Stream&#xff0c;配合同版本出现的 Lambda&#xff0c;给我们操作集合 Collection 提供了极大的便利。 那么什么是 Stream&#xff1f; Stream 将要处理的元素集合看作…

Docker部署MySQL8数据库

1、准备工作 docker pull mysql:8.0.27 Pwd"/data/software/mysql" mkdir ${Pwd}/{data,logs} -p chmod 777 ${Pwd}/logs2、添加配置文件 cat > ${Pwd}/my.cnf << EOF [mysqld] usermysql pid-file/var/run/mysqld/mysqld.pid socket/var/run/mysqld/mysq…

SQL和Python,哪个更容易自学?哪个更适合数据工作的编程新手?

如果你想从事数据工作&#xff0c;比如数据分析、数据开发、数据科学等&#xff0c;你可能会遇到这样的问题&#xff1a;SQL和Python哪个更容易自学&#xff1f;哪个更有用&#xff1f;哪个更有前途&#xff1f;其实这两种语言都是数据工作的重要技能&#xff0c;但它们的特点和…

react实现数据进度条展示组件

1.需求 在项目开发时&#xff0c;要展示一个操作的进度条&#xff0c;比如&#xff1a;要实现一个文件的上传下载进度条&#xff0c;或者实现类似拼夕夕砍一刀进度条&#xff0c;怎么实现呢&#xff1f;效果图如下&#xff1a; 对上面效果图说明&#xff1a; 进度条底色为棕黑色…

559. N 叉树的最大深度

给定一个 N 叉树&#xff0c;找到其最大深度。 最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。 N 叉树输入按层序遍历序列化表示&#xff0c;每组子节点由空值分隔&#xff08;请参见示例&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [1,null,3,2…

从统计语言模型到预训练语言模型---预训练语言模型(BERT,GPT,BART系列)

基于 Transformer 架构以及 Attention 机制&#xff0c;一系列预训练语言模型被不断提出。 ​BERT 2018 年 10 月&#xff0c; Google AI 研究院的 Jacob Devlin 等人提出了 BERT (Bidirectional Encoder Representation from Transformers ) 。具体的研究论文发布在 arXiv …

浅谈低压绝缘监测及定位系统在海上石油平台的研究与应用

安科瑞 华楠 摘要&#xff1a;海上石油平台低压系统与陆地电力系统有很大区别&#xff0c;其属于中性点绝缘系统&#xff0c;在出现单相接地故障时&#xff0c;系统允许带故障正常运行2 h&#xff0c;保证海上重要电气设备不会立即关停。现以渤海某海上平台为例&#xff0c;其…

LABVIEW 实战案例1--温度报警系统

图1 温度报警系统前面板 图2 温度报警系统后面板

1796_通过vmware打开VirtualBox虚拟机文件

全部学习汇总&#xff1a; GitHub - GreyZhang/toolbox: 常用的工具使用查询&#xff0c;非教程&#xff0c;仅作为自我参考&#xff01; 首先讲vdi格式转换成vmdk格式&#xff0c;以我自己的环境下的信息&#xff0c;处理如下&#xff1a; VBoxManage clonehd "LinuxMin…

Sentinel结合Nacos实现配置持久化(全面)

1、前言 我们在进行分布式系统的开发中&#xff0c;无论是在开发环境还是发布环境&#xff0c;配置一定不能是内存形式的&#xff0c;因为系统可能会在中途宕机或者重启&#xff0c;所以如果放在内存中&#xff0c;那么配置在服务停到就是就会消失&#xff0c;那么此时就需要重…

javascript 使用async/await简化异步操作实践

实现的功能简述&#xff1a; 新增申请单时&#xff0c;申请单的业务日期由两个因素决定&#xff1a; 异步获取的仓库的业务日期。异步获取的日期偏移天数规则。 业务日期 仓库的业务日期 偏移天数。比如仓库的业务日期是2023-01-01,偏移天数是5&#xff0c;那么最终的业务日期…