Hi.

For me it has appeared a surprise that it is impossible
to avoid start ADC at transition in a idle state (needed
to reduce power consumption). MCU is ATmega168 (and 48).
It was tryed:
  1. Disable ADC interrapt enable:  ADIE==0
  2. No clear ADC ready:  ADIE==0, ADIF==1
  3. (!) Auto Trigger Mode:  ADATE==1 (with timer source)
==> No results: ADC starts with each 'sleep' instruction.

Certainly, the disabling of interruption (ADIE=0) limits
quantity of starts, but it is no matter, when external
interruptions act in the casual manner. To stop ADC it is
impossible: it is switched off internal AREF.
   The most insulting, that in Auto Trigger mode it is
impossible to obtain a regular period.

I attach a small test of this. I shall be grateful,
if somebody will try on other ATmega. 

Thanks,
Dmitry.
/* t_ademo.c
   Is it possible to avoid an ADC start in Idle mode?
   This program has been tested with ATmega168. Test cases 1,2 and 3 give
   a negative result: ADC is started.
 */
#define ADCSRA_PS       5       /* Prescaler Selector, 5 for 4MHz/32    */
#define ADC_DIVFAC      32      /* Division factor                      */
#define ADMUX_BYTE      0x0F    /* External AREF, GND chan              */
#define BUFSIZE         100     /* Message buffer size                  */

#include <avr/io.h>
#include <avr/pgmspace.h>
#include <stdio.h>

#ifndef SMCR                    /* Sleep Control Register       */
# define SMCR   MCUCR
#endif

static char msg[BUFSIZE];

#ifndef SIG_OUTPUT_COMPARE1A
# error
#endif
__attribute__((naked))
void SIG_OUTPUT_COMPARE1A (void)
{
    asm volatile ("reti");
}

static int test (void)
{
    int tm;

    /* Initalize.       */
    TCCR1B = 1;
    ADMUX = ADMUX_BYTE;
    ADCSRA = 1<<ADEN | 1<<ADIF | ADCSRA_PS;

    /* Check ADC settings.      */
    tm = TCNT1;
    ADCSRA |= 1<<ADSC;
    do {} while (bit_is_set (ADCSRA, ADSC));
    if (bit_is_clear (ADCSRA, ADIF)) {
        snprintf (msg, sizeof(msg), "ADIF is't set");
        return __LINE__;
    }
    tm = TCNT1 - tm;
    if (tm < 25 * ADC_DIVFAC || tm > 28 * ADC_DIVFAC) {
        snprintf (msg, sizeof(msg), "Wrong conversion time: %d", tm);
        return __LINE__;
    }
    ADCSRA |= 1<<ADIF;
    if (bit_is_set (ADCSRA, ADIF)) {
        snprintf (msg, sizeof(msg), "ADIF is't clear");
        return __LINE__;
    }

    /* For timer interrupts.    */
    SMCR = 1<<SE;
    asm volatile ("sei");
    OCR1A = TCNT1;
    TIFR1 = 1<<OCF1A;
    TIMSK1 = 1<<OCIE1A;
    
    /* 1. Check ADIE==0.        */
    tm = TCNT1;
    OCR1A = tm + 5 * ADC_DIVFAC;        /* to middle of conversion      */
    asm volatile ("sleep");
    tm = TCNT1 - tm;
///    if (0)                           /* uncomment to skip test case  */
    if (bit_is_set (ADCSRA, ADSC)) {
        snprintf (msg, sizeof(msg), "ADC starts with ADIE==0");
        return __LINE__;
    }
    if (tm < 5 * ADC_DIVFAC || tm > 6 * ADC_DIVFAC) {
        snprintf (msg, sizeof(msg), "Wrong interrupt: %d", tm);
        return __LINE__;
    }
    do {} while (bit_is_set (ADCSRA, ADSC));
    
    /* 2. Check ADIF==1, ADIE==0.       */
    tm = TCNT1;
    OCR1A = tm + 5 * ADC_DIVFAC;        /* to middle of conversion      */
    asm volatile ("sleep");
    tm = TCNT1 - tm;
///    if (0)                           /* uncomment to skip test case  */
    if (bit_is_set (ADCSRA, ADSC)) {
        snprintf (msg, sizeof(msg), "ADC starts with ADIF==1, ADIE==0");
        return __LINE__;
    }
    if (tm < 5 * ADC_DIVFAC || tm > 6 * ADC_DIVFAC) {
        snprintf (msg, sizeof(msg), "Wrong interrupt: %d", tm);
        return __LINE__;
    }
    do {} while (bit_is_set (ADCSRA, ADSC));

    /* 3. Check Auto Trigger, ADIE==0.  */
    ADCSRB = 4;                         /* TCNT0 Overflow       */
    ADCSRA |= 1<<ADATE | 1<<ADIF;
    tm = TCNT1;
    OCR1A = tm + 5 * ADC_DIVFAC;        /* to middle of conversion      */
    asm volatile ("sleep");
    tm = TCNT1 - tm;
///    if (0)                           /* uncomment to skip test case  */
    if (bit_is_set (ADCSRA, ADSC)) {
        snprintf (msg, sizeof(msg), "ADC starts in ADATE case");
        return __LINE__;
    }
    if (tm < 5 * ADC_DIVFAC || tm > 6 * ADC_DIVFAC) {
        snprintf (msg, sizeof(msg), "Wrong interrupt: %d", tm);
        return __LINE__;
    }
    do {} while (bit_is_set (ADCSRA, ADSC));

    /* 4. Test of test. ADEN==0, must work!     */
    ADCSRA &= ~1<<ADEN;
    tm = TCNT1;
    OCR1A = tm + 5 * ADC_DIVFAC;        /* to middle of conversion      */
    asm volatile ("sleep");
    tm = TCNT1 - tm;
    if (bit_is_set (ADCSRA, ADSC)) {
        snprintf (msg, sizeof(msg), "ADC starts without ADEN ???");
        return __LINE__;
    }
    if (tm < 5 * ADC_DIVFAC || tm > 6 * ADC_DIVFAC) {
        snprintf (msg, sizeof(msg), "Wrong interrupt: %d", tm);
        return __LINE__;
    }

    return 0;
}

/* --------------------------------------------------------------------
   Modify below for yours system.
   Periphery (such as serial i/o) initialization -- after test run.
 */

#include "t_ademo.h"

int main ()
{
    int i;

    pio_init ();                        /* set pins (PORT*,DDR*)        */
    if (crc16_allprog ()) {
        DDRD |= 1<<PD_TXD;
        for (;;) PORTD ^= 1<<PD_TXD;
    }

    i = test ();
    
    Minit ();
    t_init ();
    asm volatile ("sei");
    if (i) {
        t_msg ("*** At line %d: %s\n", i, msg);
        return 1;
    } else {
        t_msg ("OK\n");
        return 0;
    }
}
/* -------------------- end of file ----------------------------------- */
_______________________________________________
AVR-chat mailing list
[email protected]
http://lists.nongnu.org/mailman/listinfo/avr-chat

Reply via email to