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

描述

设计背景: 

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

 

设计原理: 

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

FPGA

    TLC549的端口描述如下:

FPGA

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

FPGA


    分析时序图可知:当片选信号(/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)作比较。设计的架构图如下:

FPGA


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

FPGA


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


设计代码: 

    tlc549_Driver模块代码:

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

1 

2       input Clk;  //系统50MHz时钟输入

3       input Rst_n;//全局复位

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

5       

6       input ADC_Din;//ADC串行数据输入

7       

8       output reg ADC_Clk; //ADC时钟信号输出

9       output reg ADC_Cs_n;//ADC片选信号输出

10      output reg Get_Flag;//数据转换完成标志

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

12      

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

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

15      

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

17      always@(posedge Clk or negedge Rst_n)

18      begin 

19          if(!Rst_n)

20              Cnt1 <= 11'd0;

21          else if(!En)

22              Cnt1 <= 11'd0;

23          else if(Cnt1 == 11'd1310)

24              Cnt1 <= 11'd0;

25          else 

26              Cnt1 <= Cnt1 + 1'b1;    

27      end

28      

29      always@(posedge Clk or negedge Rst_n)

30      begin

31          if(!Rst_n)

32              begin

33                  ADC_Clk  <= 1'b0;

34                  ADC_Cs_n <= 1'b1;

35                  data_tmp <= 8'd0;

36                  Data <= 8'd0;

37              end

38          else if(En)

39              begin

40                  case(Cnt1)

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

42                      71      :   begin ADC_Clk <= 1; data_tmp[7] <= ADC_Din;end

43                      96      :   ADC_Clk <= 0;

44                      121 :   begin ADC_Clk <= 1; data_tmp[6] <= ADC_Din;end

45                      146 :   ADC_Clk <= 0;

46                      171 :   begin ADC_Clk <= 1; data_tmp[5] <= ADC_Din;end

47                      196 :   ADC_Clk <= 0;

48                      221 :   begin ADC_Clk <= 1; data_tmp[4] <= ADC_Din;end

49                      246 :   ADC_Clk <= 0;

50                      271 :   begin ADC_Clk <= 1; data_tmp[3] <= ADC_Din;end

51                      296 :   ADC_Clk <= 0;

52                      321 :   begin ADC_Clk <= 1; data_tmp[2] <= ADC_Din;end

53                      346 :   ADC_Clk <= 0;

54                      371 :   begin ADC_Clk <= 1; data_tmp[1] <= ADC_Din;end

55                      396 :   ADC_Clk <= 0;

56                      421 :   begin ADC_Clk <= 1; data_tmp[0] <= ADC_Din;end

57                      446 :   begin ADC_Clk <= 0; ADC_Cs_n <= 1'b1; Get_Flag<=1;end

58                      447 :   begin Data <= data_tmp;  Get_Flag<=0; end //447~1310(Twh)

59                      1310:   ;

60                      default:;

61                  endcase 

62              end

63          else

64              begin

65                  ADC_Cs_n <= 1'b1;

66                  ADC_Clk <= 1'b0;

67              end

68      end 

69

70  endmodule 


Control模块代码:

0   module Control(Clk,Rst_n,Get_Flag,ADC_data,seg_data);

1       

2       input Clk;//系统时钟输入

3       input Rst_n;//系统复位

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

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

6       

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

8       

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

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

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

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

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

14      

15      //采集电压值计算

16      always@(posedge Clk or negedge Rst_n)

17      begin

18          if(!Rst_n)

19              tenvalue<=0;

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

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

22      end     

23          

24      //二进制转BCD

25      always@(posedge Clk or negedge Rst_n)

26      begin

27          if(!Rst_n) 

28              begin 

29                  qianwei<=0;

30                  baiwei<=0;

31                  shiwei<=0;

32                  gewei<=0;

33              end

34          else 

35          begin 

36              qianwei<=tenvalue/1000;     //2

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

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

39              gewei<=tenvalue%10;         //0

40          end         

41      end 

42          

43      //数码管显示数值

44      always@(posedge Clk or negedge Rst_n)

45      begin

46          if(!Rst_n)

47              seg_data<=0;

48          else

49              seg_data<={

50                          qianwei,  //千位

51                          baiwei,   //百位

52                          shiwei,   //十位

53                          gewei,    //个位

54                          8'hFF     //空闲

55                      };

56      end 

57      

58  endmodule 


DIG_LED_DRIVE模块代码:

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

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

2  

3       module DIG_LED_DRIVE(Clk,Rst_n,Data,Dig_Led_seg,Dig_Led_sel);

4  

5       input Clk;       //系统时钟输入

6       input Rst_n;     //系统复位

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

8       

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

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

11      

12      parameter system_clk = 50_000_000;

13      

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

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

16 

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

18      reg clk_1K;         //扫描时钟,1KHz

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

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

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

22 

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

24      always@(posedge Clk)

25      begin

26          if(!Rst_n)cnt1<=0;

27          else if(cnt1==cnt1_MAX)cnt1<=0;

28          else cnt1<=cnt1+1'b1;

29      end

30 

31      //得到1KHz时钟

32      always@(posedge Clk or negedge Rst_n)

33      begin

34          if(!Rst_n)

35              clk_1K<=0;

36          else if(cnt1==cnt1_MAX)

37              clk_1K<=~clk_1K;

38      end 

39      

40      //位选信号控制

41      always@(posedge clk_1K or negedge Rst_n)

42      begin

43          if(!Rst_n)

44              sel_r<=3'd0;

45          else if(sel_r == 3'd3)

46              sel_r<=3'd0;

47          else

48              sel_r<=sel_r+1'b1;

49      end 

50 

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

52      always@(*)

53      begin

54          if(!Rst_n)

55              disp_data=4'd0;

56          else

57              begin

58                  case(sel_r)

59                      3'd0:disp_data=Data[23:20];

60                      3'd1:disp_data=Data[19:16];

61                      3'd2:disp_data=Data[15:12];

62                      3'd3:disp_data=Data[11:8];

63                      3'd4:disp_data=Data[7:4];

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

65                      default :disp_data=4'd0;

66                  endcase

67              end

68      end 

69 

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

71      always@(*)

72      begin   

73          if(!Rst_n)

74              seg_r=8'hff;

75          else

76              begin

77                  case(disp_data)

78                      4'd0:       seg_r=8'hc0;

79                      4'd1:       seg_r=8'hf9;

80                      4'd2:       seg_r=8'ha4;

81                      4'd3:       seg_r=8'hb0;

82                      4'd4:       seg_r=8'h99;

83                      4'd5:       seg_r=8'h92;

84                      4'd6:       seg_r=8'h82;

85                      4'd7:       seg_r=8'hf8;

86                      4'd8:       seg_r=8'h80;

87                      4'd9:       seg_r=8'h90;

88                      4'd10:   seg_r=8'h88;

89                      4'd11:    seg_r=8'h83;

90                      4'd12:    seg_r=8'hc6;

91                      4'd13:   seg_r=8'ha1;

92                      4'd14:   seg_r=8'h86;

93                      4'd15:   seg_r=8'h8e;

94                      default : seg_r=8'hff;

95                  endcase

96              end

97      end 

98 

99      assign Dig_Led_seg = seg_r;

100     assign Dig_Led_sel = sel_r;

101

102 endmodule 

AD_TLC549顶层模块代码

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

1 

2       input Clk;

3       input Rst_n;

4       input ADC_Din;

5 

6       output ADC_Clk;

7       output ADC_Cs_n;

8       output [2:0]Dig_Led_sel;

9       output [7:0]Dig_Led_seg;

10

11      wire Get_Flag;

12      wire [7:0]ADC_data;

13      wire [23:0]seg_data;

14

15      tlc549_Driver tlc549_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

26      Control Control(

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

34      DIG_LED_DRIVE DIG_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              

42  endmodule 


AD_TLC549_tb顶层测试代码如下:

0   `timescale 1ns/1ps

1 

2   module AD_TLC549_tb;

3 

4       reg Clk;

5       reg Rst_n;

6       reg ADC_Din;

7 

8       wire ADC_Clk;

9       wire ADC_Cs_n;

10      wire [2:0] Dig_Led_sel;

11      wire [7:0] Dig_Led_seg;

12      

13      initial begin

14          Clk = 1;

15          Rst_n = 0;

16          ADC_Din = 0;

17          #200.1 

18          Rst_n = 1;

19

20          #1400 ADC_Din=1; //aa

21          #1000 ADC_Din=0;

22          #1000 ADC_Din=1;

23          #1000 ADC_Din=0;

24          #1000 ADC_Din=1;

25          #1000 ADC_Din=0;

26          #1000 ADC_Din=1;

27          #1000 ADC_Din=0;  

28          

29          #17000 

30          #1400 ADC_Din=1; //98

31          #1000 ADC_Din=0;

32          #1000 ADC_Din=0;

33          #1000 ADC_Din=1;

34          #1000 ADC_Din=1;

35          #1000 ADC_Din=0;

36          #1000 ADC_Din=0;

37          #1000 ADC_Din=0;

38          

39      end 

40

41      AD_TLC549 AD_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

51      always #10 Clk = ~Clk;

52

53  endmodule 

 

仿真图: 

设计仿真图如下所示:




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





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

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分