模拟信号与数字信号的转换过程一般分为四个步骤:采样、保持、量化、编码。前两个步骤在采样-保持电路中完成,后两部在ADC芯片中完成。
常用的ADC可分为积分型、逐次逼近型、并行比较型等等
积分型ADC工作原理是将输入电压转换成时间和频率,然后由定时器计数器获得数字值。其优点是使用简单电路就能获得高分辨率。缺点是由于转换精度依赖于积分时间,因此转换速率极低。双积分是一种常用的AD转换技术,具有精度高,抗干扰能力强等优点。但高精度的双积分AD芯片,价格昂贵,设计成本较高。
逐次逼近型ADC由一个比较器和DA转换器通过逐次比较逻辑构成,从MSB开始,顺序地对每一位输入电压与内置DA转换器输出进行比较,经n次比较而输出数字值。其电路规模属于中等,优点是速度较高、功耗低,在低分辨率(小于12位)时价格便宜,但高精度(大于12位)价格昂贵。
ADC的主要技术指标包括:分辨率、转换速率、量化误差、满刻度误差、线性度。分辨率指输出数字量变化一个最低有效位(LSB)所需的输入模拟电压的变化量。转换速率是指完成一次从模拟转换到数字的AD转换所需要的时间的倒数。积分型AD的转换时间是毫秒级属于低速AD,逐次比较型AD是微秒级属中速AD,全并行/串 并行型AD可达到纳秒级。采样时间则是另一个概念,是指两次转换的时间间隔。为了保证转换的正确完成,采样速率必须小于或等于转换速率。
量化误差是由于AD的有限分辨率引起的误差,即有限分辨率AD的阶梯转移特性曲线与无限分辨率AD(理想AD)的转移特性曲线(直线)之间的最大偏差。通常是1个或半个最小数字量的模拟变化量,表示为1LSB、1/2 LSB。
满刻度误差是满刻度输出时对应的输入信号与理想输入信号值之差。
线性度指实际转换器的转移函数与理想直线的直达偏移
外部挂载的高速AD DA办卡的AD部分将输入其中的模拟信号转换成数字量,将数字量传入FPGA,FPGA将传入的数字量通过计数转化为电压数值,通过ila在线抓取转化后的电压值,实现模拟信号的电压测量。
将待测量的模拟信号接入外部挂载的高速AD / DA 板卡的模拟信号输入端,板卡会将输入的模拟信号进行采样、量化、编码后,将模拟电压转换成数字值传给FPGA;内部dig_volt 模块接收到传入的电压数字值,经过运算,转换为可用于显示的电压值输出。
由dig volt模块框图可知,模块有3路输入信号和1路输出信号。输入信号有时钟、复位和外载板卡的时钟信号,频率为12.5Mhz,由系统时钟信号4分频得到,ADC芯片在此时钟同步下进行模拟信号的采样、量化和编码。
本实验使用的ADC芯片位宽为8位,板卡模拟电压输入范围为-5到+5,即电压表测量范围,最大值和最小值降压为10v,分辨率为10/2^8
当ADC芯片采集后的电压数值ad_data 位于0-127范围内,表示测量电压位于-5v到0v范围内,换算为电压值:
当ADC芯片采集后的电压数据ad_data 位于128-255范围内,表示测量电压位于0-5v范围内,换算成电压值
在电压表上电后未接入测量电压时,取ADC芯片采集的最初的若干测量值,取平均,作为测量中值 data median,与实际测量值0v对应。
使用定义值得测量方法时,当ADC芯片采集后的电压数值 ad data 位于0-data median范围内,表示测量电压位于-5v到0v范围内,分辨率为10/((data median + 1)* 2),换算成电压值
当ADC芯片采集后的电压数值ad data 位于data median - 255 范围内,表示测量电压位于0-5v范围内,分辨率为
换算成电压值:
了解了具体实现方法,我们开始绘制框图
对于模块的输入信号不再说明,输出至外载板块的时钟信号 ad_clk ,频率为12.5Mhz,使用系统时钟4分频得来,所以声明了分频计数器cnt sys clk,初值为0,在系统时钟同步下,再0和1 之间循环计数;声明时钟信号clk sample,在 计数器cnt sys clk 计数值为1时,对自身取反,就得到了时钟频率为12.5Mhz的分频时钟信号 clk_sample,也作为本模块工作时钟信号;因为外载板卡与本模块均使用时钟上升沿对数据采样,为保证模块内工作时钟上升沿能够采集到板块传入的稳定数据,我们对clk sample 时钟信号取反作为输入板卡的时钟信号adc clk,adc clk 的上升沿刚好采集到数据的稳定状态
声明中值信号使能信号median en,方便计算中值,当median en 信号为低电平时,进行中值的计算;当median en 信号为高电平时,对ADC测量值进行累加求平均的计算。
对中值的计算我们也是用累加求平均的方法,在无测量电压输入电压表时,对前1024个数据进行累加求平均,所以声明计数器 cnt median 对累加值个数进行计数,计算范围0-1023,旨在median en 为低电平时进行计数,median en为高电平时,保持计数最大值;同时,计数最大值作为条件,拉高 median en 使能信号,1024个测量值总和保存在变量data_sum_m 中,当cnt median 计数到最大值,将平均值赋给变量 data median
///
// Company:
//
// File: dig_volt.v
// File history:
// : :
// : :
// : :
//
// Description:
//
//
//
// Targeted device:
// Author:
//
///
//`timescale /
module dig_volt
(
input wire clk,
input wire rstn,
input wire [7:0] ad_data, // AD 输入数据
output wire ad_clk // AD 驱动时钟,最大支持20Mhz时钟
);
// 数据累加次数
parameter CNT_DATA_MAX = 11'd1024;
// wire define
wire [27:0] data_p; // 根据中值计算出的正向电压AD分辨率
wire [27:0] data_n; // 根据中值计算出的负向电压AD分辨率
wire sign; // 正负符号位
wire [15:0] volt; // 数据转换后的电压值
// 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; // 电压值寄存
// main code
// 数据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 clk or negedge rstn) begin
if(rstn == 1'b0) begin
cnt_sys_clk <= 2'd0;
clk_sample <= 1'b1;
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
end
//中值信号使能
always @(posedge clk_sample or negedge rstn) begin
if(~rstn) begin
median_en <= 1'b0;
end
else if(cnt_median == CNT_DATA_MAX) begin
median_en <= 1'b1;
end
else begin
median_en <= median_en;
end
end
// cnt_median: 中值数据累加计数器
always @(posedge clk_sample or negedge rstn) begin
if(~rstn) begin
cnt_median <= 11'd0;
end
else if(median_en == 1'b0) begin
cnt_median <= cnt_median + 1'b1;
end
else
cnt_median <= cnt_median;
end
// data_sum_m : 1024次中值数据累加总和
always @(posedge clk_sample or negedge rstn) begin
if(~rstn) begin
data_sum_m <= 19'd0;
end
else if(cnt_median == CNT_DATA_MAX + 1) begin
data_sum_m <= 19'd0;
end
else begin
data_sum_m <= data_sum_m + ad_data;
end
end
// data_median: 中值数据
always @(posedge clk_sample or negedge rstn) begin
if(~rstn) begin
data_median <= 8'd0;
end
else if (cnt_median == CNT_DATA_MAX) begin
data_median <= data_sum_m / CNT_DATA_MAX;
end
else begin
data_median <= data_median;
end
end
// data_p: 根据中值计算出的正向电压AD分辨率
// data_n: 根据中值计算出的负向电压AD分辨率
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 rstn) begin
if(~rstn) begin
volt_reg <= 1'b0;
end
else if (median_en) begin
if((ad_data > (data_median - 3)) && (ad_data < (data_median + 3))) begin
volt_reg <= 'd0;
end
else if (ad_data < data_median) begin
volt_reg <= (data_n *(data_median - ad_data)) >> 13;
end
else if (ad_data > data_median) begin
volt_reg <= (data_p * (ad_data - data_median)) >> 13;
end
end
else begin
volt_reg <= 'd0;
end
end
assign volt = volt_reg;
endmodule
tb 文件
//
// Created by Microsemi SmartDesign Tue Mar 14 09:28:33 2023
// Testbench Template
// This is a basic testbench that instantiates your design with basic
// clock and reset pins connected. If your design has special
// clock/reset or testbench driver requirements then you should
// copy this file and modify it.
//
///
// Company:
//
// File: dig_volt_tb.v
// File history:
// : :
// : :
// : :
//
// Description:
//
//
//
// Targeted device:
// Author:
//
///
`timescale 1ns/100ps
module dig_volt_tb();
wire ad_clk;
reg sys_clk;
reg clk_sample;
reg sys_rst_n;
reg data_en;
reg [7:0] ad_data_reg;
reg [7:0] 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) begin
if(sys_rst_n == 1'b0)
ad_data_reg <= 8'd0;
else if (data_en == 1'b1) begin
ad_data_reg <= ad_data_reg + 1'b1;
end
else begin
ad_data_reg <= 8'd0;
end
end
always @(posedge clk_sample or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0)
ad_data <= 8'd0;
else if (data_en == 1'b0) begin
ad_data <= 8'd125;
end
else if (data_en == 1'b1) begin
ad_data <= ad_data_reg;
end
else begin
ad_data <= ad_data;
end
end
dig_volt dig_volt_inst(
.clk(sys_clk),
.rstn(sys_rst_n),
.ad_data(ad_data),
.ad_clk(ad_clk)
);
endmodule