嵌入式系统之ADC采样

文章详细介绍了MAX197芯片在嵌入式系统中用于模拟信号采集的A/D转换过程,包括逐次逼近式、双积分式和Σ-Δ型A/D转换器的工作原理。同时,文章提供了一个在PIC32平台上使用MAX197进行多通道ADC采样的驱动程序示例,支持UCOS-II操作系统,并展示了如何通过中断和查询方式读取采样数据。
摘要由CSDN通过智能技术生成

         嵌入式系统往往会有模拟信号的采集,比如模拟传感器温度、压力、流量、速度、光强等模拟量,经过放大整形滤波电路后送给ADC芯片,将电信号转转变成离散的数字量这个过程称之为AD采样,AD采样应用广泛,普遍遵循采样率3倍于信号变化频率的法则,也就是说我们采样的时间延迟在1/3信号变化延迟即可得到完美的信号变化特征。

通常情况下,A/D转换一般要经过取样、保持、量化及编码4个过程

 

根据A/D转换器的原理可将A/D转换器分成两大类。一类是直接型A/D转换器,将输入的电压信号直接转换成数字代码,不经过中间任何变量;另一类是间接型A/D转换器,将输入的电压转变成某种中间变量(时间、频率、脉冲宽度等),然后再将这个中间量变成数字代码输出。

广泛应用的主要有三种类型:逐次逼近式A/D转换器、双积分式A/D转换器、V/F变换式A/D转换器。

    逐次逼近式(SAR)A/D转换器(SAR)的基本原理是:将待转换的模拟输入信号与一个推测信号进行比较,根据二者大小决定增大还是减小输入信号,以便向模拟输入信号逼进。推测信号由D/A转换器的输出获得,当二者相等时,向D/A转换器输入的数字信号就对应的时模拟输入量的数字量。这种A/D转换器一般速度很快,但精度实用常规精度要求。

    双积分式A/D转换器的基本原理是:先对输入模拟电压进行固定时间的积分,然后转为对标准电压的反相积分,直至积分输入返回初始值,这两个积分时间的长短正比于二者的大小,进而可以得出对应模拟电压的数字量。这种A/D转换器的转换速度较慢,但精度较高。由双积分式发展为四重积分、五重积分等多种方式,在保证转换精度的前提下提高了转换速度。

     Σ-Δ型AD由积分器、比较器、1位D/A转换器和数字滤波器等组成。原理上近似于积分型,将输入电压转换成时间(脉冲宽度)信号,用数字滤波器处理后得到数字值。电路的数字部分基本上容易单片化,因此容易做到高分辨率。主要用于音频和测量。这种转换器的转换精度极高,达到16到24位的转换精度,价格低廉,弱点是转换速度比较慢,比较适合用于对检测精度要求很高但对速度要求不是太高的检验设备。

    V/F转换器是把电压信号转换成频率信号,由良好的精度和线性,而且电路简单,对环境适应能力强,价格低廉。适用于非快速的远距离信号的A/D转换过程。

 MAX197芯片是Maxim公司推出的具有12位测量精度的高速A/D转换芯片,多量程(±10V,±5V,0~10V,0~5V)、8通道、12位高精度的A/D转换器。它采用逐次逼近工作方式,有标准的微机接口。

 

     只需单一电源供电,且转换时间很短(6us),具有8路输入通道,还提供了标准的并行接口——8位三态数据I/O口,可以和大部分单片机直接接口,使用十分方便。

    MAX197与其它A/D芯片不同之处在于它的很多硬件功能都是利用内部控制字来实现的,如通道选择、模拟信号量程、极性等。MAX197的输出数据采用无符号二进制模式(单极性输入方式)或二进制补码形式(双极性输入方式)。当CS和RD都有效时,HBEN为低电平,低8位数据被读出,HBEN为高电平,复用的高4位被读出,另外4位保持低电平(在单极性方式下),或另外4位为符号位(在双极性方式下)。

   正确进行采集转换并读取数据的前提是必须正确设置控制字以及MAX197的各种控制信号。进行数据采集转换前都对MAX197进行初始化,以便确定其采集转换的通道、量程和极性等。

程序的编写可以采用查询和中断两种方式,其中查询方式是在查询相应的标志成立时,执行读取;而中断则通过把MAX197的INT引脚连接到单片机的外部中断引脚来实现。

     以下是在pic32下采用MAX197进行多通道ADC采样的程序驱动,支持ucos-ii,可以实现自定义协议上位机实时通信在PC上展示实时的采样信号波形图,实际应用中,你还需要将数字信号转换成物理数值,这个就是典型的标定问题,以后我们接着讲。

 

 

//************************************************************
//Copyright(C)2010
//					 MAX197驱动源文件
//文件名称:MAX197DRIVER.c
//文件标识:(内参)
//摘    要:
//			1.配合头文件使用;
//			2.硬件参考作品;
//			3.
//
//当前版本:1.0
//作    者:xxd
//完成日期:2010.6.17
//
//取代版本:无
//原 作 者:无
//完成日期:无
//
//硬件说明:
//		
//			MAX197_Data ----------- PE0-7 (0-3--->adc 8-11复用)
//			MAX197_CS   ----------- PMCS2/RD10,nADCS
//			MAX197_WR   ----------- PMWR/RD4,nWR
//			MAX197_RD   ----------- PMRD/RD5,nRD
//			MAX197_HBEN ----------- ETXEN/RD6,HBEN
//			MAX197_INT  ----------- CN19/RD13,ADINT
//
//使用查询法检测AD转换完毕
//***********************************************************
#define MAX197_GLOBALES
#include "MAX197DRIVER.h"

#define ADC_INT_MASK         _PORTD_RD13_MASK
#define MAX197_INT           (_RD13)//==ADC_INT_MASK)
//#define ADC_INT_BIT_IN_IDLE  0x2000 //BIT13为1,转换结束后此位为0,如果本引脚变化此值也需要变
//读写高低位引脚 ,高时读高4位,低时读低8位
#define MAX197_HBEN_LO _LATD6=0
//片选信号,低有效
#define MAX197_CS_LO   _LATD10=0
//写信号,低有效
#define MAX197_WR_LO   _LATD4=0
//读信号,低有效
#define MAX197_RD_LO   _LATD5=0

#define MAX197_HBEN_HI _LATD6=1
#define MAX197_CS_HI   _LATD10=1
#define MAX197_WR_HI   _LATD4=1
#define MAX197_RD_HI   _LATD5=1

//ADC Data Port 8Bit out 12Bit in PE0-3 复用作为高4位
//数据端口使用PORT E低8位
//数据端口输入输出方向设置
#define ADC_PORT_OUT     TRISE=0x00
#define ADC_PORT_IN      TRISE=0xFF

#define ADC_DATA_IN      PORTE //直接读端口E
#define ADC_DATA_OUT     LATE  //写端口的寄存器

//#define USE_ADCINT       1  //采用中断读取的模式
//#define USE_ADCTIMER     1  //采用定时器中断采样 
#define PRESCALE         64
#define TOGGLES_PER_SEC  200//10ms
#define ADC_TIMER_TICK   (GetPeripheralClock()/TOGGLES_PER_SEC/PRESCALE)

//#ifdef USE_ADCINT        

#define ADCIDLE_FLAG     0x01
static ADC ADCDrv[1];
//#endif

//*******************************************
//函数名称:void InitMAX197(void)
//函数功能:MAX197初始化
//形式参数:无
//行参说明:无
//返回参数:无
//使用说明:无
//*******************************************
 

void InitMAX197(void)
{
    INT8U i,err;
    ADC *pdev=NULL;
    PORTSetPinsDigitalIn(IOPORT_D, BIT_13);
    PORTSetPinsDigitalIn(IOPORT_E, BIT_0|BIT_1|BIT_2|BIT_3|BIT_4|BIT_5|BIT_6|BIT_7);
    PORTSetPinsDigitalOut(IOPORT_D,BIT_4|BIT_5|BIT_6|BIT_10);
    MAX197_CS_HI;
    MAX197_WR_HI;
    MAX197_RD_HI;
    MAX197_HBEN_HI;
//本程序暂时采用IO查询的方式读取采样值
//  使用中断法时进行下列初始化,同时还要补充中断处理函数,这个函数并没有在该驱动中写明.
//    ADC *pdev=NULL;
    pdev=&ADCDrv[0];//只有一个ADC芯片
    for(i=0;i<MAXADCCHANNELNUM;i++)
   {
    FRING_INIT (&pdev->adcchannel[i].adcin_id, NELEMENTS(pdev->adcchannel[i].adc_data));
    pdev->adcchannel[i].nChannelID=i;
    pdev->adcchannel[i].samplerate=2;//2毫秒一次,采样频率,以1ms为单位,可以通过应用程序设置
    pdev->adcchannel[i].samplecounter=0; //采样时间间隔计数器,系统中断里更新,到达采样频率值时,触发采样函数
    pdev->adcchannel[i].sampledot=ADC_DOT; //一次物理量计算值的点数,不得大于ADC_DOT即10个点,缓冲区里有这么多数据时通知应用程序取数并计算平均值
    pdev->adcchannel[i].bStart=FALSE;

    pdev->adcchannel[i].adcin_id.data=&pdev->adcchannel[i].adc_data[0];
    pdev->adcchannel[i].semadcdataok=OSSemCreate(0);//通道间切换互斥保护
    }    
#ifdef USE_ADCINT
    CNCON =_BIT(15); //CNCONjicun                                                     /* Enable the change notice module                          */
    ConfigCNPullups(CN19_PULLUP_ENABLE);                                           /* Enable a weak pull-up corresponding to the CN pin        */
    //dummy_read = PORTD;                                                 /* Perform a dummy read to clear any mismatch conditions    */
    EnableCN19;                                                       /* Enable change notice pin 19, tied to our push button     */
    mCNClearIntFlag();      
                                            /* Clear the int flag just in case it was triggered         */
    ConfigIntCN(CHANGE_INT_ON| CHANGE_INT_PRI_3);                                               /* Enable CN interrupts at priority level 3                 */
#endif
    pdev->flagadcready=OSFlagCreate(ADCIDLE_FLAG,&err);//通道间切换互斥保护
    pdev->init = TRUE; 

#ifdef USE_ADCTIMER
     //timer2用于adc定时采样
  //Clear interrupt flag
    mT2ClearIntFlag();

    // Setup Timer 3
    OpenTimer2(T2_ON |T2_SOURCE_INT | T2_PS_1_64, ADC_TIMER_TICK);

   // set up the timer interrupt with a priority of 2
    ConfigIntTimer2(T2_INT_ON | T2_INT_PRIOR_2|T2_INT_SUB_PRIOR_1);
     mT2IntEnable(1);      
/*
   T2CON = 0x0; // Stop Timer and clear control register
   T2CONSET = 0x0020; // Set prescaler at 1:64, internal clock source
   TMR2 = 0x0; // Clear timer register
   PR2 = ADC_TIMER_TICK; // Load period register
   T2CONSET = 0x8000; // Start Timer
   mT2SetIntPriority(1);
   mT2IntEnable(1);
*/
#endif  
}
/*
//根据补码进行转换      
#if ((Vref_RANG== Vref_RANG_10_10)||(Vref_RANG== Vref_RANG_5_5))//双极性
//        2|FS|
//1LSB=-------------    
//        4096

//-FS-----+FS (-5---+5/-10---+10)   
void ADC_RAW_CONV_VOLT(INT16S adcraw,FP32 *rdata)
 {
  FP32 adc_volt=0.0f; 
  INT16S adc_raw=adcraw;
  if((adc_raw&BIT_MASK_HI(11))==BIT_MASK_HI(11))
  {
    adc_raw=adc_raw-(MANPIAN-1);
  }
  adc_volt=(FP32)((FP32)(adc_raw*ADC_REF_VCC)/(MANPIAN));
  *rdata=adc_volt;
 }
#else    //单极性
//        FS
//1LSB=-------------
//        4096
//FS(5/10)
void  ADC_RAW_CONV_VOLT(INT16S adcraw,FP32 *rdata)
 {
     *rdata=(FP32)(adcraw*ADC_REF_VCC/(MANPIAN));
}
#endif
*/
//*******************************************
//函数名称:void MAX197SampleVolteWithInterAcq(INT8U ucChannel)
//函数功能:MAX197以内部获取方式采集电压
//形式参数:INT8U ucChannel
//行参说明:输入通道号,取值范围:0~7
//返回参数:无
//使用说明:
//MAX197寄存器设置:
//      控制字格式:
//      D7(MSB)  D6     D5     D4     D3      D2     D1     D0(LSB)
//        PD1    PD0  ACQMOD   RNG    BIP     A2     A1     A0                 
//      控制字说明:
//          PD1,PD0 ----- 选择时钟和掉电模式
//           ACQMOD ----- 0:内部获取模式;1:外部获取模式
//              RNG ----- 选择满幅输入电压
//              BIP ----- 选择输入极性
//         A2,A1,A0 ----- 选择输入通道
//                            
//          PD1,PD0 ----- 00:一般模式/外部时钟
//                        01:一般模式/内部时钟
//                        10:Standby Power-Down/时钟不受影响
//                        11:Full Power-Down (FULLPD)/时钟不受影响
//                                               
//          RNG,BIP ----- 00: 0V ~  +5V
//                        01:-5V ~  +5V
//                        10: 0V ~ +10V
//                        11:-10V~ +10V
//                                                
//         A2,A1,A0 ----- 000:CH0
//                        001:CH1
//                        010:CH2
//                        011:CH3
//                        100:CH4
//                        101:CH5
//                        110:CH6
//                        111:CH7    
//*******************************************
int MAX197SampleVolteWithInterAcq(INT8U ucChannel,FP32 *sampvalue)
{
    FP32 voltvalue,voltvalue1;
    INT16U status=0,Cnt=500;
    INT16U i;
    INT8U err;
	INT16S ConverValTemp =0;
    ADC *pdev=NULL;
    OS_FLAGS adcflag;
    pdev=&ADCDrv[0];

    INT8U temp =ACQMOD_INT| Vref_RANG | ucChannel;//内部获取模式,内部时钟,输入范围0~5V外部定义
    pdev->nCurChannel=ucChannel;
	writeByteto_MAX197(temp);
    while(MAX197_INT&&Cnt--);			//等待转换结束
	ConverValTemp = ReadByteFrom_MAX197();
#ifdef CONVERT_TO_VOLT
    ADC_RAW_CONV_VOLT(ConverValTemp,sampvalue);
    voltvalue=*sampvalue;
#else
     *sampvalue=voltvalue=0;
#endif
    return ConverValTemp;  	
}

int MAX197SampleVolteWithInterAcqUseSampleRate(INT8U ucChannel,INT16S *adcRaw,FP32 *sampvalue,INT8U *adcHiByte,INT8U *adcLoByte)
{
    FP32 voltvalue,voltvalue1;
    INT16U status=0,Cnt=500;
    INT8U err,i;
	INT16S ConverValTemp =0;
    ADC *pdev=NULL;
    OS_FLAGS adcflag;
    pdev=&ADCDrv[0];

    INT8U temp =ACQMOD_INT| Vref_RANG | ucChannel;
   //采用内部采样步进计数器实现采样频率控制,采样间隔时间 
    if(pdev->adcchannel[ucChannel].bStart)//本通道如果采样启动,则进入采样时序
    {
     if(pdev->adcchannel[ucChannel].samplecounter++>=pdev->adcchannel[ucChannel].samplerate)
      {
        //采样频率到,触发采样
        pdev->adcchannel[ucChannel].samplecounter=0;
        pdev->nCurChannel=ucChannel;
	    writeByteto_MAX197(temp);
      }
      else return 0; 
    }
    else return 0;
    while(MAX197_INT&&Cnt--);			//等待转换结束
	*adcRaw=ConverValTemp = ReadByteFrom_MAX197_1(adcHiByte,adcLoByte);
#ifdef CONVERT_TO_VOLT
   // *sampvalue=voltvalue=ADC_LSB(ConverValTemp);
    ADC_RAW_CONV_VOLT(ConverValTemp,sampvalue);
    voltvalue=*sampvalue;
#else
     *sampvalue=voltvalue=0;
#endif
       //FRING_GETC(&pdev->adcchannel[pdev->nCurChannel].adcin_id,voltvalue1);
    FRING_PUTC(&pdev->adcchannel[ucChannel].adcin_id,voltvalue);
    if(FRING_COUNT(&pdev->adcchannel[ucChannel].adcin_id)>=pdev->adcchannel[ucChannel].sampledot)
      OSSemPost(pdev->adcchannel[ucChannel].semadcdataok);
    //采样数据读取完毕,设置ADC空闲标志,以进行下一次采样
//    OSFlagPost(pdev->flagadcready, ADCIDLE_FLAG,OS_FLAG_SET,&err);

    return 1;  	
}


//1 busy 0 ok
int StartADCWithInterAcq(INT8U ucChannel)
{
    INT8U err;
    ADC *pdev=NULL;
    OS_FLAGS adcflag;
    INT8U temp =ACQMOD_INT| Vref_RANG | ucChannel;//内部获取模式,内部时钟,输入范围0~5V外部定义

    pdev=&ADCDrv[0];//只有一个ADC芯片

    //if(OSSemAccpet(pdev->semadcready,0,&err);
    //adcflag = OSFlagAccept(pdev->flagadcready,                        // 调用等事件标志组函数
    //                          ADCIDLE_FLAG,
    //                          OS_FLAG_WAIT_SET_ANY + OS_FLAG_CONSUME,
    //                          &err);
    //adcflag = OSFlagAccept(pdev->flagadcready,                        // 调用等事件标志组函数
    adcflag = OSFlagPend(pdev->flagadcready,                        // 调用等事件标志组函数
                              ADCIDLE_FLAG,
                              OS_FLAG_WAIT_SET_ANY + OS_FLAG_CONSUME,
                              10,
                              &err);
    if(adcflag!=ADCIDLE_FLAG) return 1;
    //可以进行采样则清标识,启动采样
    OSFlagPost(pdev->flagadcready,                        
                              ADCIDLE_FLAG,
                              OS_FLAG_CLR,
                              &err);
    
    pdev->nCurChannel=ucChannel;
	writeByteto_MAX197(temp);
    return 0;
/*
    OSSemPend(pdev->adcchannel[ucChannel].semadcdataok,0,&err);
    //ReadADCValue
   if(FRING_COUNT(&pdev->adcchannel[ucChannel].adcin_id)>0) 
  {
    FRING_GETC(&pdev->adcchannel[ucChannel].adcin_id,value);
   
    *sampvalue=value;
  }
*/
}
//由应用程序调用,等待采样点数足够后计算
void WaitReadNumsADCComplete(INT8U ucChannel,FP32 *sampvalue,INT16U to,INT8U *perr,BOOLEAN bWaitMode)
{
    INT8U i;
    ADC * pdev=NULL;
    float MaxValue=-10.0f,MinValue=10.0f;
	float TotalValue=0.0f;
    float tempvalue=0.0f;
    pdev=&ADCDrv[0];
   if(!bWaitMode)
  {
    if(OSSemAccept(pdev->adcchannel[ucChannel].semadcdataok)==0) 
   {
      *perr=OS_ERR_TIMEOUT;
      return;
   }
   *perr=OS_ERR_NONE;
  }
   else
  {
   OSSemPend(pdev->adcchannel[ucChannel].semadcdataok,to,perr);
   if(*perr!=OS_ERR_NONE) return;
  }
    //ReadADCValue
   for(i=0;i<pdev->adcchannel[ucChannel].sampledot;i++)
   {
    if(FRING_COUNT(&pdev->adcchannel[ucChannel].adcin_id)>0) 
    {
       FRING_GETC(&pdev->adcchannel[ucChannel].adcin_id,tempvalue);  
    //*sampvalue=value;
       if( tempvalue<MinValue )
			MinValue=tempvalue;
	   if( tempvalue>MaxValue )
			MaxValue=tempvalue;
       TotalValue+=tempvalue;
    }
    else
       break;
   }
   if(i>2)
    *sampvalue=(TotalValue-MaxValue-MinValue)/(i-2);
   else   if(i>0)   *sampvalue=(TotalValue)/(i);
   else  
  {
    *sampvalue=0;
    FRING_FLUSH(&pdev->adcchannel[ucChannel].adcin_id);
    *perr=OS_ERR_TIMEOUT;
  }

}
void ReadADCValue()
{
    INT8U err; 
    ADC * pdev=NULL;
	INT16S ConverValTemp =0;
	FP32 voltvalue,voltvalue1;
    ConverValTemp = ReadByteFrom_MAX197();
    //voltvalue=ADC_LSB(ConverValTemp);
    ADC_RAW_CONV_VOLT(ConverValTemp,&voltvalue);

    pdev=&ADCDrv[0];
       //FRING_GETC(&pdev->adcchannel[pdev->nCurChannel].adcin_id,voltvalue1);
    FRING_PUTC(&pdev->adcchannel[pdev->nCurChannel].adcin_id,voltvalue);
    if(FRING_COUNT(&pdev->adcchannel[pdev->nCurChannel].adcin_id)>=pdev->adcchannel[pdev->nCurChannel].sampledot)
      OSSemPost(pdev->adcchannel[pdev->nCurChannel].semadcdataok);
    //采样数据读取完毕,设置ADC空闲标志,以进行下一次采样
    OSFlagPost(pdev->flagadcready, ADCIDLE_FLAG,OS_FLAG_SET,&err);


}
//*******************************************
//函数名称:void MAX197SampleVolteWithExterAcq(INT8U ucChannel)
//函数功能:MAX197以外部获取方式采集电压
//形式参数:INT8U ucChannel
//行参说明:输入通道号,取值范围:0~7
//返回参数:无
//使用说明:
//MAX197寄存器设置:
//      控制字格式:
//      D7(MSB)  D6     D5     D4     D3      D2     D1     D0(LSB)
//        PD1    PD0  ACQMOD   RNG    BIP     A2     A1     A0                 
//      控制字说明:
//          PD1,PD0 ----- 选择时钟和掉电模式
//           ACQMOD ----- 0:内部获取模式;1:外部获取模式
//              RNG ----- 选择满幅输入电压
//              BIP ----- 选择输入极性
//         A2,A1,A0 ----- 选择输入通道
//                            
//          PD1,PD0 ----- 00:一般模式/外部时钟
//                        01:一般模式/内部时钟
//                        10:Standby Power-Down/时钟不受影响
//                        11:Full Power-Down (FULLPD)/时钟不受影响
//                                               
//          RNG,BIP ----- 00: 0V ~  +5V
//                        01:-5V ~  +5V
//                        10: 0V ~ +10V
//                        11:-10V~ +10V
//                                                
//         A2,A1,A0 ----- 000:CH0
//                        001:CH1
//                        010:CH2
//                        011:CH3
//                        100:CH4
//                        101:CH5
//                        110:CH6
//                        111:CH7    
//*******************************************
int MAX197SampleVolteWithExterAcq(INT8U ucChannel,FP32 *sampvalue)
{
    INT16 i=0;
    INT32 ConverValTemp;
	INT8U temp = ACQMOD_EXT| Vref_RANG | ucChannel;//ACQMOD=1,外部获取模式,内部时钟,输入范围0~5V外部定义
	writeByteto_MAX197(temp);
    while(MAX197_INT);			//等待转换结束
    
	ConverValTemp = ReadByteFrom_MAX197();
#ifdef CONVERT_TO_VOLT
    //*sampvalue=ADC_LSB(ConverValTemp);
    ADC_RAW_CONV_VOLT(ConverValTemp,sampvalue);
#else
     *sampvalue=0;
#endif
    return ConverValTemp;  	
}



void writeByteto_MAX197(unsigned char Dat)
{    
 //PORTSetPinsDigitalOut(IOPORT_E, BIT_0|BIT_1|BIT_2|BIT_3|BIT_4|BIT_5|BIT_6|BIT_7);
 ADC_PORT_OUT;
 MAX197_CS_LO;
 asm("nop");
 MAX197_WR_LO;
 asm("nop");
 ADC_DATA_OUT=Dat;
 asm("nop");
 MAX197_CS_HI;
 asm("nop");
 MAX197_WR_HI; 
 ADC_PORT_IN;
}

INT16S ReadByteFrom_MAX197()
{
 INT16S ADCDat;
 INT8U hi_byte;
 INT8U low_byte;
 ADC_PORT_IN;
 ADCDat=0; 
 hi_byte=low_byte=0;
 MAX197_HBEN_HI;
 asm("nop");
 MAX197_CS_LO;
 asm("nop");
 asm("nop");
 MAX197_RD_LO;
 asm("nop");
 asm("nop");
 asm("nop");
 hi_byte = ADC_DATA_IN;
 asm("nop");
 MAX197_HBEN_LO;
 asm("nop"); 
 asm("nop"); 
 asm("nop");
 asm("nop"); 
 low_byte = ADC_DATA_IN;
 ADCDat=(hi_byte&0x0F);
 ADCDat <<= 8;
 ADCDat |= low_byte;
 MAX197_RD_HI;
 asm("nop");
 MAX197_CS_HI;
 return ADCDat;
}
INT16S ReadByteFrom_MAX197_1(INT8U *adcHiByte,INT8U *adcLoByte)
{
 INT16S ADCDat;
 INT8U hi_byte;
 INT8U low_byte;
 ADC_PORT_IN;
 ADCDat=0; 
 hi_byte=low_byte=0;
 MAX197_HBEN_HI;
 asm("nop");
 MAX197_CS_LO;
 asm("nop");
 asm("nop");
 MAX197_RD_LO;
 asm("nop");
 asm("nop");
 asm("nop");
 *adcHiByte=hi_byte = ADC_DATA_IN;
 asm("nop");
 MAX197_HBEN_LO;
 asm("nop"); 
 asm("nop");
 asm("nop");
 asm("nop");
 asm("nop");
 asm("nop");
 asm("nop");
 asm("nop");
 asm("nop");
 asm("nop");
 asm("nop"); 
 asm("nop"); 
 *adcLoByte=low_byte = ADC_DATA_IN;
 ADCDat=(hi_byte&0x0F);
 ADCDat <<= 8;
 ADCDat |= low_byte;
 MAX197_RD_HI;
 asm("nop");
 MAX197_CS_HI;
 return ADCDat;
}



//调用ADC采样触发,根据配置的采样频率,最快每一毫秒采样一次,并将结果存储在buffer,
//直到采样到足够的点数后通知应用程序取数据并计算
void    ADC_Sample() 
{
    INT8U i;
   // static int i=0;
    ADC *pdev=NULL;
    pdev=&ADCDrv[0];//只有一个ADC芯片
    for(i=0;i<MAXADCCHANNELNUM;i++)
   {
    if(pdev->adcchannel[i].bStart)//本通道如果采样启动,则进入采样时序
    {
     if(pdev->adcchannel[i].samplecounter++>=pdev->adcchannel[i].samplerate)
      {
        //采样频率到,触发采样
        pdev->adcchannel[i].samplecounter=0;
        if(StartADCWithInterAcq(i)==1)
       {
         //i--;
       }
      }
    }  
  }
}
void ADCSampleReset()
{

}

BOOLEAN GetADCChannelStatus(INT8U nChannel)
{
   ADC *pdev=NULL;
    pdev=&ADCDrv[0];//只有一个ADC芯片
  if(pdev!=NULL) 
   return (pdev->adcchannel[nChannel].bStart);
  else
   return FALSE;
}
//启动或停止某物理通道采样 ,有应用程序控制                  
void ADC_Sample_Config(INT8U nChannel,INT32U samplerate,INT8U sampledot,BOOLEAN bstart)
{
    INT8U err;
    ADC *pdev=NULL;
    pdev=&ADCDrv[0];//只有一个ADC芯片
    if(samplerate>=1)
      pdev->adcchannel[nChannel].samplerate=samplerate;
    if((sampledot>=3)&&(sampledot<=ADC_DOT))
      pdev->adcchannel[nChannel].sampledot=sampledot;
    //清空缓冲区
    FRING_FLUSH(&pdev->adcchannel[nChannel].adcin_id);
    //清信号量
    OSSemSet(pdev->adcchannel[nChannel].semadcdataok,0,&err);
    pdev->adcchannel[nChannel].bStart=bstart;
}
//暂停采样
void ADC_Pause(INT8U nChannel)
{
    ADC *pdev=NULL;
    pdev=&ADCDrv[0];//只有一个ADC芯片
    pdev->adcchannel[nChannel].bStart=FALSE;
}
//启动采样,必须先配置通道,不然将采用默认值进行采样
void ADC_Start(INT8U nChannel)
{
    INT8U err;
    ADC *pdev=NULL;
    pdev=&ADCDrv[0];//只有一个ADC芯片
    //清空缓冲区
    FRING_FLUSH(&pdev->adcchannel[nChannel].adcin_id);
    //清信号量
    OSSemSet(pdev->adcchannel[nChannel].semadcdataok,0,&err);
    pdev->adcchannel[nChannel].bStart=TRUE;
}
/*******************************************************************************
* Function Name  : TIMx_IRQHandler
* Description    : This function handles TIMx Update interrupt request.
                   
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
#ifdef USE_IRQ_MAC
void __attribute__( (interrupt(ipl1), vector(8))) BSP_TIM2ISR( void );
#endif
  void TIM2_IRQHandler(void)
  {
    mT2ClearIntFlag();
#ifdef USE_ADCTIMER
    ADC_Sample();
#endif
  }
/*
*********************************************************************************************************
*                                     BSP_CNHandler()
*
* Description: This function handles change notice interrupts.
*
* Arguments  : None
*
* Returns    : None
* 
* Notes      : Each push of the user push button will actually generate 2 interrupts, as there will actually be
*              two changes of state with the IO pin. However, since the second generation occurs as the pin returns
*              to its original state there is no simple way to reliably detect which pin generated this interrupt.
*              In this implementation only the initial change notice interrupt is serviced.
*********************************************************************************************************
*/
#ifdef USE_IRQ_MAC
void __attribute__( (interrupt(ipl1), vector(26))) BSP_CNISR( void );
#endif
void  ADC_CNHandler (void)
{    
    CPU_INT32U  reg_val;
   

    reg_val = PORTD;                                                    /* Read register to clear change notice mismatch condition  */ 
    
    if ((reg_val & ADC_INT_MASK) == 0) {
      ReadADCValue();                                                                  /* Insert your application code here                        */                                                                  /* Insert your application code here                        */
    } 
    
    mCNClearIntFlag();
}
BOOLEAN GetSampleValue(INT8U nchannel,FP32* fVoltValue)
{
      
      INT8U err,adchiByte,adcloByte;
      INT16U adcRaw;
      FP32 adc_value,adc_value1;
      if(nchannel>=MAXADCCHANNELNUM) return FALSE;
      do{
       if(GetADCChannelStatus(nchannel))
       {
        if(MAX197SampleVolteWithInterAcqUseSampleRate(nchannel,&adcRaw,&adc_value1,&adchiByte,&adcloByte))
        {
        }

       //由应用程序调用,查询采样点数足够后计算
        WaitReadNumsADCComplete(nchannel,fVoltValue,10,&err,FALSE);
        if(err==OS_ERR_NONE)
        {
        //超级终端中显示
          return TRUE;
        }
        //else   return FALSE;  
        //OSTimeDly(1);
        //DelayNS()
        BSP_Dly(100);
       } 
       else return FALSE;

      }while(1); 
 
}

以下是STC12C4052AD单片机ADC采样的C语言代码示例: ```c #include <reg52.h> #define ADC_POWER 0x80 //ADC电源控制位 #define ADC_FLAG 0x10 //ADC完成标志位 #define ADC_START 0x08 //ADC开始转换控制位 sbit ADC_CS = P3^4; //模拟量输入通道选择端口 sbit ADC_CLK = P3^5; //ADC时钟端口 sbit ADC_DIN = P3^6; //模拟量输入端口 sbit ADC_DOUT = P3^7; //ADC数据输出端口 //ADC转换函数 int ADC_Convert(unsigned char ch) { unsigned char i; unsigned int dat = 0; ADC_CS = 1; //拉高通道选择端口,准备转换 ADC_CLK = 0; //ADC时钟置低 ADC_DIN = 0; //清零模拟量输入端口 //发送起始转换命令 ADC_CS = 0; ADC_CLK = 0; ADC_DIN = 1; ADC_CLK = 1; ADC_DIN = 1; ADC_CLK = 0; //发送通道选择命令 for (i = 0; i < 3; i++) { ADC_CLK = 0; ADC_DIN = ch & 0x80; ch <<= 1; ADC_CLK = 1; } //接收ADC转换结果 for (i = 0; i < 12; i++) { ADC_CLK = 0; ADC_CLK = 1; dat <<= 1; dat |= ADC_DOUT; } ADC_CS = 1; //转换完成,拉高通道选择端口 return dat; } void main() { unsigned int adc_data; P1 = 0x00; //初始化P1口为输出 P3 = 0xff; //初始化P3口为输入 while (1) { P1 = 0x00; //将P1口清零 adc_data = ADC_Convert(0); //读取ADC转换结果 P1 = adc_data >> 4; //将ADC转换结果输出到P1口 } } ``` 该代码实现了单通道ADC采样,并将转换结果通过P1口输出。其中,ADC_Convert函数用于进行ADC转换,参数ch表示需要采样的模拟量输入通道,返回值为转换结果。在主函数中,首先进行了端口初始化,然后进入循环,不断采样并输出结果。注意,ADC转换过程中需要保证时序的正确性,因此需要仔细按照代码中的时序进行实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值