搜索
bottom↓
回复: 29

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

[复制链接]

出0入0汤圆

发表于 2009-12-30 18:07:46 | 显示全部楼层 |阅读模式
大家帮忙看看,不知道为什么读出的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[AD_Count]=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;  //开始新的转换
   }
  }
  
}

阿莫论坛20周年了!感谢大家的支持与爱护!!

知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)

出0入0汤圆

发表于 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 int  adc_filter(void)
{
        unsigned int i,j,k,l;
        unsigned int temp;
        unsigned int value_buf[N];
        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[k]>value_buf[k+1] )
                        {
                                temp = value_buf[k];
                                value_buf[k] = value_buf[k+1];
                                value_buf[k+1] = temp;
                        }
                }
        }
        for (l=1;l<N-1;l++)                //只把value_buf[ ] 数组中的1-(N-1)相加,抛弃最左,最右数据(即最大,最小值)
        {
                sum += value_buf[l];
        }
        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低位
//---------------------------------------------------------------------------------------------------- 放电电流

出0入0汤圆

发表于 2009-12-30 18:20:02 | 显示全部楼层
上面是一个完成项目的程序,可以参考参考

出0入0汤圆

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


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

出0入0汤圆

发表于 2009-12-30 20:33:47 | 显示全部楼层
直接ADC_Value=ADC试试

出0入0汤圆

发表于 2009-12-30 20:49:03 | 显示全部楼层
你测的是什么电池的电压.有没有做过12V蓄电池电压测试

出0入0汤圆

发表于 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;
}

出0入0汤圆

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

出0入0汤圆

 楼主| 发表于 2009-12-30 21:31:14 | 显示全部楼层
回复【7楼】gaojian413
-----------------------------------------------------------------------

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

出0入0汤圆

 楼主| 发表于 2009-12-30 21:36:59 | 显示全部楼层
回复【5楼】fengwan
-----------------------------------------------------------------------

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

出0入0汤圆

发表于 2009-12-30 21:37:52 | 显示全部楼层
ADC_Value= ADC_Value/1024*256;
如果不要这句呢?

或者改为ADC_Value= ADC_Value/4;

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

出0入0汤圆

发表于 2009-12-30 21:42:54 | 显示全部楼层
楼主
原理图也发上来.看看.

出0入0汤圆

 楼主| 发表于 2009-12-30 21:52:06 | 显示全部楼层
回复【10楼】zht9961020
-----------------------------------------------------------------------

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

出0入0汤圆

 楼主| 发表于 2009-12-30 22:07:37 | 显示全部楼层
好了,再次感谢zht9961020的提醒,疏忽了。浪费了我几乎一天的时间,怎么也找不出毛病。

出0入0汤圆

 楼主| 发表于 2009-12-30 22:09:42 | 显示全部楼层
为表感谢。我将全部代码贴出.
main.c
#include <Init.h>

unsigned int AD_Read[AD_Counter],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};
volatile  INT32U  ADC_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[AD_Count]=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;
}

出0入0汤圆

 楼主| 发表于 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=OCRnA  0x30d3
// 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(unsigned  int *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[k]>a[j ]) k=j; //*是k总是指向最小元素
      if(i!=k)  //*当k!=i是才交换,否则a[i ] 即为最小
       {
          temp=a[i ];
          a[i ]=a[k];
          a[k]=temp;
       }
     }

}

出0入0汤圆

 楼主| 发表于 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_Counter  50
#define Power_Lock  PC4  //电平锁定
#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(unsigned  int *a,unsigned int n) ;


#else

#endif

出0入0汤圆

 楼主| 发表于 2009-12-30 22:16:56 | 显示全部楼层
是正在做的一个小项目,基本完成。代码有点乱,还没有整理。简单修改一下,OCR1B口就可输出任意精确占空比的PWM波形。比较简单O(∩_∩)O~

出0入0汤圆

发表于 2009-12-30 22:20:06 | 显示全部楼层
MARK

出0入137汤圆

发表于 2009-12-30 22:25:14 | 显示全部楼层
留个记号

出0入0汤圆

 楼主| 发表于 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~

出0入0汤圆

 楼主| 发表于 2009-12-30 22:38:32 | 显示全部楼层
经测试,AD测量电压值在12.30到12.40V之间。OK了。

出0入0汤圆

发表于 2009-12-30 23:14:53 | 显示全部楼层
呵呵呵,刚要说,以前也犯过~~

出0入0汤圆

发表于 2010-1-24 00:13:14 | 显示全部楼层
留个爪子

出0入0汤圆

发表于 2010-8-23 17:33:18 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-10-8 22:48:14 | 显示全部楼层
最近正好在ADC,看了这贴受益匪浅,留个记号

出0入0汤圆

发表于 2010-10-9 14:07:08 | 显示全部楼层
哇~~~刚好要弄ADC,参考了~~

出0入0汤圆

发表于 2011-7-8 13:13:20 | 显示全部楼层
mark

出0入0汤圆

发表于 2013-1-8 14:48:25 | 显示全部楼层
做个记号

出0入0汤圆

发表于 2013-1-8 20:35:14 | 显示全部楼层
ADC_Value= ADC_Value*256/1024;
这条语句还用这样写吗?写成下面的语句不就结了!
ADC_Value >>= 2;
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-7-24 06:24

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表