Obtención de lecturas de ADC después de despertar un ATTiny85

1

Tengo este pequeño proyecto mío que implica poner un ATTiny85 en suspensión para ahorrar energía. La idea principal es:

1- Poner ATTiny85 en reposo, incluida la desactivación del ADC 2- Despertador MC en cambio de pin 3- Activando ADC 4- Lectura de la tensión desde el mismo pin que detectó el cambio

El problema es que no puedo obtener una lectura decente del ADC después de activar el MC. Parece estar en ALTO todo el tiempo.

Aquí está el código:

#include <avr/sleep.h>
#include <avr/interrupt.h>

// These constants won't change.  They're used to give names
// to the pins used:
const int analogInPin = 2;
const int LED = 3;

volatile int outputValue = 0;

void setup() {

  pinMode(LED, OUTPUT);
  pinMode(analogInPin, INPUT);

  digitalWrite(LED, LOW);
  digitalWrite(analogInPin, HIGH);

}

void MCSleep() {

  GIMSK |= _BV(PCIE);                     // Enable Pin Change Interrupts
  PCMSK |= _BV(PCINT2);                   // Use PB2 as interrupt pin
  ADCSRA &= ~_BV(ADEN);                   // ADC off
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // replaces above statement

  sleep_enable();                         // Sets the Sleep Enable bit in the MCUCR Register (SE BIT)
  sei();                                  // Enable interrupts
  sleep_cpu();                            // sleep

  cli();                                  // Disable interrupts
  PCMSK &= ~_BV(PCINT2);                  // Turn off PB2 as interrupt pin
  sleep_disable();                        // Clear SE bit
  ADCSRA |= _BV(ADEN);                    // ADC on

  sei();                                  // Enable interrupts
} // sleep

// Whatever the pin this IRS should always point to the PCINT0_vect, because is the only capable of
// providing the interrupt. The ISR must be provided otherwise, the MC just resets instead of resuming.
ISR(PCINT0_vect) {

  // This is called when the interrupt occurs, but I don't need to do anything in it
  delay(100);

}

void loop() {

  MCSleep();

  delay(100);

  outputValue = analogRead(analogInPin);

  if (outputValue > 100){
    digitalWrite(LED, HIGH); // Door is open.
  }
  else{
    digitalWrite(LED, LOW); // Door is closed.
  }

  delay(200);
}

De las especificaciones ATTiny85 parece que después de reactivar el ADC hay una conversión extendida, así que intenté agregar un retraso después de despertarme (que no funcionó) y más adelante He intentado implementar algunas otras ideas como guardar el valor de ADCSRA antes de dormir y recuperarlo después de despertarme, pero desafortunadamente, eso tampoco ha funcionado.

#include <avr/sleep.h>
#include <avr/interrupt.h>

// These constants won't change.  They're used to give names
// to the pins used:
const int analogInPin = 2;
const int LED = 3;
byte keep_ADCSRA;

volatile int outputValue = 0;

void setup() {

  pinMode(LED, OUTPUT);
  pinMode(analogInPin, INPUT);

  digitalWrite(LED, LOW);
  digitalWrite(analogInPin, HIGH);

}

void MCSleep() {

  GIMSK |= _BV(PCIE);                     // Enable Pin Change Interrupts
  PCMSK |= _BV(PCINT2);                   // Use PB2 as interrupt pin
  keep_ADCSRA = ADCSRA;
  ADCSRA = 0;
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // replaces above statement

  sleep_enable();                         // Sets the Sleep Enable bit in the MCUCR Register (SE BIT)
  sei();                                  // Enable interrupts
  sleep_cpu();                            // sleep

  cli();                                  // Disable interrupts
  PCMSK &= ~_BV(PCINT2);                  // Turn off PB2 as interrupt pin
  sleep_disable();                        // Clear SE bit
  ADCSRA = keep_ADCSRA;

  sei();                                  // Enable interrupts
} // sleep

// Whatever the pin this IRS should always point to the PCINT0_vect, because is the only capable of
// providing the interrupt. The ISR must be provided otherwise, the MC just resets instead of resuming.
ISR(PCINT0_vect) {

  // This is called when the interrupt occurs, but I don't need to do anything in it
  delay(100);

}

void loop() {

  MCSleep();

  delay(100);

  outputValue = analogRead(analogInPin);

  if (outputValue > 100){
    digitalWrite(LED, HIGH); // Door is open.
  }
  else{
    digitalWrite(LED, LOW); // Door is closed.
  }

  delay(200);
}

¿Alguna idea?

Gracias de antemano.

Aparentemente, si uso el pin 3 (PB3) en lugar de 2 (PB2) como pin de interrupción, obtengo lecturas decentes después de reactivar el ADC. ¡Todavía tengo que descubrir por qué!

    
pregunta cvicente

1 respuesta

1

Así que terminé encontrándome la respuesta en las especificaciones de ATTiny85.

Cuando estaba probando el código, descubrí que cada vez que usaba el pin 3 (PCINT3) como pin de interrupción y lectura analógica, el código realmente funcionaba. Así que empecé a profundizar un poco más los detalles de cada pin y descubrí lo siguiente:

  

Puerto B, Bit 3 - XTAL1 / CLKI / ADC3 / OC1B / PCINT3 • XTAL1: Chip Clock   Pin del oscilador 1. Se utiliza para todas las fuentes de reloj de chip excepto las internas   Oscilador RC calibrable. Cuando se usa como un pin de reloj, el pin no puede   ser utilizado como un pin de E / S. • CLKI: Entrada de reloj de un reloj externo   fuente, consulte “Reloj externo” en la página 26. • ADC3: analógico a digital   Convertidor, Canal 3 . • OC1B: Salida de comparación de salida invertida:   El pin PB3 puede servir como una salida externa para el temporizador / contador1   Compare Match B cuando se configura como una salida (conjunto DDB3). El pin OC1B   es también el pin de salida invertida para la función del temporizador del modo PWM. •   PCINT3: Fuente de interrupción de cambio de pin 3.

     

Puerto B, Bit 2 - SCK / ADC1 / T0 / USCK / SCL / INT0 / PCINT2 • SCK: reloj maestro   Salida, pin esclavo de entrada de reloj para canal SPI. Cuando el SPI está habilitado   como esclavo, este pin se configura como una entrada independientemente de la   ajuste de DDB2. Cuando el SPI está habilitado como maestro, los datos   La dirección de este pin es controlada por DDPB2. Cuando el pin es forzado   por el SPI para ser una entrada, el pull-up todavía puede ser controlado por el   PORTB2 bit. • ADC1: Convertidor analógico a digital, canal 1 . • T0:   Fuente de contador temporizador / contador0. • USCK: Modo de tres hilos Universal   Reloj de interfaz serial. • SCL: Reloj serial en modo de dos cables para USI   Modo de dos hilos. • INT0: Fuente de interrupción externa 0. • PCINT2: Pin   Cambiar la fuente de interrupción 2.

El problema fue que asumí que el número de pin era el mismo que el canal analógico, por lo que terminé siempre leyendo otra cosa.

Aquí está el código de trabajo (mientras tanto lo he grabado)

#include <avr/sleep.h>
#include <avr/interrupt.h>

const int switchPin = A1;
const int door = 3;
const int sleep = 4;

int outputValue = 0;

void setup() {

    //pinMode(switchPin, INPUT);
    //digitalWrite(switchPin, HIGH);

    pinMode(door, OUTPUT);
    digitalWrite(door, LOW);

    pinMode(sleep, OUTPUT);
    digitalWrite(sleep, HIGH);

    // Flash quick sequence so we know setup has started
    for (int k = 0; k < 10; k = k + 1) {
        if (k % 2 == 0) {
            digitalWrite(door, HIGH);
            }
        else {
            digitalWrite(door, LOW);
            }
        delay(250);
        } // for
    } // setup

void MCsleep() {

    GIMSK |= _BV(PCIE);                     // Enable Pin Change Interrupts
    PCMSK |= _BV(PCINT2);                   // Use PB2 as interrupt pin
    ADCSRA &= ~_BV(ADEN);                   // ADC off
    //digitalWrite(switchPin, LOW);
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // replaces above statement

    sleep_enable();                         // Sets the Sleep Enable bit in the MCUCR Register (SE BIT)
    sei();                                  // Enable interrupts
    sleep_cpu();                            // sleep

    cli();                                  // Disable interrupts
    PCMSK &= ~_BV(PCINT2);                  // Turn off PB2 as interrupt pin
    sleep_disable();                        // Clear SE bit
    //digitalWrite(switchPin, HIGH);
    ADCSRA |= _BV(ADEN);                    // ADC on

    sei();                                  // Enable interrupts
    } // sleep

// Whatever the pin this IRS should always point to the PCINT0_vect, because is the only capable of
// providing the interrupt. The ISR must be provided otherwise, the MC just resets instead of resuming.
ISR(PCINT0_vect) {
    // This is called when the interrupt occurs, but I don't need to do anything in it
    }

void loop() {

  MCsleep();
  delay(20);

  outputValue = analogRead(switchPin);

  if(outputValue > 200){
    digitalWrite(sleep, LOW);
    delay(1000);
    digitalWrite(door, HIGH);
    delay(2000);
    digitalWrite(sleep, HIGH);
  }
  else{
    digitalWrite(sleep, LOW);
    delay(1000);
    digitalWrite(door, LOW);
    delay(2000);
    digitalWrite(sleep, HIGH);
  }
}
    
respondido por el cvicente

Lea otras preguntas en las etiquetas