ADC_TLC549是一个8位串行模数转换器,A/D转化时间不超过17us, I/O频率不能超过1.1MHZ.如图所示,为TLC549的时序图,从图中可以看出,当cs拉低时,ADC前一次的转换数据A的最高位A7立即出现在DATA_OUT上,之后数据在I/O clock的下降沿变化,在上升沿读取数据。读取完8位数据后,ADC开始转换这一次采集的信号,转化时间不超过17us,转换时,cs要给高电平。总之,操作时序时需注意tsu、tconv、I/0 clock几个参数,
tsu为cs拉低到第一个I/O clock时钟上升沿所需的时间至少需要1.4us; tconv为ADC转换数据所需的时间,不超过17us; I/O clock时钟不超过1.1MHZ.
程序代码
//程序实现的功能:将测得模拟电压(0-255)转化数字电压显示在数码管上; module adc_tlc549 //顶层模块; ( clk, rst_n, analog_data, adc_cs_n, adc_clk, smg_sel, smg_data ); input clk; //系统时钟,50MHZ; input rst_n; //复位信号,低电平有效; input analog_data; //模拟数据; output adc_cs_n; //ADC_TLC549片选信号,低电平有效; output adc_clk; //ADC_TLC549 I/O时钟,最大不超过1.1MHZ; output[1:0]smg_sel; //数码管片选; output[7:0]smg_data; //数码管段选; wire adc_en; //增加了adc_en使能信号来决定是否需要开启AD转换,当一个系统内部有多个模块时 assign adc_en=1'b1; //每一个模块都是可控制的,禁止使能,模块内部停止工作,此时功耗最低; wire[3:0] vol_int; //从A/D转换芯片输出的电压值的整数部分; wire[3:0] vol_dec; //从A/D转换芯片输出的电压值的小数部分; adc_ctl adc_ctl //ADC控制模块 ( .clk(clk), .rst_n(rst_n), .analog_data(analog_data), .adc_en(adc_en), .adc_cs_n(adc_cs_n), .adc_clk(adc_clk), .vol_int(vol_int), .vol_dec(vol_dec) ); smg smg //数码管模块 ( .clk(clk), .rst_n(rst_n), .data_1(vol_int), .data_2(vol_dec), .smg_sel(smg_sel), .smg_data(smg_data) ); endmodule module adc_ctl //ADC控制模块 ( clk, rst_n, analog_data, adc_en, adc_cs_n, adc_clk, vol_int, vol_dec ); input clk; //系统时钟,50MHZ; input rst_n; //复位信号,低电平有效; input analog_data; //模拟数据; input adc_en; //ADC使能信号; output adc_cs_n; //ADC_TLC549片选信号,低电平有效; output adc_clk; //ADC_TLC549 I/O时钟,最大不超过1.1MHZ; output[3:0]vol_int; //从A/D转换芯片输出的电压值的整数部分; output[3:0]vol_dec; //从A/D转换芯片输出的电压值的小数部分; //1.1MHZ(909ns) 909ns/20ns=46=0x2E, 0x2E/2=0X17; //17us 17000ns/20ns=850=0x352; parameter ADC_CLK_CNT=6'h2e; parameter ADC_CLK_HALF=6'h17; parameter IDLE= 2'b00; // 状态机的初始状态; parameter READY= 2'b01; // adc_cs_n有效时到adc_clk到第一个时钟到来时的状态,至少要1.4us; parameter READ= 2'b10; //是读ADC转化后数据的状态,8个时钟的上升沿逐位移入ADC数据线上的8位串行数据; parameter WAIT_CONV=2'b11; //是ADC正在转化数据需要消耗的时间,是17us; reg[1:0] cstate; reg[1:0] nstate; reg[5:0] time_cnt; always @ (posedge clk or negedge rst_n) begin if(!rst_n) time_cnt<=6'd0; else if(!adc_en) time_cnt<=6'd0; else if(time_cnt==ADC_CLK_CNT) time_cnt<=6'd0; else time_cnt<=time_cnt+1'b1; end reg [5:0] bit_cnt; always @ (posedge clk or negedge rst_n) begin if(!rst_n) bit_cnt<=6'd0; else if(!adc_en) bit_cnt<=6'd0; else if(cstate!=nstate) bit_cnt<=6'd0; else if(time_cnt==ADC_CLK_HALF) bit_cnt<=bit_cnt+1'b1; end always @ (posedge clk or negedge rst_n) begin if(!rst_n) cstate<=IDLE; else cstate<=nstate; end always @ (*) begin case(cstate) IDLE : if((bit_cnt==6'h2)&&(time_cnt==ADC_CLK_CNT)&&(adc_en)) nstate=READY; else nstate=IDLE; READY : if((bit_cnt==6'h1)&&(time_cnt==ADC_CLK_CNT)) //延时大约1.4us; nstate=READ; else nstate=READY; READ : if((bit_cnt==6'h8)&&(time_cnt==ADC_CLK_CNT)) nstate=WAIT_CONV; else nstate=READ; WAIT_CONV : if((bit_cnt==6'h12)&&(time_cnt==ADC_CLK_CNT)) nstate=READY; else nstate=WAIT_CONV; default : nstate=IDLE; endcase end reg adc_clk_r; always @ (posedge clk or negedge rst_n) begin if(!rst_n) adc_clk_r<=1'b0; else if(cstate!=READ) adc_clk_r<=1'b0; else if(time_cnt==ADC_CLK_HALF) adc_clk_r<=1'b1; else if(time_cnt==ADC_CLK_CNT) adc_clk_r<=1'b0; end assign adc_clk=adc_clk_r; reg adc_cs_n_r; always @ (posedge clk or negedge rst_n) begin if(!rst_n) adc_cs_n_r<=1'b0; else if(!adc_en) adc_cs_n_r<=1'b1; else if((cstate==READY)||(cstate==READ)) adc_cs_n_r<=1'b0; else adc_cs_n_r<=1'b1; end assign adc_cs_n=adc_cs_n_r; // 脉冲边沿检测 reg adc_clk_1; reg adc_clk_2; always @ (posedge clk or negedge rst_n) begin if(!rst_n) begin adc_clk_1<=1'b0; adc_clk_2<=1'b0; end else begin adc_clk_1<=adc_clk; adc_clk_2<=adc_clk_1; end end wire pos_clk; assign pos_clk=adc_clk_1&(~adc_clk_2) ; //脉冲上升沿边沿检测; reg[7:0] adc_data_r ; always @(posedge clk or negedge rst_n) begin if(!rst_n) adc_data_r<=8'd0; else if((cstate==READ)&&(pos_clk)) adc_data_r={adc_data_r[6:0],analog_data}; end reg[7:0] adc_data; always @(posedge clk or negedge rst_n) begin if(!rst_n) adc_data<=8'd0; else if(cstate==WAIT_CONV) adc_data<=adc_data_r; end //将读到数字(0-255)转化成电压信号; //adc_data/ff=voltage/5; //voltage=5*adc_data/ff; //整数部分: =((adc_data<<2)+adc_data)>>8; //小数部分: =((5adc_data&&0xff)*10)>>8; wire[11:0] num_1; assign num_1={2'h0,adc_data[7:0],2'h0}+{4'h0,adc_data[7:0]}; //5adc_data; wire[11:0] num_2; assign num_2={1'h0,num_1[7:0],3'h0}+{3'h0,num_1[7:0],1'h0}; //*10; assign vol_int=num_1[11:8]; //整数部分 assign vol_dec=num_2[11:8]; //小数部分 endmodule module smg //数码管模块 ( clk, rst_n, data_1, data_2, smg_sel, smg_data ); input clk ; input rst_n; input[3:0] data_1; input[3:0] data_2; output[1:0] smg_sel; output[7:0] smg_data; parameter scan_num=16'hc350; //1ms; reg[15:0] cnt; always @(posedge clk or negedge rst_n) begin if(!rst_n) cnt<=16'd0; else if(cnt==scan_num) cnt<=16'd0; else cnt<=cnt+1'b1; end reg smg_sel_num; always @(posedge clk or negedge rst_n) begin if(!rst_n) smg_sel_num<=1'b0; else if(cnt==scan_num) smg_sel_num<=smg_sel_num+1'b1; end reg[1:0] smg_sel_r; always @(*) //数码管片选; begin case(smg_sel_num) 1'b0 : smg_sel_r=2'b01; 1'b1 : smg_sel_r=2'b10; default: smg_sel_r=2'b11; endcase end assign smg_sel=smg_sel_r; reg[3:0] smg_data_num; //数码管要显示的数; always @(*) begin case(smg_sel_num) 1'b0 : smg_data_num=data_1; 1'b1 : smg_data_num=data_2; default : smg_data_num=4'h0; endcase end reg[7:0] smg_data_r; always @(*) //是否要显示小数点 begin case(smg_sel_num) 1'b0 : smg_data_r[7]=1'b0; 1'b1 : smg_data_r[7]=1'b1; default: smg_data_r[7]=1'b1; endcase end always @ (*) begin case(smg_data_num) 4'h0 :smg_data_r[6:0]=7'b100_0000; 4'h1: smg_data_r[6:0]=7'b111_1001; 4'h2: smg_data_r[6:0]=7'b010_0100; 4'h3: smg_data_r[6:0]=7'b011_0000; 4'h4: smg_data_r[6:0]=7'b001_1001; 4'h5: smg_data_r[6:0]=7'b001_0010; 4'h6: smg_data_r[6:0]=7'b000_0010; 4'h7: smg_data_r[6:0]=7'b111_1000; 4'h8: smg_data_r[6:0]=7'b000_0000; 4'h9: smg_data_r[6:0]=7'b001_0000; default:smg_data_r[6:0]=7'b111_1111; endcase end assign smg_data=smg_data_r; endmodule
仿真波形
之前一直没怎么用modelsim来仿真,现在要开始学习怎样用modelsim来仿真了,仿真其实是一个很重要的环节,通过仿真出来的波形来修改自己写的程序,也可以增加对程序的进一步理解。
实验现象
我是测得一个5V的电压,最后在数码管上显示的是4.9V,由于电压的波动,导致的一点误差。