1
\$\begingroup\$

I'm playing with an ATMega328P running off a USBtiny 1.0, and have the following set up:

  • Potentiometer to ADC0.
  • PB0 sourcing current for a LED.

My program then mirrors ADC0 to the PWM on PB0, so the position of the potentiometer directly controls the brightness of the LED.

This works when it is done inside the infinite loop at the end of main(), but if I move it to the ADC interrupt, nothing seems to happen (see "Remove me" comment).

I'm partially basing this off a tutorial at http://www.avr-tutorials.com/adc/utilizing-avr-adc-interrupt-feature, but using free-running mode.

#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;
}

I've also experimented with the INT0 interrupt in another program, but that also seemingly doesn't fire the interrupt handler.

avr-gcc version:

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)

I'm probably missing some setup, but haven't been able to find anything I haven't done. Can anyone see what I'm doing wrong? Thank you.

\$\endgroup\$
  • \$\begingroup\$ I'm not familiar with the AVR syntax, but did you mean to CLEAR(ADMUX, MUX0) if that's the one you're interested in? Also, are you sure the interrupt doesn't fire at all - not even once? If once, then clear the interrupt flag inside the ISR, it may stop it retriggering? \$\endgroup\$ – Ed King Feb 19 '17 at 9:36
  • \$\begingroup\$ Yeah. ADMUX MUX0-3 select a single input from up to 16 sources. I'm working from the datasheet but the table is also available at robotplatform.com/knowledge/ADC/adc_tutorial_3.html. I'm pretty sure the ADC/PWM setup are right as when I mirror ADC->PWM inside the main() loop it works. If I set the LED to full brightness in the interrupt handler and make no other changes to it, it remains dark, so I'm thinking it doesn't get fired at all. Thank you for looking. \$\endgroup\$ – jameswilddev Feb 19 '17 at 9:41
  • \$\begingroup\$ Have you set a breakpoint in the ISR? Is that how you know it's not firing? \$\endgroup\$ – Ed King Feb 19 '17 at 9:49
  • \$\begingroup\$ I installed Atmel Studio to get breakpoint support and the binaries that generates work. Must be something up with the avr-gcc or libraries supplied with WinAVR. Thanks for the help! \$\endgroup\$ – jameswilddev Feb 19 '17 at 10:23
  • \$\begingroup\$ Check if the correct MCU is passed to the compiler on the command line (-mmcu=...). \$\endgroup\$ – JimmyB Feb 19 '17 at 12:26
3
\$\begingroup\$

This appears to have been a problem with the avr-gcc/libraries included with WinAVR, or how it was configured. Building the same application using Atmel Studio works as expected.

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

avr-gcc version:

I can confirm for you that your code works under my winavr, an ancient 2009 version.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.