FPGA的学习:简易电压表的设计与验证
Posted 石小舟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FPGA的学习:简易电压表的设计与验证相关的知识,希望对你有一定的参考价值。
外部挂载的高速 AD/DA 板卡的 A/D 部分将输入其中的模拟信号转换为数字量,将数字量传入 FPGA, FPGA 将传入的数字量通过计数转化为电压数值,通过数码管显示转化后的电压值,实现模拟信号的电压测量。
整体框图如图所示。
其中adc模块。
`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
顶层模块的设计:
`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 stcp , //数据存储器时钟
output wire shcp , //移位寄存器时钟
output wire ds , //串行数据输入
output wire oe //使能信号
);
//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_595_dynamic_inst --------------
seg_595_dynamic seg_595_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 ), //符号位,高电平显示负号
.stcp (stcp ), //输出数据存储寄时钟
.shcp (shcp ), //移位寄存器的时钟输入
.ds (ds ), //串行数据输入
.oe (oe ) //输出使能信号
);
endmodule
其中要将seg_595的文件添加到工程。
然后全编译,得到RTL视图。
最后进行仿真。
`timescale 1ns/1ns
module tb_dig_volt();
//wire define
wire ad_clk ;
wire shcp ;
wire stcp ;
wire ds ;
wire oe ;
//reg define
reg sys_clk ;
reg clk_sample ;
reg sys_rst_n ;
reg data_en ;
reg [7:0] ad_data_reg ;
reg [7:0] ad_data ;
//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 ),
.shcp (shcp ),
.stcp (stcp ),
.ds (ds ),
.oe (oe )
);
endmodule
以上是关于FPGA的学习:简易电压表的设计与验证的主要内容,如果未能解决你的问题,请参考以下文章