【小梅哥FPGA进阶教程】第十三章 四通道数字电压表
十三、四通道数字电压表
本文由山东大学研友袁卓贡献,特此感谢
实验目的
设计一个四通道的数字电压表
实验平台
芯航线FPGA核心板、AD/DA模块
实验现象
实现一个四通道的数字电压表,其中可以用按键切换测量通道并在4位数码管上显示对应的测量值。
实验原理及步骤
数字电压表的工作原理即为,被测信号接入ADC模块的输入引脚,FPGA控制ADC的转换进程以及原始数据的采集,并将其采集到的二进制数据转换为数码管的显示数据。其中按键可以选择ADC模块不同的通道。其系统工作原理图如图1所示。
图1 系统工作原理图
由工作原理图可以暂时将本系统划分为ADC控制模块、码制转换模块、按键数据模块以及数码管驱动模块组成。
ADC控制模块之TLV1544
本系统采用的是TLV1544芯片,其为10位的ADC。因此其理论测量精度为,且当其输出为’dx时,实际电压为
V。本模块的设计在基础课程已经讲解,此处不再详述。其模块接口示意图如下所示。
图2 ADC模块接口示意图
数据预处理模块
ADC模块输出还是一个十位二进制数,因此需要先将数据转换成实际电压值。
上式中3.42为满量程电压,data为输出的二进制数,1024为ADC总的阶梯数。之所以是3.42,是本模块基准电源TL341输出电压。
这样得出的数据太小,因此先将其放大倍。这里也可以放大其他倍数。
经过上式的转换,还是一个小数,这里再放大1000倍以消除小数。即实际显示的数据为实际电压的1000倍。这样就完成了二进制数到实际电压的转换。
由于在上面TLV1544驱动设计中,数据更新速度为4000ns/次。这样已足够用做电压表显示,但是此处为了使数据稳定,加入均值滤波程序。本模块接口示意图如图3所示,其接口功能列表如表1所示。
图3 数据预处理模块接口示意图
表1 数据预处理模块接口功能描述
先将原始数据进行累加1024次。
reg [:]Hex_SUM;
reg [:]Hex; reg [:]cnt;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
cnt <= 'b0;
else if(ADC_flag)
cnt <= cnt + 'b1;
else
cnt <= cnt; always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
Hex_SUM <= 'd0;
else if(cnt == && ADC_flag)
Hex_SUM <= 'd0;
else if(ADC_flag)
Hex_SUM <= Hex_SUM + Hex_data;
else
Hex_SUM <= Hex_SUM;
将累加后的数据除以1024,也就是右移10位,这里数据总位数为20直接取其高10位即可。
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
Hex <= 'b0;
else if(cnt == && ADC_flag)
Hex <= Hex_SUM[:];
else
Hex <= Hex;
利用上面推导的公式即可输出最后的数据。
assign Voltage = ( * Hex) >> ;
码制转换模块
由于ADC输出的为10位二进制数而数码管需要的是BCD码的格式,因此需要将其进行码制的转换。
首先,先了解二进制与BCD码的位数对应关系。比如一个8位二进制码,可以表示的最大十进制数为255,转换成BCD码为 0010_0101_0101,共需12位,其中每4位组成一个BCD单元。n位二进制码转换成D个BCD码的n~D对应关系表见表2。
表2 n~D对应关系
此处采用加3移位法进行转换,附件中列举了另一种方式来进行转换。以8位二进制转换为3位BCD码为例,转换步骤是:将待转换的二进制码从最高位开始左移BCD的寄存器(从高位到低位排列),每移一次,检查每一位BCD码是否大于4,是则加上3,否则不变。左移8次后,即完成了转换。需要注意的是第八次移位后不需要检查是否大于5。
注意:为什么检查每一个BCD码是否大于4,因为如果大于 4(比如 5、6),下一步左移就要溢出了,所以加 3,等于左移后的加 6,起到十进制调节的作用。
表3给出了一个二进制码11101011转换成8421BCD码的时序。
表3 B/BCD时序
首先进行判断一个BCD码是否大于4,是则进行加3处理,否则输出原来数值。
module bcd_single_modify(bcd_in,bcd_out); input [:] bcd_in;
output [:] bcd_out; reg [:] bcd_out; always @ (bcd_in)
begin
if (bcd_in > )
bcd_out = bcd_in + 'd3;
else
bcd_out = bcd_in;
end endmodule
由以上原理可看出,这里需要定义一个10+12位的寄存器。同时从表3克拿出有几位二进制数就需移位几次。这里为了增加适用范围,将输入定位20位的二进制数,因此输出为7*4位BCD数。这样定义一个48位的移位寄存器。低20位为二进制数,高28位为BCD码。
每移位一次就需验证高28位BCD码是否大于4因此,编写以下代码。
module bcd_modify(data_in, data_out); input [:] data_in;
output [:] data_out; bcd_single_modify bcd6(.bcd_in(data_in[:]), .bcd_out(data_out[:]));
bcd_single_modify bcd5(.bcd_in(data_in[:]), .bcd_out(data_out[:]));
bcd_single_modify bcd4(.bcd_in(data_in[:]), .bcd_out(data_out[:]));
bcd_single_modify bcd3(.bcd_in(data_in[:]), .bcd_out(data_out[:]));
bcd_single_modify bcd2(.bcd_in(data_in[:]), .bcd_out(data_out[:]));
bcd_single_modify bcd1(.bcd_in(data_in[:]), .bcd_out(data_out[:]));
bcd_single_modify bcd0(.bcd_in(data_in[:]), .bcd_out(data_out[:]));
assign data_out[:] = data_in[:]; endmodule
现在编写顶层文件,其端口示意图及功能描述如下所示/
图4 码制转换模块接口示意图
表4 码制转换模块接口功能描述
reg [:] shift_reg;
wire [:] shift_reg_out;
wire [:]tmp;
reg [:]bcd_tmp; reg [:] cnt = 'b0; assign tmp = {'b0,Bin};
向左移位20次。
always@(posedge Clk or negedge Rst_n)
begin
if(!Rst_n)
begin
Done_Sig <= 'b0;
cnt <= 'd0;
shift_reg <= 'd0;
end
else
begin
case(cnt)
:begin
Done_Sig <= 'b0;
if(Do_Translate)
begin
cnt <= cnt + 'b1;
shift_reg <= tmp<<;
end
else
cnt <= 'b0;
end ,,,,,,,,,,,,,,,,,:
begin
shift_reg <= (shift_reg_out<<);
cnt <= cnt + 'b1;
end :
begin
bcd_tmp <= shift_reg_out<<;
Done_Sig <= ;
cnt <= 'b0;
end
default :cnt <= 'b0;
endcase
end
end
校验以及输出最终输出数据。
assign Bcd = bcd_tmp[:]; bcd_modify bcd_modify
(
.data_in(shift_reg),
.data_out(shift_reg_out)
);
按键输入模块
本部分在基础课程中也有介绍,此处只给出其端口示意图。
图5 按键输入模块接口示意图
通道选择模块
通过按键进行ADC四通道的选择,本模块接口示意图以及功能描述如下所示。
图6 通道选择模块接口示意图
表5 通道选择模块接口功能描述
内部除了例化还需产生通道选择信号,这里用的模块有四个通道但是为何按键一来就加’d2,是因为通道选择实际信号需为0000、0010、0100、1000,这样我们就需要加’d2。
wire key_state;
wire key_flag; always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
ADC_CHSEL <= ;
else if(key_flag && !key_state)begin
if(ADC_CHSEL == 'b0110)
ADC_CHSEL <= 'd0;
else
ADC_CHSEL <= ADC_CHSEL + 'd2;
end
else
ADC_CHSEL <= ADC_CHSEL;
数码管驱动模块
本部分在基础课程中也有介绍,此处只给出其端口示意图。
图7 数码管模块接口示意图
顶层设计
此处只需例化各个模块即可,顶层模块接口示意图如下所示。
图8 顶层模块接口示意图
综合后的RTL视图如图9所示。
图9 RTL Viewer视图
分配好引脚下载后可以看到改变输入电压,数码管上均有正常的显示,且切换通道时数据可以随之更新。
至此一个四通道数字电压表设计完毕。
附:基于查找表的数据电压换算
前面指出了一种数据处理及码制的方式,这里再列举利用查找表的实现方式。
这里因为是输入的10位二进制数,也就是说每一位变化对应的变化量为即为精度,这样就可以得出以下待转换数据与实际电压的对照表。这样当3.296v
这样就可以建立一个查找表,来分别计算其对应位的电压值的BCD码,然后相加。
reg [:] data1;
reg [:] data2;
reg [:] data3;
case(ADC_DATA[:])
'b0000:data1[15:0] = 16'b0000_0000_0000_0000;
'b0001:data1[15:0] = 16'b0000_0000_0000_0011; //.003
'b0010:data1[15:0] = 16'b0000_0000_0000_0110; //.006
'b0011:data1[15:0] = 16'b0000_0000_0001_0000; //.010
'b0100:data1[15:0] = 16'b0000_0000_0001_0011; //.013
'b0101:data1[15:0] = 16'b0000_0000_0001_0110; //.016
'b0110:data1[15:0] = 16'b0000_0000_0001_1001; //.019
'b0111:data1[15:0] = 16'b0000_0000_0010_0011; //.023
'b1000:data1[15:0] = 16'b0000_0000_0010_0110; //.026
'b1001:data1[15:0] = 16'b0000_0000_0010_1001; //.029
'b1010:data1[15:0] = 16'b0000_0000_0011_0010; //.032
'b1011:data1[15:0] = 16'b0000_0010_0011_0101; //.035
'b1100:data1[15:0] = 16'b0000_0010_0011_1001; //.039
'b1101:data1[15:0] = 16'b0000_0010_0100_0010; //.042
'b1110:data1[15:0] = 16'b0000_0010_0100_0101; //.045
'b1111:data1[15:0] = 16'b0000_0010_0100_1000; //.048
endcase
case(ADC_DATA[:])
'b0000:data2[15:0] = 16'b0000_0000_0000_0000;
'b0001:data2[15:0] = 16'b0000_0000_0101_0010; //.052
'b0010:data2[15:0] = 16'b0000_0001_0000_0011; //.103
'b0011:data2[15:0] = 16'b0000_0001_0101_0101; //.155
'b0100:data2[15:0] = 16'b0000_0010_0000_0110; //.206
'b0101:data2[15:0] = 16'b0000_0010_0110_1000; //.258
'b0110:data2[15:0] = 16'b0000_0011_0000_1001; //.309
'b0111:data2[15:0] = 16'b0000_0011_1000_0001; //.361
'b1000:data2[15:0] = 16'b0000_0100_0001_0011; //.413
'b1001:data2[15:0] = 16'b0000_0100_0110_0100; //.464
'b1010:data2[15:0] = 16'b0000_0101_0001_0110; //.516
'b1011:data2[15:0] = 16'b0000_0101_0011_0111; //.567
'b1100:data2[15:0] = 16'b0000_0110_0001_1000; //.618
'b1101:data2[15:0] = 16'b0000_0110_0111_0000; //.670
'b1110:data2[15:0] = 16'b0000_0111_0010_0001; //.722
'b1111:data2[15:0] = 16'b0000_0111_0111_0011; //.773
endcase
case(ADC_DATA[:])
'b00: data3[15:0] = 16'b0000_0000_0000_0000;
'b01: data3[15:0] = 16'b0000_1000_0010_0101; //.825
'b10: data3[15:0] = 16'b0001_0110_0101_0000; //1.650
'b11: data3[15:0] = 16'b0010_0100_0101_0111; //2.457
endcase
如果0~3位相加大于9,则加6调整为BCD码,并产生进位信号。再进行4~7位相加加上进位信号判断,再判断8-9位。
reg[:] c1; //低4位BCD进位信号
reg[:] c2; //中
reg[:] c3;
if(data1[:]+data2[:]+data3[:] < 'b01010) begin
disp_data[:] = data1[:]+data2[:]+data3[:];
c1 = ;
end
else begin
disp_data[:] = data1[:]+data2[:]+data3[:]-'b1010;
c1 = ;
end if(c1+data1[:]+data2[:]+data3[:] < 'b01010) begin
disp_data[:] = c1+data1[:]+data2[:]+data3[:];
c2 = ;
end
else begin
disp_data[:] = c1+data1[:]+data2[:]+data3[:]-'b1010;
c2 = ;
end if(c2+data1[:]+data2[:]+data3[:] < 'b01010) begin
disp_data[:] = c2+data1[:]+data2[:]+data3[:];
c3 = ;
end
else begin
disp_data[:] = c2+data1[:]+data2[:]+data3[:]-'b1010;
c3 = ;
end if(c3+data1[:]+data2[:]+data3[:] < 'b01010) begin
disp_data[:]=c3+data1[:]+data2[:]+data3[:] ;
end
else begin
disp_data[:]='bz;
end
这样再将以上两个部分放置到一个always块中即可。同样可以看到实际效果。将输入数据显示格式修改为十进制后,输入512时输出0001_0110_0101_0000。此时实际电压为512*3.296/1024=1.648,显示为1.650。输入256时实际电压0.824,显示为0.825。
如有更多问题,欢迎加入芯航线 FPGA 技术支持群交流学习:472607506
小梅哥
芯航线电子工作室
关于学习资料,小梅哥系列所有能够开放的资料和更新(包括视频教程,程序代码,教程文档,工具软件,开发板资料)都会发布在我的云分享。(记得订阅)链接:http://yun.baidu.com/share/home?uk=402885837&view=share#category/type=0
赠送芯航线AC6102型开发板配套资料预览版下载链接:链接:http://pan.baidu.com/s/1slW2Ojj 密码:9fn3
赠送SOPC公开课链接和FPGA进阶视频教程。链接:http://pan.baidu.com/s/1bEzaFW 密码:rsyh
【小梅哥FPGA进阶教程】第十三章 四通道数字电压表的更多相关文章
- 【小梅哥FPGA进阶教程】第九章 基于串口猎人软件的串口示波器
九.基于串口猎人软件的串口示波器 1.实验介绍 本实验,为芯航线开发板的综合实验,该实验利用芯航线开发板上的ADC.独立按键.UART等外设,搭建了一个具备丰富功能的数据采集卡,芯航线开发板负责进行数 ...
- 【小梅哥FPGA进阶教程】第十四章 TFT屏显示图片
十四.TFT屏显示图片 本文由杭电网友曾凯峰贡献,特此感谢 学习了小梅哥的TFT显示屏驱动设计后,想着在此基础上通过TFT屏显示一张图片,有了这个想法就开始动工了.首先想到是利用FPGA内部ROM存储 ...
- 【小梅哥FPGA进阶教程】第十二章 数字密码锁设计
十二.数字密码锁设计 本文由山东大学研友袁卓贡献,特此感谢 实验目的 实现数字密码锁设计,要求矩阵按键输出且数码管显示输入密码,密码输入正确与否均会有相应标志信号产生. 实验平台 芯航线FPGA核心板 ...
- 【小梅哥FPGA进阶教程】第十一章 四通道幅频相可调DDS信号发生器
十一.四通道幅频相可调DDS信号发生器 本文由山东大学研友袁卓贡献,特此感谢 实验目标 实现多通道可调信号发生器 实验平台 芯航线FPGA核心板.ADDA模块 实验现象 实现基于FPGA的多通道可调信 ...
- 【小梅哥FPGA进阶教程】MC8051软核在FPGA上的使用
十.MC8051软核在FPGA上的使用 本教程内容力求以详细的步骤和讲解让读者以最快的方式学会 MC8051 IP core 的应用以及相关设计软件的使用,并激起读者对 SOPC 技术的兴趣.本实验重 ...
- 【小梅哥FPGA进阶教程】串口发送图片数据到SRAM在TFT屏上显示
十五.串口发送图片数据到SRAM在TFT屏上显示 之前分享过rom存储图片数据在TFT屏上显示,该方法只能显示小点的图片,如果想显示TFT屏幕大小的图片上述方法rom内存大小不够.小梅哥给了个方案,利 ...
- 【小梅哥FPGA进阶学习之旅】基于Altera FPGA 的DDR2+千兆以太网电路设计
DDR2电路设计 在高速大数据的应用中,高速大容量缓存是必不可少的硬件.当前在FPGA系统中使用较为广泛的高速大容量存储器有经典速度较低的单数据速率的SDRAM存储器,以及速度较高的双速率DDR.DD ...
- 小梅哥FPGA数字逻辑设计教程——基于线性序列机的TLC5620型DAC驱动设计
基于线性序列机的TLC5620型DAC驱动设计 目录 TLC5620型DAC芯片概述: 2 TLC5620型DAC芯片引脚说明: 2 TLC5620型DAC芯片详细介绍: 3 TLC ...
- javascript进阶教程第三章--匿名和闭包--案例实战
javascript进阶教程第三章--匿名和闭包--案例实战 一.学习任务 通过几个小练习回顾学过的知识点 二.实例 练习1: 实例描述:打开页面后规定时间内弹出一个新窗口,新窗口指定时间后自动关闭. ...
随机推荐
- Python代码审计中一些需要重点关注的项
SQL注入: 如果是常规没有进行预编译,或者直接使用原生的进行拼凑,那么在view的时候就需要多去观察了 [PythonSQL预编译]https://www.cnblogs.com/sevck/p/6 ...
- Fastq 常用软件
文章转载于 Original 2017-06-08 Jolvii 生信百科 由于生物信息的大部分工作都是在没有 root 权限的集群上进行的,本期我主要介绍一下非 root 用户怎么安装常用的软件.工 ...
- 如何在Oracle中向Collection类型的变量中逐条插入数据
这篇文章将要介绍如果需要生成一个新的Collection并且向其中添加数据的方法. procedure insert_object(d in dept_array, d2 out dept_array ...
- 小酌Jmeter4.0新版本特性
1. 首先下载打开jmeter4.0,说一个能感受到的视觉变化,如图, 黑色界面,不少朋友认为做技术黑色的东西看起来高上大一点,虽然这个观念有点肤浅,但似乎也有点道理,毕竟还是有不少朋友热衷于lin ...
- C经典实例
1,九九乘法表 undefined reference to `sqrt' linux本身无math库 -lm,伪链接;
- [Z] 关于Python Tornado的一些资料
一个简单的样例: http://osedu.net/article/python/2014-03-18/501.html ioloop的官方doc: http://www.tornadoweb.org ...
- Arduino UNO 键盘记录器中时钟接到2口或3口,其它接口不行。马上就要放弃了。要修改例子中时钟的引脚。
- IDEA2018.2版本注册
IntelliJ IDEA 2018.2版本注册 1.到官网下载IDEA安装文件,windows版本ideaIU-2018.2.2.exe,然后安装: 2.下载补丁包JetbrainsCrack-3. ...
- c# typeof 与 Type.GetType 使用与效率对比
static void ReflectionTest() {//测试两种反射的效率问题 //Type.GetType()只能在同一个程序集中使用,typeof则可以跨程序集(assembly) //通 ...
- LUA 环境
LUA中环境是指一个函数执行的表,即一个函数在什么表中执行. 这里的函数是特殊的,是loadfile("x.lua")的返回值. loadfile("x.lua" ...