本实验是用ADC0809CCN进行数据采样,并用7段数码管进行显示。
ADC0809由一个8路模拟开关、一个地址锁存与译码器、一个A/D转换器和一个三态输出锁存器组成。多路开关可选通8个模拟通道,允许8路模拟量分时输入,共用A/D转换器进行转换。三态输出锁器用于锁存A/D转换完的数字量,当OE端为高电平时,才可以从三态输出锁存器取走转换完的数据。如下图所示。
时序图(本实验用上升沿去采数据):
原理图:
工作方式:
ALE为地址锁存允许输入线,高电平有效。当ALE线为高电平时,地址锁存与译码器将A,B,C三条地址线的地址信号进行锁存,经译码后被选中的通道的模拟量进入转换器进行转换。A,B和C为地址输入线,用于选通IN0-IN7上的一路模拟量输入,这里选通IN0。START为转换启动信号。当START上跳沿时,所有内部寄存器清零;下跳沿时,开始进行A/D转换;在转换期间,START应保持低电平。EOC为转换结束信号,在转换期间EOC为低。当EOC在为高电平时,表明转换结束;否则,表明正在进行A/D转换。OE为输出允许信号,用于控制三条输出锁存器向FPGA输出转换得到的数据。OE=1,输出转换得到的数据;OE=0,输出数据线呈高阻状态。D7-D0为数字量输出线,可以得到状态图。
状态图:
代码实现:
adc0809_control.v
1 module adc0809_control( 2 //input 3 sys_clk, 4 rst_n, 5 eoc,//adc转换结束信号标志 6 data,//adc转换出的数据, 7 8 //ouput 9 clk_adc,//给adc的时钟500HZ 10 ale,//adc地址锁存 11 start,//启动adc 12 address,//adc地址选通 13 oe,//控制adc数据传出 14 seg_data//传给数码显示 15 ); 16 input sys_clk;//27MHZ 17 input rst_n; 18 input eoc; 19 input [7:0] data; 20 21 output clk_adc; 22 output ale; 23 output start; 24 output [2:0] address; 25 output oe; 26 output [7:0] seg_data; 27 /*********************************************/ 28 parameter IDLE = 3'd0, 29 ALE = 3'd1, 30 START_P = 3'd2, 31 START_N = 3'd3, 32 CHECK_EOC_P = 3'd4, 33 CHECK_EOC_N = 3'd5, 34 OE = 3'd6, 35 SEND_DATA = 3'd7; 36 /*********************************************/ 37 //产生500kHZ的频率 38 reg clk_adc; 39 reg [4:0] cnt; 40 always @(posedge sys_clk or negedge rst_n) 41 if(!rst_n) begin 42 cnt <= 5'd0; 43 clk_adc <= 1'b0; 44 end 45 else if(cnt == 5'd26) 46 begin 47 cnt <= 5'd0; 48 clk_adc <= ~clk_adc; 49 end 50 else 51 cnt <= cnt + 1'b1; 52 /*********************************************/ 53 reg start; 54 reg ale; 55 reg oe; 56 reg [7:0] data_temp; 57 reg [2:0] state; 58 //always @(clk_adc or rst_n or eoc)//用组合逻辑方式不行,采集不到数据 59 always @(posedge clk_adc or negedge rst_n) 60 if(!rst_n) begin 61 start <= 1'b0; 62 ale <= 1'b0; 63 oe <= 1'b0; 64 data_temp <= 8'd0; 65 state <= IDLE; 66 end 67 else 68 case(state) 69 IDLE: begin 70 ale <= 1'b0; 71 start <= 1'b0; 72 oe <= 1'b0; 73 state <= ALE; 74 end 75 76 ALE: begin 77 ale <= 1'b1; 78 start <= 1'b0; 79 state <= START_P; 80 end 81 82 START_P: begin 83 ale <= 1'b0;//1 84 start <= 1'b1; 85 state <= START_N; 86 end 87 88 START_N: begin 89 ale <= 1'b0; 90 start <= 1'b0; 91 state <= CHECK_EOC_P; 92 end 93 94 CHECK_EOC_P: begin 95 if(eoc == 1'b1) 96 state = CHECK_EOC_P; 97 else 98 state = CHECK_EOC_N;//检测到了低电平,说明开始转换 99 end 100 101 CHECK_EOC_N: begin 102 if(eoc == 1'b0) 103 state <= CHECK_EOC_N;//等待转换的结束 104 else 105 state <= OE; 106 end 107 108 OE: begin 109 oe <= 1'b1; 110 state <= SEND_DATA; 111 end 112 113 SEND_DATA: begin 114 data_temp <= data; 115 state <= IDLE; 116 end 117 118 default: begin 119 ale <= 1'b0; 120 start <= 1'b0; 121 oe <= 1'b0; 122 state <= IDLE; 123 end 124 endcase 125 /*********************************************/ 126 assign address = 3'b000;//选通IN0 127 assign seg_data = data_temp; 128 /*********************************************/ 129 endmodule
display_control.v
1 module display_control( 2 //input 3 sys_clk, 4 rst_n, 5 seg_data, 6 7 //output 8 slec_wei, 9 slec_duan 10 ); 11 input rst_n; 12 input [7:0] seg_data; 13 input sys_clk; 14 15 output [3:0] slec_wei; 16 output [6:0] slec_duan; 17 /*****************************************/ 18 parameter SEG_NUM0 = 7'h3f,//c0, 19 SEG_NUM1 = 7'h06,//f9, 20 SEG_NUM2 = 7'h5b,//a4, 21 SEG_NUM3 = 7'h4f,//b0, 22 SEG_NUM4 = 7'h66,//99, 23 SEG_NUM5 = 7'h6d,//92, 24 SEG_NUM6 = 7'h7d,//82, 25 SEG_NUM7 = 7'h07,//F8, 26 SEG_NUM8 = 7'h7f,//80, 27 SEG_NUM9 = 7'h6f,//90, 28 SEG_NUMa = 7'h77, 29 SEG_NUMb = 7'h7c, 30 SEG_NUMc = 7'h39, 31 SEG_NUMd = 7'h5e, 32 SEG_NUMe = 7'h79, 33 SEG_NUMf = 7'h71; 34 /*****************************************/ 35 reg[7:0] cnt; 36 always @ (posedge sys_clk or negedge rst_n) 37 if(!rst_n) cnt <= 8'd0; 38 else cnt <= cnt+1'b1; 39 /*****************************************/ 40 wire[3:0] num; //用两位数码管显示 41 assign num = cnt[7] ? seg_data[7:4] : seg_data[3:0]; 42 assign slec_wei[0] = cnt[7]; 43 assign slec_wei[1] = ~cnt[7]; 44 45 //由于板上数码管是四位,另两位不点亮 46 assign slec_wei[2] = 1'b1; 47 assign slec_wei[3] = 1'b1; 48 49 reg [6:0] slec_duan; 50 always @ (posedge sys_clk) 51 case(num) //进行编码 52 4'h0: slec_duan <= SEG_NUM0; 53 4'h1: slec_duan <= SEG_NUM1; 54 4'h2: slec_duan <= SEG_NUM2; 55 4'h3: slec_duan <= SEG_NUM3; 56 4'h4: slec_duan <= SEG_NUM4; 57 4'h5: slec_duan <= SEG_NUM5; 58 4'h6: slec_duan <= SEG_NUM6; 59 4'h7: slec_duan <= SEG_NUM7; 60 4'h8: slec_duan <= SEG_NUM8; 61 4'h9: slec_duan <= SEG_NUM9; 62 4'ha: slec_duan <= SEG_NUMa; 63 4'hb: slec_duan <= SEG_NUMb; 64 4'hc: slec_duan <= SEG_NUMc; 65 4'hd: slec_duan <= SEG_NUMd; 66 4'he: slec_duan <= SEG_NUMe; 67 4'hf: slec_duan <= SEG_NUMf; 68 default:slec_duan <= SEG_NUM0; 69 endcase 70 /*****************************************/ 71 endmodule
adc0809_top.v
1 module adc0809_top(//input 2 sys_clk, 3 rst_n, 4 eoc, 5 data, 6 7 //output 8 clk_adc, 9 ale, 10 start, 11 address, 12 oe, 13 slec_wei, 14 slec_duan 15 ); 16 input sys_clk;//27MHZ 17 input rst_n; 18 input [7:0] data; 19 input eoc; 20 21 output clk_adc; 22 output ale; 23 output start; 24 output [2:0] address; 25 output oe; 26 output [3:0] slec_wei; 27 output [6:0] slec_duan; 28 29 wire [7:0] seg_data; 30 31 adc0809_control u1( 32 //input 33 .sys_clk(sys_clk), 34 .rst_n(rst_n), 35 .eoc(eoc),//adc转换结束信号标志 36 .data(data),//adc转换出的数据, 37 38 //ouput 39 .clk_adc(clk_adc),//给adc的时钟500HZ 40 .ale(ale),//adc地址锁存 41 .start(start),//启动adc 42 .address(address),//adc地址选通 43 .oe(oe),//控制adc数据传出 44 .seg_data(seg_data)//传给数码显示 45 ); 46 47 display_control u2( 48 //input 49 .sys_clk(sys_clk), 50 .rst_n(rst_n), 51 .seg_data(seg_data), 52 53 //output 54 .slec_wei(slec_wei), 55 .slec_duan(slec_duan) 56 ); 57 endmodule
仿真代码:
1 `timescale 1 ns/ 1 ps 2 module adc0809_top_vlg_tst(); 3 // constants 4 // general purpose registers 5 reg eachvec; 6 // test vector input registers 7 reg [7:0] data; 8 reg eoc; 9 reg rst_n; 10 reg sys_clk; 11 // wires 12 wire [2:0] address; 13 wire ale; 14 wire clk_adc; 15 wire oe; 16 wire [6:0] slec_duan; 17 wire [3:0] slec_wei; 18 wire start; 19 20 // assign statements (if any) 21 adc0809_top i1 ( 22 // port map - connection between master ports and signals/registers 23 .address(address), 24 .ale(ale), 25 .clk_adc(clk_adc), 26 .data(data), 27 .eoc(eoc), 28 .oe(oe), 29 .rst_n(rst_n), 30 .slec_duan(slec_duan), 31 .slec_wei(slec_wei), 32 .start(start), 33 .sys_clk(sys_clk) 34 ); 35 initial 36 begin 37 sys_clk =0; 38 rst_n = 0; 39 #100; 40 rst_n = 1; 41 end 42 always #19 sys_clk = ~sys_clk; 43 44 initial begin 45 data = 8'h0; 46 eoc = 1; 47 end 48 49 always @(negedge start) 50 begin 51 #2000; 52 eoc = 0; 53 data <= data + 1'b1; 54 #10000; 55 eoc = 1; 56 end 57 endmodule
仿真波形:
调试过程的心情:
看完人家的例子,时序图也看明白了,然后开始写自己的代码,在编译时也遇到很奇怪的问题,如下图,代码中找了许久都没找到,这让很头疼,后来我就把关于case这段语句剪切掉,只留接口定义部分,进行编译,编译OK,我在把刚才剪切掉的代码复制原位置(完全是一摸一样),编译既然通过了,很郁闷,这个问题一直没有弄明白,实在想不通,就跳过这个问题,既然编译通过了,那赶紧下载到板子上去验证吧,下完之后,显示两个0,变阻器无论怎么调都没用,哎,无语了,只能回过头在检查代码,看是否哪里有错误。实在检查不出来,只有仿真看时序对不对哦,仿真后还真没看出来有啥问题(很可能当时仿真代码没有写好,其实也没仔细的看波形),然后反复的看别人的代码,对照自己的代码是否哪里写的不对。最终把这个语句always @(clk_adc or rst_n or eoc)改为时序逻辑always @(posedge clk_adc or negedge rst_n)就可以了 ,终于看到了数码显示数据了,调电位器,数据也在变动,很高兴啊。。。。
总结:
1、例子最好找一个比较规范的例子,可以到网上收集相同的例子,进行参考对比。
2、遇到问题时,不要气馁,沉住气。问题总会解决的。