前言
电压表是测量电压的一种仪器。由永磁体、线圈等构成。电压表是个相当大的电阻器,理想的认为是断路。初中阶段实验室常用的电压表量程为0~3V和0~15V。
传统的指针式电压表包括一个灵敏电流计,在灵敏电流计里面有一个永磁体,在电流计的两个接线柱之间串联一个由导线构成的线圈,线圈放置在永磁体的磁场中,并通过传动装置与表的指针相连。大部分电压表都分为两个量程。电压表有三个接线柱,一个负接线柱,两个正接线柱,电压表的正极与电路的正极连接,负极与电路的负极连接。
正文
一、简易电压表设计验证
1.项目需求
基于fpga设计一款电压表,显示数据误差不高于0.001V。
2.技术介绍
模数转换器即AD转换器,或简称ADC(Analog to DigitalConver),通常是指一个将模拟信号转变为数字信号的电子元件或电路。
将经过与标准量比较处理后的模拟量转换为以二进制数值表示的离散信号。
模拟信号向数字信号的转换过程一般分为四个步骤:采样、保持、量化、编码。通常量化,编码在ADC芯片中进行,常用ADC芯片有积分型、逐次逼近型、闪烁型、流水线型
本实验的数据采集需要借助于AD转换模块,该模块使用芯片AD9280,是一款并行8位宽,采样率32M,电压范围+5V,到-5V。
da_in的数据范围为0000_0000到1111_1111,即0到255,表示-5到+5v,在0到127表示-5到0V,当前电压为= -精度*(127-测量值)。当前精度为10/(2^8);128到255表示0到+5V,当前电压为= 精度*(测量值-127)。当前精度为10/(2^8)。
这里测量方法选用中值法:系统上电后采集接口悬空,此时ad输入数据应为0V,对此时采集的数据取均值M,此时M对应0V,测量数据0到M即对应-5到0V,当前电压为= -精度*(M-测量值),当前精度为10/((M+1)*2);M到255表示0到+5V,当前电压为= 精度*(测量值-M)。当前精度为10/((255-M)*2)。
3.顶层架构
4.端口描述
clk | 时钟信号(50Mhz) |
rst_n | 复位信号(低电平有效) |
Clk_ad | 采样时钟 |
da | AD模块接转换的数据 |
[2:0] sel | 位选信号 |
[7:0] seg | 段选信号 |
二、代码验证
数据计算模块
module div_volt(
input clk ,
input rst_n ,
input[7:0] da_in ,
output da_clk ,
output [23:0] data ,
output sign //符号位
);
reg cnt ;//分频计数器
reg da_clk_o ;//四分频时钟
reg [12:0] cnt_ad ;//数据计数器,基于采样时钟
reg [17:0] data_sum ;//数据累加器
reg sum_en ;//累加范围标准信号,1024个数据后拉高
reg [7:0] data_pj ;//数据M求平均
wire [27:0] data_p ;//数据精度(-5——0V)
wire [27:0] data_l ;//数据精度(-5——0V)
reg [27:0] data_out ;//输出数据暂存
always@(posedge clk,negedge rst_n)//四分频计数器
begin
if(rst_n == 0)
cnt <= 1'b0;
else
cnt = cnt + 1'b1;
end
always@(posedge clk,negedge rst_n)//实现四分频
begin
if(rst_n == 0)
da_clk_o <= 1'b0;
else
if(cnt == 1'b1)
da_clk_o <= ~da_clk_o;
else
da_clk_o <= da_clk_o;
end
assign da_clk = ~da_clk_o;//输出实现四分频,采样时钟
always@(posedge da_clk,negedge rst_n)//数据计数器
begin
if(rst_n == 0)
cnt_ad <= 13'd0;
else
if(sum_en == 1'b0)
cnt_ad <= cnt_ad + 13'd1;
else
cnt_ad <= cnt_ad;
end
always@(posedge da_clk,negedge rst_n)//数据累加
begin
if(rst_n == 0)
data_sum <= 18'd0;
else
if(cnt_ad == 13'd1024)
data_sum <= 18'd0;
else
data_sum <= data_sum + da_in;
end
always@(posedge da_clk,negedge rst_n)//累加范围标志信号
begin
if(rst_n == 0)
sum_en <= 1'b0;
else
if(cnt_ad == 13'd1024)//0-1024为悬空状态下确定M的过程
sum_en <= 1'b1;
else
sum_en <= sum_en ;
end
always@(posedge da_clk,negedge rst_n)//求M
begin
if(rst_n == 0)
data_pj <= 8'd0;
else
if(cnt_ad == 13'd1024)
data_pj <= data_sum / 1024;
else
data_pj <= data_pj;
end
assign data_p = (sum_en == 1'b1)? 81920000/((data_pj +1)*2 ):0;//((10*2^13*1000)/(M+1)*2)
assign data_l = (sum_en == 1'b1)? 81920000/((255-data_pj +1)*2 ):0;//精度扩大2^13倍
always@(posedge da_clk,negedge rst_n)//求M
begin
if(rst_n == 0)
data_out <= 24'd0;
else
if(sum_en == 1'b1)
if(da_in < data_pj)
data_out <= (data_p * (data_pj - da_in)) >>13;
else
if(da_in > data_pj)
data_out <= (data_l * (da_in - data_pj)) >>13;
end
assign sign = (da_in > data_pj)?1'b0:1'b1;
assign data = data_out;
endmodule
数码管驱动模块
module seg_driver(
input clk ,
input rst_n ,
input [23:0] data_in ,//接收数字时钟信号
output reg [2:0] sel ,//位选信号
output reg [7:0] seg //段选信号
);
reg [18:0] cnt;//1MS
parameter MAX = 19'd50_000;
//20ns记50000次
//产生1ms的延时
//位选信号1ms变换一次
reg [3:0] temp;
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
cnt <= 19'd0;
else
if(cnt < MAX - 1)//产生1ms的延时
cnt <= cnt + 19'd1;
else
cnt <= 19'd0;
end
//数码管位选信号控制逻辑
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
sel <= 3'd0;
else
if(cnt == MAX - 1)//位选信号6位,1ms的延时到变化一次
if(sel < 5)
sel <= sel + 3'd1;
else
sel <= 3'd0;
else
sel <= sel;
end
always @(*) begin
if(rst_n == 0)
temp <= 4'd0;
else
case(sel)//位选信号到将数据输出到对应输出位上
3'd0 : temp <= data_in[23:20];
3'd1 : temp <= data_in[19:16];
3'd2 : temp <= data_in[15:12];
3'd3 : temp <= data_in[11:8];
3'd4 : temp <= data_in[7:4];
3'd5 : temp <= data_in[3:0];
default : temp <= 4'd0;
endcase
end
always @(*) begin
if(rst_n == 0)
seg <= 8'hff;
else
case(temp)//段选信号,对应的数码管信号
4'd0 : seg <= 8'b1100_0000;//0
4'd1 : seg <= 8'b1111_1001;//1
4'd2 : seg <= 8'b1010_0100;//2
4'd3 : seg <= 8'b1011_0000;//3
4'd4 : seg <= 8'b1001_1001;//4
4'd5 : seg <= 8'b1001_0010;//5
4'd6 : seg <= 8'b1000_0010;//6
4'd7 : seg <= 8'b1111_1000;//7
4'd8 : seg <= 8'b1000_0000;//8
4'd9 : seg <= 8'b1001_0000;//9
4'd10 : seg <= 8'b1000_1000;//A
4'd11 : seg <= 8'b1000_0011;//b
4'd12 : seg <= 8'b1100_0110;//C
4'd13 : seg <= 8'b1010_0001;//d
4'd14 : seg <= 8'b1000_0110;//E
4'd15 : seg <= 8'b1000_1110;//F
default : seg <= 8'hff;
endcase
end
endmodule
顶层连线
module div_volt_top(
input clk ,
input rst_n ,
input[7:0] da_in ,
output da_clk ,
output [2:0] sel ,//位选信号
output [7:0] seg //段选信号
);
wire [23:0]data;
div_volt div_volt_inst(
.clk (clk ),
.rst_n (rst_n),
.da_in (da_in),
.da_clk (da_clk),
.data (data ),
.sign ( )
);
seg_driver seg_driver_inst(
.clk (clk ),
.rst_n (rst_n ),
.data_in (data ),//接收数字时钟信号
.sel (sel ),//位选信号
.seg (seg ) //段选信号
);
endmodule
仿真代码
`timescale 1ns/1ps
module div_volt_top_tb;
reg clk ;
reg rst_n ;
reg[7:0] da_in ;
reg da_en ;
reg[7:0] da_reg;
wire [7:0] seg ;
wire [2:0] sel ;
wire da_clk;
div_volt_top div_volt_top_inst(
.clk (clk ),
.rst_n (rst_n ),
.da_in (da_in ),
.da_clk (da_clk ),
.sel (sel ),//位选信号
.seg (seg ) //段选信号
);
initial clk =1;
always #10 clk = ~clk;
initial begin
rst_n = 0;
#20
rst_n = 1;
#100
da_en = 0;
#499990
da_en = 1;
#20000
$stop;
end
always@(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
da_reg <= 8'b0;
else
if(da_en == 1)
da_reg <= da_reg + 1;
else
da_reg <= 8'b0;
end
always@(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
da_in <= 8'b0;
else
if(da_en == 0)
da_in <= 8'd125;
else
if(da_en == 1)
da_in <= da_reg;
else
da_in <= da_in;
end
endmodule
三、仿真验证
运行仿真,这里可以看到在500818665ps后开始有数据输出。
调出中间数据,放大波形图进行观察
数据输出4920,在精度计数块,对数据进行了1000倍的放大,所以这里输出数据为4.92V,sign为高电平,表示该数据为负数,即-4.92V。此时输入数据为1,0V时对应输入124,输入范围为0-255,通过对比计算得到输出电压。
数据输出1249,在精度计数块,对数据进行了1000倍的放大,所以这里输出数据为1.249V,sign为低电平,表示该数据为正数,即1.249V。数据输出正确,实验成功
参考资料
等精度测量