0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

FPGA学习系列:29. 数字电压表设计(AD)

FPGA学习交流 2018-08-15 14:07 次阅读

设计背景:

模数转换器,又称A/D转换器,简称ADC,通常是指一个将模拟信号转换为抗干扰性更强的数字信号电子器件。一般的ADC是将一个输入电压信号转换为一个输出的数字信号。由于数字信号本身不具有实际意义,仅仅表示一个相对大小,故任何一个ADC都需要一个参考模拟量作为转换标准。比较常见的参考标准为最大的可转换信号大小,而输出的数字量则表示输入信号相对于参考信号的大小。本设计则通过对模数转换芯片(TLC549)的采样控制,实现一个简易的数字电压表。

设计原理:

TLC549典型的配置电路如下图所示

image.png

TLC549的端口描述如下:

image.png

TLC549是一个8位的串行模数转换器,A/D转换时间最大为17us,最大转换速率为4MHz。下图为TLC549的访问时序,从图中可以看出,TLC549的使用只需对外接输入输出时钟I/O CLK芯片选择(/CS)、输入的模拟信号(ANALOG IN)的控制。

image.png


分析时序图可知:当片选信号(/CS)拉低时,ADC前一次的转换数据(A)的最高位A7立即出现在数据线DATA OUT上,之后的数据在时钟I/O CLOCK的下降沿改变,可在I/O CLOCK的上升沿读取数据。转换时,/CS要置为高电平。在设计操作时,要注意Tsu(CS)、Tconv、Twh(CS)和I/O CLOCK的频率这几个参数Tsu(CS)为CS拉低到I/O CLOCK第一个时钟到来的时间,至少要1.4us;Twh(CS)为ADC的转换时钟,不超过17us,Tconv的值也不超过17us;I/O CLOCK为 1.1MHz。其他参数可参考数据手册。

由于ADC是8位的,所以采样的电压值为:

V =(D*Vref)/256

其中V为采样的电压值;D为ADC转换后读取的8位二进制数;Vref为参考电压值,此处为2.5V。


设计架构图:

本设计通过调节电位器RW1改变ADC的模拟输入值,数据采样读取后由数码管显示,最后用万用表测量输入电压,并与读取在数码管上的数据(单位为mV)作比较。设计的架构图如下:

image.png


设计架构图对应端口的功能描述表:

image.png


tlc549_Driver模块采用序列机实现接口访问时序,并且产生1MHz的ADC_Clk和采集到ADC_data;Control模块,将采集到的ADC数据(ADC_data)换算成对应的电压值并经过二进制到BCD转换以后传送到数码管DIG_LED_DRIVE模块负责数码管的驱动,将传递过来的数据显示出来。


设计代码:

tlc549_Driver模块代码:

0moduletlc549_Driver(Clk,Rst_n,En,ADC_Din,ADC_Clk,ADC_Cs_n,Data,Get_Flag);

1

2inputClk;//系统50MHz时钟输入

3inputRst_n;//全局复位

4inputEn;//ADC转换使能,高电平有效

5

6inputADC_Din;//ADC串行数据输入

7

8outputregADC_Clk;//ADC时钟信号输出

9outputregADC_Cs_n;//ADC片选信号输出

10outputregGet_Flag;//数据转换完成标志

11outputreg[7:0]Data;//ADC转换以后的电压值

12

13reg[10:0]Cnt1;//系统时钟计数器

14reg[7:0]data_tmp;//数据寄存器

15

16//系统时钟上升沿计数

17always@(posedgeClkornegedgeRst_n)

18begin

19if(!Rst_n)

20Cnt1<=11'd0;

21elseif(!En)

22Cnt1<=11'd0;

23elseif(Cnt1==11'd1310)

24Cnt1<=11'd0;

25else

26Cnt1<=Cnt1+1'b1;

27end

28

29always@(posedgeClkornegedgeRst_n)

30begin

31if(!Rst_n)

32begin

33ADC_Clk<=1'b0;

34ADC_Cs_n<=1'b1;

35data_tmp<=8'd0;

36Data<=8'd0;

37end

38elseif(En)

39begin

40case(Cnt1)

411:ADC_Cs_n<=1'b0;//1~71Tsu

4271:beginADC_Clk<=1;data_tmp[7]<=ADC_Din;end

4396:ADC_Clk<=0;

44121:beginADC_Clk<=1;data_tmp[6]<=ADC_Din;end

45146:ADC_Clk<=0;

46171:beginADC_Clk<=1;data_tmp[5]<=ADC_Din;end

47196:ADC_Clk<=0;

48221:beginADC_Clk<=1;data_tmp[4]<=ADC_Din;end

49246:ADC_Clk<=0;

50271:beginADC_Clk<=1;data_tmp[3]<=ADC_Din;end

51296:ADC_Clk<=0;

52321:beginADC_Clk<=1;data_tmp[2]<=ADC_Din;end

53346:ADC_Clk<=0;

54371:beginADC_Clk<=1;data_tmp[1]<=ADC_Din;end

55396:ADC_Clk<=0;

56421:beginADC_Clk<=1;data_tmp[0]<=ADC_Din;end

57446:beginADC_Clk<=0;ADC_Cs_n<=1'b1;Get_Flag<=1;end

58447:beginData<=data_tmp;Get_Flag<=0;end//447~1310(Twh)

591310:;

60default:;

61endcase

62end

63else

64begin

65ADC_Cs_n<=1'b1;

66ADC_Clk<=1'b0;

67end

68end

69

70endmodule


Control模块代码:

0moduleControl(Clk,Rst_n,Get_Flag,ADC_data,seg_data);

1

2inputClk;//系统时钟输入

3inputRst_n;//系统复位

4inputGet_Flag;//ADC采集数据完成标志

5input[7:0]ADC_data;//ADC采集数据输入

6

7outputreg[23:0]seg_data;//数码管待显示数据

8

9reg[3:0]qianwei;//千位

10reg[3:0]baiwei;//百位

11reg[3:0]shiwei;//十位

12reg[3:0]gewei;//个位

13reg[15:0]tenvalue;//采样的电压值

14

15//采集电压值计算

16always@(posedgeClkornegedgeRst_n)

17begin

18if(!Rst_n)

19tenvalue<=0;

20elseif(Get_Flag)//新的数据采集完成,可以进行计算

21tenvalue<=(ADC_data*100*25)/256;

22end

23

24//二进制转BCD

25always@(posedgeClkornegedgeRst_n)

26begin

27if(!Rst_n)

28begin

29qianwei<=0;

30baiwei<=0;

31shiwei<=0;

32gewei<=0;

33end

34else

35begin

36qianwei<=tenvalue/1000;//2

37baiwei<=(tenvalue/100)%10;//5

38shiwei<=(tenvalue/10)%10;//0

39gewei<=tenvalue%10;//0

40end

41end

42

43//数码管显示数值

44always@(posedgeClkornegedgeRst_n)

45begin

46if(!Rst_n)

47seg_data<=0;

48else

49seg_data<={

50qianwei,//千位

51baiwei,//百位

52shiwei,//十位

53gewei,//个位

548'hFF//空闲

55};

56end

57

58endmodule


DIG_LED_DRIVE模块代码:

0/*数码管扫描模块,位选为外部74hc138译码器进行控制*/

1/*仿真时请将本文件设置为顶层,并在代码中根据相应注释中的内容选择cnt1_MAX = 24*/

2

3moduleDIG_LED_DRIVE(Clk,Rst_n,Data,Dig_Led_seg,Dig_Led_sel);

4

5inputClk;//系统时钟输入

6inputRst_n;//系统复位

7input[23:0]Data;//待显示数据

8

9output[7:0]Dig_Led_seg;//数码管段选

10output[2:0]Dig_Led_sel;//数码管位选

11

12parametersystem_clk=50_000_000;

13

14// localparam cnt1_MAX = 24;/*仿真的时候使用,板级验证时请注释掉*/

15localparamcnt1_MAX=system_clk/1000/2-1;/*板级验证的时候使用,仿真时请注释掉*/

16

17reg[14:0]cnt1;//分频计数器

18regclk_1K;//扫描时钟,1KHz

19reg[2:0]sel_r;//数码管位选

20reg[7:0]seg_r;//数码管段选

21reg[3:0]disp_data;//单位显示数据缓存

22

23//1KHz时钟分频计数器

24always@(posedgeClk)

25begin

26if(!Rst_n)cnt1<=0;

27elseif(cnt1==cnt1_MAX)cnt1<=0;

28elsecnt1<=cnt1+1'b1;

29end

30

31//得到1KHz时钟

32always@(posedgeClkornegedgeRst_n)

33begin

34if(!Rst_n)

35clk_1K<=0;

36elseif(cnt1==cnt1_MAX)

37clk_1K<=~clk_1K;

38end

39

40//位选信号控制

41always@(posedgeclk_1KornegedgeRst_n)

42begin

43if(!Rst_n)

44sel_r<=3'd0;

45elseif(sel_r==3'd3)

46sel_r<=3'd0;

47else

48sel_r<=sel_r+1'b1;

49end

50

51//根据不同的数码管位选择不同的待显示数据

52always@(*)

53begin

54if(!Rst_n)

55disp_data=4'd0;

56else

57begin

58case(sel_r)

593'd0:disp_data=Data[23:20];

603'd1:disp_data=Data[19:16];

613'd2:disp_data=Data[15:12];

623'd3:disp_data=Data[11:8];

633'd4:disp_data=Data[7:4];

643'd5:disp_data=Data[3:0];

65default:disp_data=4'd0;

66endcase

67end

68end

69

70//数据译码,将待显示数据翻译为符合数码管显示的编码

71always@(*)

72begin

73if(!Rst_n)

74seg_r=8'hff;

75else

76begin

77case(disp_data)

784'd0:seg_r=8'hc0;

794'd1:seg_r=8'hf9;

804'd2:seg_r=8'ha4;

814'd3:seg_r=8'hb0;

824'd4:seg_r=8'h99;

834'd5:seg_r=8'h92;

844'd6:seg_r=8'h82;

854'd7:seg_r=8'hf8;

864'd8:seg_r=8'h80;

874'd9:seg_r=8'h90;

884'd10: seg_r=8'h88;

894'd11: seg_r=8'h83;

904'd12: seg_r=8'hc6;

914'd13: seg_r=8'ha1;

924'd14: seg_r=8'h86;

934'd15: seg_r=8'h8e;

94default:seg_r=8'hff;

95endcase

96end

97end

98

99assignDig_Led_seg=seg_r;

100assignDig_Led_sel=sel_r;

101

102endmodule

AD_TLC549顶层模块代码

0moduleAD_TLC549(Clk,Rst_n,ADC_Din,ADC_Clk,ADC_Cs_n,Dig_Led_sel,Dig_Led_seg);

1

2inputClk;

3inputRst_n;

4inputADC_Din;

5

6outputADC_Clk;

7outputADC_Cs_n;

8output[2:0]Dig_Led_sel;

9output[7:0]Dig_Led_seg;

10

11wireGet_Flag;

12wire[7:0]ADC_data;

13wire[23:0]seg_data;

14

15tlc549_Drivertlc549_Driver(

16.Clk(Clk),

17.Rst_n(Rst_n),

18.En(1'b1),

19.ADC_Din(ADC_Din),

20.ADC_Clk(ADC_Clk),

21.ADC_Cs_n(ADC_Cs_n),

22.Data(ADC_data),

23.Get_Flag(Get_Flag)

24);

25

26ControlControl(

27.Clk(Clk),

28.Rst_n(Rst_n),

29.Get_Flag(Get_Flag),

30.ADC_data(ADC_data),

31.seg_data(seg_data)

32);

33

34DIG_LED_DRIVEDIG_LED_DRIVE(

35.Clk(Clk),

36.Rst_n(Rst_n),

37.Data(seg_data),

38.Dig_Led_seg(Dig_Led_seg),

39.Dig_Led_sel(Dig_Led_sel)

40);

41

42endmodule


AD_TLC549_tb顶层测试代码如下:

0`timescale1ns/1ps

1

2moduleAD_TLC549_tb;

3

4regClk;

5regRst_n;

6regADC_Din;

7

8wireADC_Clk;

9wireADC_Cs_n;

10wire[2:0]Dig_Led_sel;

11wire[7:0]Dig_Led_seg;

12

13initialbegin

14Clk=1;

15Rst_n=0;

16ADC_Din=0;

17#200.1

18Rst_n=1;

19

20#1400ADC_Din=1;//aa

21#1000ADC_Din=0;

22#1000ADC_Din=1;

23#1000ADC_Din=0;

24#1000ADC_Din=1;

25#1000ADC_Din=0;

26#1000ADC_Din=1;

27#1000ADC_Din=0;

28

29#17000

30#1400ADC_Din=1;//98

31#1000ADC_Din=0;

32#1000ADC_Din=0;

33#1000ADC_Din=1;

34#1000ADC_Din=1;

35#1000ADC_Din=0;

36#1000ADC_Din=0;

37#1000ADC_Din=0;

38

39end

40

41AD_TLC549AD_TLC549_dut(

42.Clk(Clk),

43.Rst_n(Rst_n),

44.ADC_Din(ADC_Din),

45.ADC_Clk(ADC_Clk),

46.ADC_Cs_n(ADC_Cs_n),

47.Dig_Led_sel(Dig_Led_sel),

48.Dig_Led_seg(Dig_Led_seg)

49);

50

51always#10Clk=~Clk;

52

53endmodule

仿真图:

设计仿真图如下所示:




观察仿真图,实现了数据的采集,并正确显示,下板验证结果也达到了设计的预期效果。





声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • FPGA
    +关注

    关注

    1601

    文章

    21295

    浏览量

    593031
收藏 人收藏

    评论

    相关推荐

    基于单片机AT89C51的数字电压表仿真实现,原理图,论文,源码

    设计要求 1.以MCS-51系列单片机为核心器件,设计一个简单的直流数字电压表; 2.电压量程:0~5V; 3.最小分辨率:0.01V; 4.所用元器件较少,成本低,且测量精度和可靠性
    发表于 04-03 20:16

    电压表原理及使用方法 电压表串联在电路中会怎么样

    电压表是测量电路中电势差(电压)的一种电器仪器。它能够通过测量两个点之间的电势差来确定电路的电压。在以下文章中,我们将详细介绍电压表的原理和使用方法,并探讨
    的头像 发表于 02-02 13:39 679次阅读

    电压表的使用方法 电压表的电阻有多大

    电压表是用来测量电路中电压的仪器,可以测量直流电压和交流电压。使用电压表需要注意一些注意事项,同时了解
    的头像 发表于 02-02 11:05 454次阅读

    数字电压表测量的基本工作原理

    数字电压表是一种用于测量电压的仪器。它基于一些基本的工作原理来实现测量的准确性和可靠性。本文将详细介绍数字电压表的基本工作原理,以及其在测量
    的头像 发表于 01-25 13:55 509次阅读

    数字电压表的固有误差由什么构成

    数字电压表的固有误差是指在一定条件下,由于仪器本身存在的不确定因素所导致的测量结果与待测量真值之间的偏差。数字电压表的固有误差主要由以下几个方面构成: 量程误差: 量程误差是指
    的头像 发表于 01-16 15:35 367次阅读

    数字电压表测量的基本工作原理

    数字电压表是一种常用的测试仪器,它能够准确测量电路中的电压值。要了解数字电压表的工作原理,需要了解它是如何将输入信号转换为
    的头像 发表于 01-11 09:23 496次阅读

    multism为什么电压表没识数

    Multism是一款常用的电路模拟软件,它在电路设计、仿真等领域广泛应用。然而,在使用Multism进行电路仿真时,有时可能会遇到电压表无法识数的情况。本文将对Multism电压表无法识数的原因进行
    的头像 发表于 01-04 16:46 351次阅读

    电压表的符号是什么样的,电压表分几种类型

    用于测量交流电压。它通过固定在校准刻度上的指针显示读数。指针的偏转取决于作用在其上的扭矩。产生的扭矩的大小与测量电压成正比。   数字电压表   以
    发表于 09-01 16:43

    电压表是并联还是串联?电压表并联连接的原因

      电压表的结构使其内阻始终保持高。如果它与电路串联,则由于测量电压而流过的电流最小化。因此,干扰电压表的读数。   电压表始终与电路并联连接,以便电路上出现相同的压降。
    发表于 09-01 16:39

    电压表具有高电阻吗为什么

    电压表具有非常高的内阻,因为它测量电路两点之间的电位差。电压表不会改变测量设备的电流。 如果电压表的电阻很低,则电流通过它,电压表给出不正确的结果。
    发表于 09-01 16:36

    基于微控制器的数字电压表设计方案,使用8051微控制器的数字电压表的实现方法

    数字电压表是一种测量电信号电压的电子仪器。它用于各种应用,包括电子、电力系统和自动化领域。在本文中,我们将讨论使用8051微控制器的数字电压表
    的头像 发表于 07-18 16:07 929次阅读
    基于微控制器的<b class='flag-5'>数字</b><b class='flag-5'>电压表</b>设计方案,使用8051微控制器的<b class='flag-5'>数字</b><b class='flag-5'>电压表</b>的实现方法

    icl7135数字电压表制作,基于icl7107的数字电压表

    ICL7107是一款高精度模数转换器(ADC),可用于构建数字电压表。它具有3-1/2位显示分辨率,可测量高达200mV、2V和20V满量程的电压,并采用+5V单电源电压工作。
    的头像 发表于 07-18 15:49 3847次阅读
    icl7135<b class='flag-5'>数字</b><b class='flag-5'>电压表</b>制作,基于icl7107的<b class='flag-5'>数字</b><b class='flag-5'>电压表</b>

    Proteus教程:简易电压表

    Proteus教程:简易电压表
    的头像 发表于 06-14 11:33 2779次阅读
    Proteus教程:简易<b class='flag-5'>电压表</b>

    基于单片机的数字电压表proteus仿真程序

    基于单片机的数字电压表proteus仿真资料
    发表于 05-22 15:32 1次下载

    基于51单片机数字电压表Proteus仿真程序

    基于51单片机数字电压表Proteus仿真设计资料
    发表于 05-22 15:20 2次下载