设计背景:
模数转换器,又称a/d转换器,简称adc,通常是指一个将模拟信号转换为抗干扰性更强的数字信号的电子器件。一般的adc是将一个输入电压信号转换为一个输出的数字信号。由于数字信号本身不具有实际意义,仅仅表示一个相对大小,故任何一个adc都需要一个参考模拟量作为转换标准。比较常见的参考标准为最大的可转换信号大小,而输出的数字量则表示输入信号相对于参考信号的大小。本设计则通过对模数转换芯片(tlc549)的采样控制,实现一个简易的数字电压表。
设计原理:
tlc549典型的配置电路如下图所示:
tlc549的端口描述如下:
tlc549是一个8位的串行模数转换器,a/d转换时间最大为17us,最大转换速率为4mhz。下图为tlc549的访问时序,从图中可以看出,tlc549的使用只需对外接输入输出时钟(i/o clk)和芯片选择(/cs)、输入的模拟信号(analog in)的控制。
分析时序图可知:当片选信号(/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)作比较。设计的架构图如下:
设计架构图对应端口的功能描述表:
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~71(tsu)
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
仿真图:
设计仿真图如下所示:
观察仿真图,实现了数据的采集,并正确显示,下板验证结果也达到了设计的预期效果。
关于水质毒性快速检测仪的产品介绍说明
Silicon Labs发布Wireless Xpress快速联网方案
肖特基二极管工作频率是多少?
盘点5款值得收藏的Linux开发板
苹果iPhone7/7plus国行价格5388元/6388元 性能配置横向大对比!
FPGA学习系列:29. 数字电压表设计(AD)
dfrobot迷你阻燃插头XT30 恒定15A 瞬时30A简介
时代赋予柔性导电软连接的使命,金桥铜业将不负众望
uc3844引脚图及功能_封装
一文解析美打压中兴的原因是什么?
什么是核心交换机_选择核心交换机要考虑的因素
宁德时代拟定增募资不超过582亿元 宁德时代最新消息
2016电子设备产品环保涂料研讨会在深圳顺利举行
物联网时代,WiFi厂商在争什么?
蓝牙是射频技术吗?蓝牙射频技术是什么
英飞凌发布适用于xEV主逆变器的功率模块 打造高性价比电动汽车产品组合
基于STM32单片机的疫苗冷链加温湿度系统设计
陀螺仪的工作原理 陀螺仪在航天航空中的应用
聊一下解梯度折射率
苹果获汽车专利,“泰坦计划”开始实施