Прерывание сравнения таймера AVR ATmega32U4 не срабатывает

Я попытался создать прерывание таймера CTC на моей плате ATmega32U4 leonardo. Когда я постоянно проверяю значение OCF1A, я без проблем определяю, когда выход достигает желаемого значения, однако, как только я помещаю код в прерывание, прерывание никогда не срабатывает.
Настройка таймера:

#include <avr/io.h>

void setupTimer()
{
    TCCR1B |= (1 << WGM12); // CTC mode
    TCCR1B |= ((0 << CS10) | (0 << CS11) | (1 << CS12)); // set up prescaler
    OCR1A = 6249; // 100 ms set up output compare value for interrupt
    TIMSK1 |= (1 << OCIE1A); // enable interrupt on clock compare
}

Цикл, который работает:

setupTimer();
for (;;) {
    if (TIFR1 & (1 << OCF1A)) {
        PORTC ^= (1 << PORTC7);
        TIFR1 = (1 << OCF1A);
    }
}

Прерывание, которое не работает:

#include <avr/interrupt.h>

ISR(TIMER1_COMPA_vect) {
    PORTC ^= (1 << PORTC7);
}

Я должен что-то упустить, так как из того, что я видел в учебниках, приведенный выше код должен работать. Одно интересное наблюдение заключается в том, что если в моем коде одновременно есть цикл и прерывание, то при вызове sei() светодиод не мигает, как если бы регистр OCF1A был очищен преждевременно.
Я почти уверен, что это не имеет значения. в этом случае предохранители следующие: E:CB, H:D8, L:FF.

Я использую avr-g++ для компиляции, и код распределен между несколькими файлами.


person pluczak    schedule 16.12.2015    source источник
comment
Я считаю, что код намного легче читать, если он использует макрос _BV() для битовых масок вместо ручного сдвига 1.   -  person Cactus    schedule 17.12.2015


Ответы (3)


Учитывая, что кто-то попал сюда через Google через два года после того, как был задан этот вопрос, я полагаю, что должен поделиться своими собственными выводами по этому вопросу.

Код, указанный в моем вопросе, верен и предполагает, что вызов sei()somewhere после setupTimer() прерывания должен срабатывать правильно. Проблема была такой же, как c0redumb, описанной в его ответе - загрузчик возился с некоторыми регистрами и, таким образом, препятствовал правильному запуску кода. . Однако мое решение этой проблемы было немного другим, так как в моем случае прерывание не срабатывало даже после отключения и повторного подключения платы (возможно, загрузчик изменился за два года с тех пор, как я задал этот вопрос).

Самый простой способ предотвратить конфликт между кодом и загрузчиком — просто удалить загрузчик. Используя программатор USBasp, можно просто загрузить свой собственный код на плату и, таким образом, быть уверенным, что это единственное, что работает на процессоре.

person pluczak    schedule 19.07.2017

У вас две проблемы:

  1. Вы должны убедиться, что main() не возвращается, даже если он ничего не делает, кроме ожидания прерывания.

  2. Вам нужно включить прерывания через sei() после того, как все настроите.

Вот рабочий пример (я изменил порт светодиода на PB5, потому что тестировал его на Arduino Uno, в котором уже есть встроенный светодиод)

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

void main ()
{
    DDRB |= 1 << DDB5;

    TCCR1B |= 1 << WGM12;
    TCCR1B |= 1 << CS12;
    OCR1A = 6249;
    TIMSK1 |= 1 << OCIE1A;

    sei();
    for(;;);
}

ISR(TIMER1_COMPA_vect)
{
    PORTB ^= 1 << PORTB5;
}
person Cactus    schedule 17.12.2015
comment
Да, я знаю и делаю обе упомянутые вами вещи, я просто включил в свой вопрос только наиболее важную часть кода. Проблема с тем, что прерывание не срабатывает, все еще существует, и я вижу 3 возможных источника: код, make-файл или фьюзы. На данный момент, если я не пропускаю какую-то странную настройку реестра, проблема, вероятно, не в коде, и я не уверен, что вообще возможно вызвать такую ​​​​проблему с помощью настроек makefile или предохранителя. - person pluczak; 18.12.2015

Этот вопрос меня и сегодня озадачивает. Через поиск нашел ваш вопрос. Я сделал еще несколько поисков по всему миру и не нашел ответа на этот вопрос. Я думал, что, должно быть, забыл включить какую-то схему или установить какие-то флаги. Наконец, используя светодиод в качестве отладчика, я нашел причину.

Проблема в загрузчике, а не в вашем коде. Чтобы заставить его работать, вы просто отключаете плату от USB (после написания кода через загрузчик) и повторно подключаете, чтобы загрузчик переходил непосредственно к вашему коду при включении питания, и он там работал. Загрузчик, должно быть, проделал какую-то причудливую работу при загрузке кода, и впоследствии в этой ситуации он не работал должным образом.

В качестве эталона я использовал плату ProMicro, которая, как я полагаю, имеет загрузчик Caterina, такой же, как плата Leonardo, которую вы использовали.

person c0redumb    schedule 18.07.2017