用FPGA设计电压表

介绍:通过外部挂载的高速AD/DA板卡的A/D部分将输入其中的模拟信号转换为数字量,然后将数字量传入FPGA,FPGA通过计数转化为电压数值,然后通过数码管显示转化后的电压值,实现模拟信号的电压测量。

原理:

ADC(模拟到数字转换器)的转换过程分为以下四个步骤:

  1. 抗混叠滤波:这一步的目的是为了消除混叠效应。混叠效应是指,当不同的信号频率在相同的采样频率下,可能会得到相同的采样波形。抗混叠滤波器的作用就是确保在采样过程中,信号不会因为混叠而失真。
  2. 采样保持:将一个时间上连续变化的模拟量转化为时间上离散变化的模拟量。将采样结果储存起来,直到下次采样,这个过程叫作保持
  3. 量化:量化是将幅度的模拟量转化为数字量的过程。是将连续的模拟信号转换为离散的数字信号。由于量化输出的数字信号位数有限,所以输出的数字信号和你采样得到的模拟信号会有一个误差,被称为量化误差,对于一个N位ADC来说,假设其满量程电压为Vref,Vref被ADC分为2N个区间,区间宽度用LSB(last significant bit)表示LSB=Vref/2N
  4. 编码:编码是将量化的数值用二进制代码表示的过程。数字化编码电路将量化后的数值转换成一组N位的二进制数输出。

                例如:Vref=8V,ADC为3位,LSB=1,所以每个区间为1V,
                        000代表电压0≤V<1
                        001代表电压1≤V<2
                        010代表电压2≤V<3
                        011代表电压3≤V<4
                        100代表电压4≤V<5
                        101代表电压5≤V<6
                        110代表电压6≤V<7
                        111代表电压7≤V<8

运用:以AD9280芯片为例,芯片位宽为8位,模拟电压输入范围为-5v~+5v,即电压表测量范围,最大值和最小值压降为10v,分辨率为10/2^8。当ADC芯片采集后的电压数值ad_data位于0 - 127范围内,表示测量电压位于-5V ~ 0V范围内,换算为电压值:Vin = - (10/2^8 * (127 - ad_data));当ADC芯片采集后的电压数值ad_data位于128 - 255范围内,表示测量电压位于0V ~ 5V范围内,换算为电压值:Vin = (10/2^8 * (ad_data - 127))。

实验目的:设计一个用数码管显示显示AD模块测量电压

(本实验参考了野火的简易电压表设计与验证)

链接:4. 简易电压表的设计与验证 — [野火]FPGA Verilog开发实战指南——基于Altera EP4CE10 征途Pro开发板 文档 (embedfire.com)

模块框图:

adc模块代码

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

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

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) ? 4'd11 : 4'd12;

//时钟分频(4分频,时钟频率为12.5Mhz),产生采样AD数据时钟
always@(posedge clk_50Mhz or negedge rst_n)
    if(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 rst_n)
    if(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 rst_n)
    if(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 rst_n)
    if(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 rst_n)
    if(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 rst_n)
    if(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

bcd_8421模块代码,见我的这一篇文章 :用FPGA设计8421BCD编码-CSDN博客

smg模块代码,见我的这一篇文章 :用FPGA设计数码管-CSDN博客

adc_top模块代码

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

    output  wire            ad_clk         ,   //AD驱动时钟,最大支持20Mhz时钟
    output  reg [7:0] smg_reg              ,   //输出单个数码管显示的数据信号
    output  reg [7:0] smg_rel                  //输出数码管选择信号
);

wire [26:0] data     ;
wire [31:0] bcd_data ;
wire [3:0]  sign     ;

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

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

bcd_8421 bcd_8421_inst
(
    .clk_50mhz   (clk_50mhz)           ,      //系统时钟50Mhz     
    .rst_n       (rst_n    )           ,      //全局复位
    .data        (data     )           ,      //待编码数据

    .bcd_data    (bcd_data )                  //编码后的数据
);


smg smg_inst
(
    .clk_50Mhz  (clk_50Mhz  )     , //系统时钟50Mhz
    .rst_n      (rst_n      )     , //全局复位
    .data_in    ({12'd0,sign,bcd_data[15:0]}), //输入信号
    .point      (8'b00001000)     , //小数点显示,高电平有效
    
    .smg_reg    (smg_reg    )     , //输出单个数码管显示的数据信号
    .smg_rel    (smg_rel    )       //输出数码管选择信号
);

endmodule //adc_top

  • 21
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值