打印
[AVR单片机]

求助高手~~关于MEGA16 AD自动转换的问题

[复制链接]
7296|8
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
perfugee|  楼主 | 2009-12-4 18:35 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
求助高手解疑~~

我现在用mega16 做AD 转换,设置 AD自动转换模式,由定时器0比较匹配溢出中断来触发AD转换,现在的问题是程序编译好后,在AVRSTUDIO的环境里软件仿真,可以进入定时器中断服务函数,但是不能自动触发AD转换,必须在服务函数里再次开启AD转换才可以。请教高人,难道AD的自动转换模式我理解有误么? 可是这样的话,如果直接用定时器溢出去开启AD转换不就是间接地自动转换么?那么自动转化的意义何在?

可能我描述的不太清楚。。但是真的真的 想请教高手解答,AD与定时器的自动触发到底是怎么实现的? 谢谢~~不生感激。。

附上我的程序~
#include <avr/io.h>
#include <stdint.h>
#include <avr/interrupt.h>
#define uint8 unsigned char
#define uint16 unsigned int
//数字滤波
#define a 0xF4  // a=0.95
#define b 0x0D  // b=1-a=0.05
//多路转换
volatile uint8 ADC_Value[]={0x00};
//通道选择
const uint8 ADChannel[32] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E, 0x0F,
                           0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F};
/******************************常量定义********************************
单端通道,不放大
0x00 ADC0
0x01 ADC1
0x02 ADC2
0x03 ADC3
0x04 ADC4
0x05 ADC5
0x06 ADC6
0x07 ADC7
差分通道ADC0作负端,10/200倍放大
0x08 ADC0+ ADC0-, 10倍放大,校准用
0x09 ADC1+ ADC0-, 10倍放大
0x0A ADC0+ ADC0-,200倍放大,校准用
0x0B ADC1+ ADC0-,200倍放大
差分通道ADC2作负端,10/200倍放大
0x0C ADC2+ ADC2-, 10倍放大,校准用
0x0D ADC3+ ADC2-, 10倍放大
0x0E ADC2+ ADC2-,200倍放大,校准用
0x0F ADC3+ ADC2-,200倍放大
差分通道ADC1作负端,不放大
0x10 ADC0+ ADC1-
0x11 ADC1+ ADC1-,校准用
0x12 ADC2+ ADC1-
0x13 ADC3+ ADC1-
0x14 ADC4+ ADC1-
0x15 ADC5+ ADC1-
0x16 ADC6+ ADC1-
0x17 ADC7+ ADC1-
差分通道ADC2作负端,不放大
0x18 ADC0+ ADC2-
0x19 ADC1+ ADC2-
0x1A ADC2+ ADC2-,校准用
0x1B ADC3+ ADC2-
0x1C ADC4+ ADC2-
0x1D ADC5+ ADC2-
单端通道,不放大
0x1E VBG 内部能隙1.22V电压基准,校准用
0x1F 接地 校准用
*********************************************************************/
const uint8 ADEnStatus[4] = {0xFE,0xFC,0xF8,0xF0};//设置使用AD转换占用的IO的通道
/*
  0xFE  使用 AD0               AdDoor=0
  0xFC  使用 AD0 AD1           AdDoor=1
  0xF8  使用 AD0 AD1 AD2       AdDoor=2
  0xF0  使用 AD0 AD1 AD2 AD3   AdDoor=3
*/
uint8 AdDoor;   //AD转换IO通道选择
extern volatile uint8 Key;  //用来设置AD转换通道
volatile uint8 AdChoose;   //AD端口选择
uint16 AdcVal; //ADC转换值
uint16 AdcValConvert; //经过数字滤波之后的转换值

/*--------------------------------------------------------------------
程序名称:定时器0中断服务程序
程序功能:
注意事项:
提示说明:
输入:
返回:(14.7456Mhz/1024)=35.556ms
--------------------------------------------------------------------*/
ISR(TIMER0_COMP_vect)
{
    OCR0 = 0x91; //重载计数数值
ADCSRA |= (1<<ADSC);//ADSC: AD start conversion
}
/*--------------------------------------------------------------------
程序名称:AD转换中断服务程序
程序功能:
注意事项:
提示说明:
输入:
返回:
--------------------------------------------------------------------*/
ISR(ADC_vect)    //进入本中断,则软件自动屏蔽其他中断
{
    volatile uint8 Key=AdDoor+1;
    ADCSRA |= (1<<ADIF);//ADIF置1清位
//信号采集
    AdcVal = ADCH&0xFF;
//数字滤波
    AdcValConvert=(uint8)((uint16)a*AdcValConvert + (uint16)b*AdcVal);
ADC_Value[AdChoose]=AdcValConvert;
//这是多路采集的程序
AdChoose++;
if(AdChoose==Key) AdChoose=0;      
ADMUX = (1<<REFS0)|(1<<ADLAR)|(ADChannel[AdChoose]);//使用 AVcc +5v 作为ADC参考电源
   
}
/*--------------------------------------------------------------------
程序名称:AD转换初始化程序
程序功能:
注意事项:
提示说明:
输入:
返回:
--------------------------------------------------------------------*/
void ADC_init()
{
/* 设置对应的IO口为输入高阻态 */
    DDRA &= ADEnStatus[AdDoor];
    PORTA &= ADEnStatus[AdDoor];
    ADCSRA = 0x00;//disable ADC
    ADMUX = (1<<REFS0)|(1<<ADLAR)|(0x00);//初始化时默认单通道,不放大,开启ADC0
/*
  ADMUX (ADC Multiplexer Select Register)
             bit7  bit6  
             REFS1 REFS0  参考电压选择  
              0     0     AREF,内部Vref关闭  
              0     1     AVCC,AREF引脚外加滤波电容  
              1     0     保留  
              1     1     2.56V的片内基准电压源,AREF引脚外加滤波电容  
             bit5         ADC结果左对齐选择 1=左对齐  0=右对齐
             bit4~0       选择AD通道   
*/
     ACSR = (1<<ACD);//close analog comparator 关闭模拟比较器

     ADCSRA=(1<<ADEN)|(1<<ADATE)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
/*   
               ADCSRA (ADC Control and Status Register A)
                bit7   ADEN ADC使能=1
                bit6   ADSC 启动ADC开始转换=1
                bit5   ADATE 自己触发使能
                bit4   ADIF ADC中断标志
                bit3   ADIE ADC中断使能
                bit2~0 ADPS0~2   ADC 预分频选择位 111=128时钟分频
*/  
    SFIOR=(1<<ADTS1)|(1<<ADTS0);
/*
  SFIOR (Special Function IO Register)   自动触发源选择
             bit7  bit6   bit5
             ADTS2 ADTS1  ADTS0
              0     0       0        连续转化模式
     0     0       1        模拟比较器
     0     1       0        外部中断请求0
     0     1       1        定时器/计数器0比较匹配
     1     0       0        定时器/计数器0溢出
     1     0       1        定时器/计数器比较匹配B
     1     1       0        定时器/计数器1溢出
     1     1       1        定时器/计数器1捕捉事件
*/
}
/*--------------------------------------------------------------------
程序名称:void Sys_Init(void)
程序功能:系统初始化函数
注意事项:
提示说明:
输入:
返回:
--------------------------------------------------------------------*/
void Sys_Init(void)
{
    cli();          //关总中断
PORTA = 0;
DDRA = 0x00; //设置PA
PORTB = 0;
DDRB = 0x00; //设置PB
PORTC = 0;
DDRC = 0;     //设置PC  
PORTD = 0;  
DDRD = 0x00;    //设置PD
MCUCR = 0x00;  //全局MCU控制寄存器 电源管理项
GICR  = 0x00;  //全局向量配置寄存器 boot 选项

TCCR0 = (1<<WGM01)|(1<<CS02)|(1<<CS00);//start timer 1024分频 CTC比较输出
/*
*/
    TCNT0= 0x01;
OCR0 = 0x91;
  
    TIMSK = (1<<OCIE0); //开定时器T1比较匹配中断
}
/*--------------------------------------------------------------------
程序名称:main()主函数
程序功能:
注意事项:
提示说明:
输入:
返回:
--------------------------------------------------------------------*/
int main(void)
{
  AdDoor = 1; //使用AD0 AD1,开放其对应的IO口
  Sys_Init();
  ADC_init();  
  
  sei();  //开放全局中断
  while(1)
  {
  }
}

相关帖子

沙发
suoma| | 2009-12-4 20:24 | 只看该作者
这里有一个编译通过的AD转换的实例,可参考下!

AD中断采集完成后切换通道,这样AD触发用定时器触发或者连续采集模式都可以使用。

采样结果用一个数组存起来。
//注意,得到的AD值并非实际的电压,而是0~1024(10位)或0~255(8位)。
#pragma interrupt_handler adc_isr:iv_ADC
void adc_isr(void)
{
//conversion complete, read value (int) using...
#if AD_JINGDU ==10
  advalue=ADCL;         //Read 8 low bits first (important)
  advalue|=(int)ADCH << 8; //read 2 high bits and shift into top byte
#endif
#if AD_JINGDU ==8
  advalue = ADCH;
#endif
if(SET_ADC==3) SET_ADC=0; //这是多路采集的一个思路
SET_ADC++;
AD_done=1;   //转换完成标识位
}

使用特权

评论回复
板凳
suoma| | 2009-12-4 20:25 | 只看该作者
#include "config.h"
volatile uint8 ADC_Value[]={0x00,0x00,0x00};

void main(void)
{
adc_init(SET_ADC); //启动AD,ADC0(PA0)输入
DDRB = 0xff;

while(1)
{
if(AD_done==1) //AD转换完成标志位
{
AD_done=0;
advalue=(advalue*5000)/1024;//AVCC的实际测得电压乘以1000代替5000,结果转化成了mv,后面的1024取决于 #define AD_JINGDU 10,如果使用8位精度,则是256
ADC_Value[SET_ADC]=advalue; //SET_ADC从0到2变化,采集三路电压值,存到 ADC_Value[0],ADC_Value[1],ADC_Value[2],具体的存放,你自己完成吧
//PORTB = advalue; //AD采样结果将在中断中存入advalue
//show(ADget_vol();) //显示浮点的实际电压值
}

}
}

AD转换测试程序,在硬件上测试通过。
//ICC-AVR application builder : 2007-7-25 10:40:22
// Target : M16
// Crystal: 7.3728Mhz

#include <iom16v.h>
#include <macros.h>

#define AD_JINGDU 10

volatile unsigned int value=0;

void port_init(void)
{
PORTA = 0x00;
DDRA = 0x00;
PORTB = 0x00;
DDRB = 0xFF;
PORTC = 0x00; //m103 output only
DDRC = 0x00;
PORTD = 0x00;
DDRD = 0x00;
}

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

void adc_single_init(unsigned char adc_input)
{
ADCSR = 0x00; //disable adc
#if AD_JINGDU ==10
ADMUX=0xC0|(1<<ADLAR)|adc_input; //2.56V 的片内基准电压源, AREF 引脚外加滤波电容,左对齐,8位精度,AD输入端ADC0
#endif
#if AD_JINGDU == 8
ADMUX=0xC0|adc_input;
#endif
//ADMUX=0x40|adc_input; //AVCC, AREF 引脚外加滤波电容
//ADMUX=adc_input; //AREF, 内部Vref 关闭, AREF 引脚外加滤波电容
ACSR |= 0x80;     //禁用模拟比较器
ADCSR = (1<<ADEN)|(1<<ADSC)|(1<<ADATE)|(1<<ADIE)|0x06; //允许AD,启动转换,自动触发使能,中断使能,64分频
SFIOR &=~ 0xe0;//自动中断源选择,连续转换模式
}

#pragma interrupt_handler adc_isr:iv_ADC
void adc_isr(void)
{
//conversion complete, read value (int) using...
#if AD_JINGDU ==10
  value=ADCL;         //Read 8 low bits first (important)
  value|=(int)ADCH << 8; //read 2 high bits and shift into top byte
#endif
#if AD_JINGDU ==8
  value = ADCH;
#endif
}

//call this routine to initialize all peripherals
void init_devices(void)
{
//stop errant interrupts until set up
CLI(); //disable all interrupts
port_init();
adc_single_init(0);

MCUCR = 0x00;
GICR = 0x00;
TIMSK = 0x00; //timer interrupt sources
SEI(); //re-enable interrupts
//all peripherals are now initialized
}

void main(void)
{
init_devices();
while(1)
{
PORTB = value;
}
}

使用特权

评论回复
地板
perfugee|  楼主 | 2009-12-5 07:45 | 只看该作者
谢谢LS....这个例子我在ICCAVR论坛上好像看见过,我看到他的AD转换是工作在连续转换模式下。。我的程序中AD连续转换是工作在定时器比较匹配中断,咋软件模拟仿真的环境里,如果定时器中断服务函数里没有ADCSRA |= (1<<ADSC);这句就无法进入AD中断...不知道AD的连续工作模式能这么理解么?

ISR(TIMER0_COMP_vect)
{
    OCR0 = 0x91; //重载计数数值
ADCSRA |= (1<<ADSC);//ADSC: AD start conversion
}

使用特权

评论回复
5
love_life| | 2009-12-5 22:02 | 只看该作者
好东西,收藏了

使用特权

评论回复
6
wangwo| | 2009-12-6 18:54 | 只看该作者
辛苦楼主了

使用特权

评论回复
7
XZL| | 2010-2-26 09:28 | 只看该作者
连续转换是自动的,无需触发。
不过AVR不能实现自动顺序转换,是一件遗憾的事情

使用特权

评论回复
8
Karlshen| | 2010-2-28 23:37 | 只看该作者
无法进入AD中断是肯定的

使用特权

评论回复
9
XZL| | 2013-4-20 19:32 | 只看该作者
2010年的问题了!我记得连续方式是ADC一直在工作,转换完一次直接启动下一次ADC

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

5

主题

13

帖子

1

粉丝