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,由于电压的波动,导致的一点误差。