La interrupción de ADC de ejecución libre ATMega328P no se dispara

Estoy jugando con un ATMega328P que funciona con un USBtiny 1.0 y tengo la siguiente configuración:

  • Potenciómetro a ADC0.
  • Fuente de corriente PB0 para un LED.

Luego, mi programa refleja ADC0 en PWM en PB0, por lo que la posición del potenciómetro controla directamente el brillo del LED.

Esto funciona cuando se hace dentro del ciclo infinito al final de main(), pero si lo muevo a la interrupción ADC, parece que no pasa nada (vea el comentario "Eliminarme").

Estoy basándome parcialmente en un tutorial en http://www.avr-tutorials.com/adc/utilizing-avr-adc-interrupt-feature , pero usando el modo de ejecución libre.

#define __AVR_ATmega328P__
#define F_CPU 1000000UL
#include <avr/io.h>
#include <avr/interrupt.h>

#define SET(register, bit) register |= _BV(bit)
#define CLEAR(register, bit) register &= ~_BV(bit)

int main(void) {
    // Set up ADC0 as a 8-bit potentiometer input.
    // -------------------------------------------------------------------------

    // Switch from 10-bit to 8-bit mode.
    // Our output is 8-bit anyway.
    SET(ADMUX, ADLAR);

    // Use AVCC as the reference for the ADC.
    SET(ADMUX, REFS0);
    CLEAR(ADMUX, REFS1);

    // Specify that we are only interested in ADC0.
    CLEAR(ADMUX, MUX0);
    CLEAR(ADMUX, MUX1);
    CLEAR(ADMUX, MUX2);
    CLEAR(ADMUX, MUX3);

    // Set the clock divider to 128x.
    // TODO: Experiment with reducing the divider when logic is in.
    SET(ADCSRA, ADPS0);
    SET(ADCSRA, ADPS1);
    SET(ADCSRA, ADPS2);

    // Set to free-running mode; the ADC will run continuously.
    CLEAR(ADCSRB, ADTS0);
    CLEAR(ADCSRB, ADTS1);
    CLEAR(ADCSRB, ADTS2);

    // Enable auto-triggering of the ADC by the above.
    SET(ADCSRA, ADATE);

    // Fire the ADC interrupt when a sample is ready.
    SET(ADCSRA, ADIE);

    // Enable the ADC now it has been configured.
    SET(ADCSRA, ADEN);

    // Start the free-running timer.
    SET(ADCSRA, ADSC);

    // Set up LED output on PB1.
    // -------------------------------------------------------------------------

    // Set the pin up as an output.
    SET(DDRB, PB1);

    // We'll be comparing a timer (sawtooth) to a value (OCR1A);
    // the pin will be high or low depending upon whether the timer is greater
    // or lower than the value (OCR1A).
    SET(TCCR1A, COM1A1);

    // Switch to fast PWM.
    // This doubles the frequency, but means that phase shifts when OCR1A is
    // changed.  Doesn't matter for us.
    SET(TCCR1A, WGM10);
    SET(TCCR1B, WGM12);

    // Disable the timer divider; as fast as possible.
    SET(TCCR1B, CS10);

    // Enable interrupts now everything is configured.
    // -------------------------------------------------------------------------
    sei();

    while (1) {
        // Remove me to have the ADC interrupt set the LED brightness instead.
        OCR1A = ADCH;
    }
}

// This ADC interrupt handler is called every time a potentiometer sample is ready.
ISR(ADC_vect) {
    OCR1A = ADCH;
}

También experimenté con la interrupción INT0 en otro programa, pero aparentemente tampoco activa el controlador de interrupciones.

versión avr-gcc:

Using built-in specs.
Target: avr
Configured with: ../gcc-4.3.3/configure --enable-win32-registry=WinAVR-20100110 --with-gmp=/usr/local --with-mpfr=/usr/local --prefix=/c/WinAVR --target=avr --enable-languages=c,c++,objc --with-dwarf2 --enable-doc --disable-shared --disable-libada --disable-libssp --disable-nls --with-pkgversion='WinAVR 20100110' --with-bugurl='URL:http://sourceforge.net/tracker/?atid=520074&group_id=68108&func=browse'
Thread model: single
gcc version 4.3.3 (WinAVR 20100110)

Probablemente me falta alguna configuración, pero no he podido encontrar nada que no haya hecho. ¿Alguien puede ver lo que estoy haciendo mal? Gracias.

No estoy familiarizado con la sintaxis de AVR, pero ¿quisiste hacerlo CLEAR(ADMUX, MUX0)si ese es el que te interesa? Además, ¿está seguro de que la interrupción no se activa en absoluto, ni siquiera una vez? Si una vez, borra el indicador de interrupción dentro del ISR, ¿puede evitar que se vuelva a disparar?
Sí. ADMUX MUX0-3 selecciona una sola entrada de hasta 16 fuentes. Estoy trabajando con la hoja de datos, pero la tabla también está disponible en robotplatform.com/knowledge/ADC/adc_tutorial_3.html . Estoy bastante seguro de que la configuración de ADC/PWM es correcta, ya que cuando espejo ADC->PWM dentro del ciclo principal () funciona. Si configuro el LED a su máximo brillo en el controlador de interrupciones y no realizo ningún otro cambio, permanecerá oscuro, por lo que creo que no se activará en absoluto. Gracias por mirar.
¿Ha establecido un punto de interrupción en el ISR? ¿Es así como sabes que no está disparando?
Instalé Atmel Studio para obtener soporte de punto de interrupción y los archivos binarios que generan trabajo. Debe haber algo con el avr-gcc o las bibliotecas provistas con WinAVR. ¡Gracias por la ayuda!
Compruebe si se pasa la MCU correcta al compilador en la línea de comandos (-mmcu=...).

Respuestas (2)

Esto parece haber sido un problema con las bibliotecas avr-gcc/libraries incluidas con WinAVR, o cómo se configuró. La creación de la misma aplicación con Atmel Studio funciona como se esperaba.

versión avr-gcc:

Puedo confirmarle que su código funciona bajo mi winavr, una versión antigua de 2009.