Adrian 发表于 2009-12-30 18:07:46

用mega8单片机的单路ADC(PC2)测电池电压,为什么读出的AD值始终为0?

大家帮忙看看,不知道为什么读出的AD值始终是0???


一、ADC初始化函数:
void ADC_Init(void)
{
ADCSR = 0x00; //disable adc
ACSR= 0x80;//模拟比较器禁用
ADMUX = 0xc2;//片内2.56V,右对齐,ADC2单端输入
ADCSRA= 0xcf;//ADC使能,开始转换,单次转换,中断使能,128分频。
}
二、中断函数
#pragma interrupt_handler adc_isr:iv_ADC
void adc_isr(void)
{
//conversion complete, read value (int) using...
ADC_Flag=1;
ADC_Value=(unsigned int)ADCL;            //Read 8 low bits first (important)
ADC_Value|=(unsigned int)ADCH<< 8; //read 2 high bits and shift into top byte
ADC_Value= ADC_Value/1024*256;
}
三、主函数
void main(void)
{
volatile INT32U AD_Sum=0;
unsigned int i,j;
ADC_Init();

while(1)
{
    if(ADC_Flag)//转换完成
    {
      ADC_Flag=0;//清除转换完成标志
      AD_Read=ADC_Value;
      AD_Count++;
   if(AD_Count==50)//数据满
       {
          AD_Count=0;
          choise(AD_Read, AD_Counter);//排序
          for(i=10;i<40;i++)//取中间30个
         {
            AD_Sum=AD_Sum+AD_Read;
         }
         AD_Sum=AD_Sum/30;
          if((AD_Sum*11)<=1000)//电压值小于10V
          {
              PWMB_Stop();
              PORTD=0x00;
              delay_nms(3000);
              PORTC&=~BIT(Power_Lock); //关机
              while(1);
             }
          AD_Sum=0;
          }
       ADCSRA|=0xC0;//开始新的转换
   }
}

}

rqiang 发表于 2009-12-30 18:19:12

//----------------------------------------------------------------------------------------------------
// ADC采样函数
//----------------------------------------------------------------------------------------------------
unsigned int get_ad(void)
{
        unsigned int adc_result;

        ADCSRA |= 1<<ADSC;                        //置位ADSC位,启动一次AD转换

        while(!(ADCSRA & (1 << ADIF)));        //等待

        adc_result = ADC;                                //保存ADC采样值

        return adc_result;
}
//----------------------------------------------------------------------------------------------------
// ADC滤波函数
// 连续采样202次,去掉最大值,最小值,再除以N-2
//----------------------------------------------------------------------------------------------------
unsigned intadc_filter(void)
{
        unsigned int i,j,k,l;
        unsigned int temp;
        unsigned int value_buf;
        unsigned long sum=0;
        unsigned int adc_result=0;

        for (i=0;i<N;i++)                //一次采集N个数据,放入value_buf[ ] 数组中
        {
                value_buf = get_ad();
                delay_us(5);
                delay_us(5);
                delay_us(5);
                delay_us(5);
        }

        for (j=0;j<N-1;j++)                //采样值由小到大排列,排序采样冒泡法
        {
                for (k=0;k<N-j;k++)
                {
                        if ( value_buf>value_buf )
                        {
                                temp = value_buf;
                                value_buf = value_buf;
                                value_buf = temp;
                        }
                }
        }
        for (l=1;l<N-1;l++)                //只把value_buf[ ] 数组中的1-(N-1)相加,抛弃最左,最右数据(即最大,最小值)
        {
                sum += value_buf;
        }
        adc_result=sum/(N-2);
        return (adc_result);
}

下面是主程序中采用AN0的使用:

//---------------------------------------------------------------------------------------------------- 充电电流
                        ADMUX = 0x00;                                                //选择AREF, 结果右对齐,选择ADC通道0
                        adc_result = adc_filter();
                        v0_h = (adc_result >> 8) & 0xFF;                        //ADC高位
                        v0_l = adc_result & 0xFF;                                //ADC低位
//---------------------------------------------------------------------------------------------------- 放电电流

rqiang 发表于 2009-12-30 18:20:02

上面是一个完成项目的程序,可以参考参考

Adrian 发表于 2009-12-30 20:20:29

谢谢【2楼】 rqiang :我就是不理解为什么我的不可以?ADC转换中断完成后,置位ADC_Flag标志,同时将AD结果读入一变量ADC_Value;主函数判断标志ADC_Flag,如果置位,则读取ADC_Value的值放入数组内,同时清零ADC_Flag标志、启动下一次转换。数组满后,做滤波处理。


    可能是初始化有问题,可实在看不出那里有问题?郁闷

zht9961020 发表于 2009-12-30 20:33:47

直接ADC_Value=ADC试试

fengwan 发表于 2009-12-30 20:49:03

你测的是什么电池的电压.有没有做过12V蓄电池电压测试

rqiang 发表于 2009-12-30 21:10:23

ADC初始化程序应该对的,下面是ICCAVR自动生成的AN0的初始化程序

//ADC initialize
// Conversion time: 416uS
void adc_init(void)
{
ADCSR = 0x00; //disable adc
ADMUX = 0x00; //select adc input 0
ACSR= 0x80;
ADCSR = 0xCF;
}

gaojian413 发表于 2009-12-30 21:12:41

一般有两种方法:中断法和查询法。既然你使用中断服务程序,主程序就不该查询中断标志位。可以参考清华大学出版社马潮 詹卫前 耿德根编著的《atmega8原理及应用手册》P284页程序

Adrian 发表于 2009-12-30 21:31:14

回复【7楼】gaojian413
-----------------------------------------------------------------------

我是在中断函数里置位了一标志,主函数查询的。而且我测试过,中断里直接判断ADC_Value的值就是0!!!

Adrian 发表于 2009-12-30 21:36:59

回复【5楼】fengwan
-----------------------------------------------------------------------

没有我测试的电压可以是外接的,或者12V镍氢电池。

zht9961020 发表于 2009-12-30 21:37:52

ADC_Value= ADC_Value/1024*256;
如果不要这句呢?

或者改为ADC_Value= ADC_Value/4;

是不是先除以1024之后就已经是零了

fengwan 发表于 2009-12-30 21:42:54

楼主
原理图也发上来.看看.

Adrian 发表于 2009-12-30 21:52:06

回复【10楼】zht9961020
-----------------------------------------------------------------------

汗。。。疏忽了,不出意外的话就是这个问题了,我现在去调试一下。谢谢。

Adrian 发表于 2009-12-30 22:07:37

好了,再次感谢zht9961020的提醒,疏忽了。浪费了我几乎一天的时间,怎么也找不出毛病。

Adrian 发表于 2009-12-30 22:09:42

为表感谢。我将全部代码贴出.
main.c
#include <Init.h>

unsigned int AD_Read,AD_Count=0;
enum
{
    SET             =   0x0a,
    OK            =   0x09,
    POWER         =   0x03,
};
INT8U Key,PWM_Flag=1,Run_State,ADC_Flag=0;
//volatile ADC_T Adc={0,0};
volatileINT32UADC_Value;
OCR_T Ocr1;
OCR_T OCR_Set(INT8U flag);
void ADC_Init(void)
{
ADCSR = 0x00; //disable adc
ACSR= 0x80;//模拟比较器禁用
ADMUX = 0xc2;//片内2.56V,右对齐,ADC2单端输入
ADCSRA= 0xcf;//ADC使能,开始转换,单次转换,中断使能,128分频。
}
void Init(void)
{
DDRC=0x00;
PORTC=0x00;
DDRB=0x00;
PORTB=0x00;

DDRD=0xff;//灯全部亮
PORTD=0x00;

DDRC&=~(BIT(Key_Set)|BIT(Key_Ok)|BIT(Power_Key)); //输入
DDRC|= BIT(Power_Lock)|BIT(PWM_A); //输出
PORTC|=BIT(Key_Set)|BIT(Key_Ok)|BIT(Power_Key);//开上拉
PORTC|= BIT(Power_Lock); //电平锁定
PORTC&=~BIT(PWM_A); //电平锁定

delay_nms(1000);
PORTD=0xff;

DDRB|=BIT(Motor_A)|BIT(Motor_B)|BIT(PWM_B);
PORTB&=~(BIT(Motor_A)|BIT(Motor_B)|BIT(PWM_B));

ADC_Init();
SEI();

delay_nms(1000); //LED灯长亮
PORTD=0xff;
while(!(PINC&0x08));   //等待开机按键释放
}

#pragma interrupt_handler adc_isr:iv_ADC
void adc_isr(void)
{
//conversion complete, read value (int) using...
ADC_Flag=1;
ADC_Value=(unsigned int)ADCL;            //Read 8 low bits first (important)
ADC_Value|=(unsigned int)ADCH<< 8; //read 2 high bits and shift into top byte
ADC_Value= ADC_Value*256/1024;

}
void main(void)
{
volatile INT32U AD_Sum=0;
unsigned int i,j;
Init();

while(1)
{
    if(ADC_Flag)//转换完成
    {
          ADC_Flag=0;//清除转换完成标志
      AD_Read=ADC_Value;
      AD_Count++;
          if(AD_Count==50)//数据满
          {
          AD_Count=0;
          choise(AD_Read, AD_Counter);//排序
          for(i=10;i<40;i++)//取中间30个
          {
             AD_Sum=AD_Sum+AD_Read;
          }
          AD_Sum=AD_Sum/30;
          if((AD_Sum*11)<=1000)//电压值小于10V
          {
              PWMB_Stop();
              PORTD=0x00;
              delay_nms(3000);
              PORTC&=~BIT(Power_Lock); //关机
              while(1);
             }
          AD_Sum=0;
          }
       ADCSRA|=0xC0;//开始新的转换
   }

        Key=Key_Scan();
        if(Key==SET)//按下设定键
        {
          if(Run_State==0x01)
          {
          PWMB_Stop();
          Run_State=0x02;
          }
          else
          {
      PWM_Flag++;
      if(PWM_Flag==9) {PWM_Flag=1;}
      Ocr1=OCR_Set(PWM_Flag);
          }
        }
        if(Key==OK)//按下确定键
        {
       if(Run_State!=0x01)
       {
           Run_State=0x01;
       PWMB_Start(Ocr1);
       }
        }
        if(Key==POWER)
        {
       PORTD=0x00;
       delay_nms(500);
       PORTD=0xff;
       delay_nms(500);
       PORTD=0x00;
       delay_nms(500);
       PORTC&=~BIT(Power_Lock); //关机
       while(1);//防止乱按键
        }
        LED_Train(PWM_Flag-1);
}

}

OCR_T OCR_Set(INT8U flag)
{
static OCR_T Ocr_Temp;
switch (flag)
{
   case 1: //    1/1
    {
      Ocr_Temp.b=TOP/1;
      Ocr_Temp.a=Ocr_Temp.b*1;
    }break;
       case 2: //    1/2
    {
      Ocr_Temp.b=TOP/2;
      Ocr_Temp.a=Ocr_Temp.b*2;
    }break;
       case 3: //    1/4
    {
      Ocr_Temp.b=TOP/4;
      Ocr_Temp.a=Ocr_Temp.b*4;
    }break;
       case 4: //    1/8
    {
      Ocr_Temp.b=TOP/8;
      Ocr_Temp.a=Ocr_Temp.b*8;
    }break;
       case 5: //    1/10
    {
      Ocr_Temp.b=TOP/10;
      Ocr_Temp.a=Ocr_Temp.b*10;
    }break;
       case 6: //    1/20
    {
      Ocr_Temp.b=TOP/20;
      Ocr_Temp.a=Ocr_Temp.b*20;
    }break;
       case 7: //    1/40
    {
      Ocr_Temp.b=TOP/40;
      Ocr_Temp.a=Ocr_Temp.b*40;
    }break;
       case 8: //    1/50
    {
      Ocr_Temp.b=TOP/50;
      Ocr_Temp.a=Ocr_Temp.b*50;
    }break;
    default :break;
}
return Ocr_Temp;
}

Adrian 发表于 2009-12-30 22:11:27

Init.c   //为方便好多函数都在main.c内编写了

#define INIT_C
#include <Init.h>

void port_init(void)
{
PORTB = 0x00;
DDRB= 0xff;
PORTC = 0x00; //m103 output only
DDRC= 0x00;
PORTD = 0x00;
DDRD= 0xff;
}
void Key_Init(void)
{
KEY_PORT =0xff;
KEY_DDR=0xf0;

}
//TIMER1 initialize - prescale:64
// WGM: 15) PWM fast, TOP=OCRnA0x30d3
// desired value: 10Hz
// actual value: 10.000Hz (0.0%)
void timer1_init(void)
{
TCCR1B = 0x00; //stop
TCNT1=0;
OCR1A=12499;
OCR1B=6000;
//TCCR1A = 0x23;
//TCCR1B = 0x1b; //start Timer
}
void PWMB_Start(OCR_T Ocr)
{
TCCR1B = 0x00; //stop
TCNT1=0;
OCR1A=Ocr.a;
OCR1B=Ocr.b;
TCCR1A = 0x23;
TCCR1B = 0x1b; //start Timer
}
void PWMB_Stop(void)
{
TCCR1A = 0x00;
TCCR1B = 0x00; //start Timer
}
void PWMA_Start(void)
{
DDRC |=BIT(PWM_A);
PORTC&=~BIT(PWM_A);
}
void PWMA_Stop(void)
{
DDRC |=BIT(PWM_A);
PORTC|=BIT(PWM_A);
}
void MotorA_Start(void)
{
DDRB |=BIT(Motor_A);
PORTB&=~BIT(Motor_A);

}
void MotorA_Stop(void)
{
DDRB |=BIT(Motor_A);
PORTB|=BIT(Motor_A);
}
void MotorB_Start(void)
{
DDRB |=BIT(Motor_B);
PORTB&=~BIT(Motor_B);
}
void MotorB_Stop(void)
{
DDRB |=BIT(Motor_B);
PORTB|=BIT(Motor_B);
}


//call this routine to initialize all peripherals
void init_devices(void)
{
//stop errant interrupts until set up
CLI(); //disable all interrupts
port_init();
timer1_init();
//ADC_Init();
MCUCR = 0x00;
GICR= 0x00;
TIMSK = 0x00; //timer interrupt sources
SEI(); //re-enable interrupts
//all peripherals are now initialized
}
uchar KEY_Pressed(void)
{
        uchar key;
        //Key_Init();
        key=KEY_PIN&0x0b;
       
        if(key==0x0b)
          return 0;
        else
          return 1;
}
INT8U Key_Scan(void)
{
//void *ptr=&flag;
    uchar oldkey,newkey;
if((KEY_Pressed())==1)//有按键
   {
    oldkey=KEY_PIN&0x0b;
    }
    else {return 0;}
    delay_nms(50);
    newkey=KEY_PIN&0x0b;
    if(oldkey==newkey) //确认按键
    {
          while((PINC&0x0B)!=0x0B);//等待按键释放
   return newkey;
    }
    else   {return 0;}
}
void LED_Train(INT8U flag)
{
DDRD=0xff;//输出
PORTD=~BIT(flag);

}
void delay_1us(void)               //1us延时函数
{
   asm("nop");
}

void delay_nus(unsigned int n)       //N us延时函数
{
   unsigned int i=0;
   for (i=0;i<n;i++)
   delay_1us();
}

void delay_1ms(void)               //1ms延时函数
{
   unsigned int i;
   for (i=0;i<(unsigned int)(XTAL*143-2);i++);
}

void delay_nms(unsigned int n)       //N ms延时函数
{
   unsigned int i=0;
   for (i=0;i<n;i++)
   {
      delay_1ms();
   }   
}

void delay(long int x)
{
    uchar j;
        while((x--)!=0)
        {
          for(j=0;j<125;j++)
                {
                  ;
                }
        }
}

void choise(unsignedint *a,unsigned int n) //从小到大排序
{
unsigned int i,j,k,temp;
   for(i=0;i<n-1;i++)
    {
      k=i; //*给记号赋值
      for(j=i+1;j<n;j++)
      if(a>a) k=j; //*是k总是指向最小元素
      if(i!=k)//*当k!=i是才交换,否则a 即为最小
       {
          temp=a;
          a=a;
          a=temp;
       }
   }

}

Adrian 发表于 2009-12-30 22:12:02

Init.h

#ifndef INIT_H

#define INIT_H
#include <iom8v.h>
#include <macros.h>
//-------------------------------------宏定义---------------------------------------
#define XTAL 8
#define TOP             0x30d3    //12499
#define uchar         unsigned char
#define uint            unsigned int
#define cchar         const unsigned char
typedef unsigned char    INT8U;
typedef signed   char    INT8S;
typedef unsigned int   INT16U;
typedef signed   int   INT16S;
typedef unsigned long    INT32U;
typedef signed   long    INT32S;
typedef float            FP32;
typedef double         FP64;
typedef enum{false = 0, true = 1}BOOLEAN;
typedef enum
{
Key_Set=0x01,
Key_Start=0x02,
Key_Power=0x08,
}Key_T;
typedef struct
{
volatile INT16U a;
volatile INT16U b;
} OCR_T;
typedef struct
{
volatile INT8U ADC_Flag;
volatile INT16U ADC_Value;
} ADC_T;
#define KEY_PORT   PORTC
#define KEY_DDR    DDRC
#define KEY_PIN    PINC



#define LED_PORT   PORTD
#define LED_DDR    DDRD
#define LED_PIN    PIND


#define AD_Counter50
#define Power_LockPC4//电平锁定
#define Power_Key   PC3//开关机检测
#define Power_ADC   PC2//电池电压检测
#define Key_Ok      PC1//确定
#define Key_Set   PC0//设置

#define PWM_B       PB2//配气
#define PWM_A       PC5//气路切换
#define Motor_A   PB1
#define Motor_B   PB0





//------------------------------------函数声明区------------------------------------------
void port_init(void);
void init_devices(void);
void PWMB_Start(OCR_T Ocr);
void PWMB_Stop(void);

uchar KEY_Pressed(void);
INT8U Key_Scan(void);
void LED_Train(INT8U flag);
void delay_1us(void);               //1us延时函数
void delay_nus(unsigned int n);       //N us延时函数
void delay_1ms(void);               //1ms延时函数
void delay_nms(unsigned int n);       //N ms延时函数
void ADC_Init(void);
void choise(unsignedint *a,unsigned int n) ;


#else

#endif

Adrian 发表于 2009-12-30 22:16:56

是正在做的一个小项目,基本完成。代码有点乱,还没有整理。简单修改一下,OCR1B口就可输出任意精确占空比的PWM波形。比较简单O(∩_∩)O~

xingcai 发表于 2009-12-30 22:20:06

MARK

pulan 发表于 2009-12-30 22:25:14

留个记号

Adrian 发表于 2009-12-30 22:33:49

呵呵没有液晶显示,调起来不方便。反复测试if((AD_Sum*11)<=1187)这句,最后测量的电压为11.88V,实测12.34V,分压电阻用的是102和103的,实际102:(102+103)并不是正好11.结果还可以接受。

算了,不测了。给AD_Sum*11乘个系数k=12.34/11.88。应该好了。O(∩_∩)O~

Adrian 发表于 2009-12-30 22:38:32

经测试,AD测量电压值在12.30到12.40V之间。OK了。

liu969610245 发表于 2009-12-30 23:14:53

呵呵呵,刚要说,以前也犯过~~

litteworm 发表于 2010-1-24 00:13:14

留个爪子

yzeng 发表于 2010-8-23 17:33:18

mark

sjiajie 发表于 2010-10-8 22:48:14

最近正好在ADC,看了这贴受益匪浅,留个记号

joycat 发表于 2010-10-9 14:07:08

哇~~~刚好要弄ADC,参考了~~

kongethan 发表于 2011-7-8 13:13:20

mark

yest 发表于 2013-1-8 14:48:25

做个记号

skyxjh 发表于 2013-1-8 20:35:14

ADC_Value= ADC_Value*256/1024;
这条语句还用这样写吗?写成下面的语句不就结了!
ADC_Value >>= 2;
页: [1]
查看完整版本: 用mega8单片机的单路ADC(PC2)测电池电压,为什么读出的AD值始终为0?