Изменчивая переменная не обновляется с таймера ISR

Я знаю, что эта проблема возникала много раз, но я не могу заставить этот фрагмент кода обновлять переменную count на ATMEGA328P. Я использую Arduino Uno в качестве платы для разработки, а светодиод на контакте 13 настроен на вспышку для отладки. Я убедился, что таймер и светодиодная часть работают, переключив контакт 5 порта B в процедуре ISR.

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

volatile uint8_t count;

void init_100us_tick(void) {
    TCCR0A = _BV(WGM01) | _BV(WGM00);       // Режим WGM 7 с OCRA наверху
    TCCR0B = _BV(WGM02) | _BV(CS01);        // CLK/8 прескалер
    TIMSK0 = _BV(OCIE0B) | _BV(TOIE0);      // Прерывание разрешено на OCR0A
    OCR0A = 200;                            // 16 МГц / 8 / 200 = 100 мкс
    sei();                                  // Разрешить прерывания
}

ISR(TIMER0_COMPB_vect) {
    count++;
}

int main(void) {
    DDRB |= _BV(DDB5);                      // вывод PB5
    init_100us_tick();                      // Инициализировать 100 мкс тик
    count = 0;                              // В случае микросброса

    for (;;) {
        if (count >= 100) {                 // Каждые 10 мс
            count = 0;
            PORTB ^= _BV(DDB5);             // Переключить PB5
        }
   }
   return 0;
}

Это скомпилировано и записано на Arduino Uno со следующим:

avr-gcc -Os -DF_CPU=16000000UL -mmcu=atmega328p -c -o test.o test.c
avr-gcc -mmcu=atmega328p test.o -o test
avr-objcopy -O ihex -R .eeprom test test.hex
avrdude -F -V -c arduino -p ATMEGA328P -P /dev/ttyUSB0 -b 115200 -U flash:w:test.hex

Я пытался отключить оптимизацию, но безрезультатно. AVR-GCC версии 4.9.2

, 👍1

Обсуждение

Прерывание включено на OCR0A - не забудьте изменить комментарии, если вы меняете код! У вас есть прерывание на TIMER0_COMPB_vect!, @Nick Gammon


1 ответ


Лучший ответ:

2

Для этого вам нужен режим CTC. Как в:

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

volatile uint8_t count;

void init_100us_tick(void) {
    TCCR0A = _BV(WGM01);       // CTC режим 2 с OCRA наверху
    TCCR0B = _BV(CS01);        // CLK/8 прескалер
    TIMSK0 = _BV(OCIE0B) | _BV(TOIE0);      // Прерывание разрешено на OCR0B
    OCR0A = 200;                            // 16 МГц / 8 / 200 = 100 мкс
    sei();                                  // Разрешить прерывания
}

ISR(TIMER0_COMPB_vect) {
    count++;
}

int main(void) {
    DDRB |= _BV(DDB5);                      // вывод PB5
    init_100us_tick();                      // Инициализировать 100 мкс тик
    count = 0;                              // В случае микросброса

    for (;;) {
        if (count >= 100) {                 // Каждые 10 мс
            count = 0;
            PORTB ^= _BV(PORTB5);             // Переключить PB5
        }
   }
   return 0;
}

Переключается каждые 10 мс.


(Отредактировано для добавления)

Выбранный вами режим ШИМ будет работать, если вы включите правильное прерывание. Это также выводит импульсы 10 мс:

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

volatile uint8_t count;

void init_100us_tick(void) {
    TCCR0A = _BV(WGM01) | _BV(WGM00);       // Режим Fast PWM 7 с OCRA наверху
    TCCR0B = _BV(WGM02) | _BV(CS01);        // CLK/8 прескалер
    TIMSK0 = _BV(OCIE0A);                   // Прерывание разрешено на OCR0A ***
    OCR0A = 200;                            // 16 МГц / 8 / 200 = 100 мкс
    sei();                                  // Разрешить прерывания
}

ISR(TIMER0_COMPA_vect) {                    // Сравнить вектор ***
    count++;
}

int main(void) {
    DDRB |= _BV(DDB5);                      // вывод PB5
    init_100us_tick();                      // Инициализировать 100 мкс тик
    count = 0;                              // В случае микросброса

    for (;;) {
        if (count >= 100) {                 // Каждые 10 мс
            count = 0;
            PORTB ^= _BV(PORTB5);           // Переключить PB5 ***
        }
   }
   return 0;
}

Измененные строки, обозначенные ***

Обратите внимание, что не следует включать _BV(TOIE0), поскольку у вас нет обработчика прерываний для переполнения таймера 0. Без обработчика прерываний компилятор, вероятно, сгенерировал код для перехода к вектору сброса.

Прерывание происходит, когда таймер соответствует количеству Compare A (не Compare B), поэтому вам нужно прерывание на стороне A.

,

Возможно, вы захотите установить для OCR0B определенное значение, вместо того, чтобы полагаться на значение по умолчанию 0., @Gerben

Спасибо, это сработало! Не могли бы вы объяснить, почему переход в режим CTC имеет такой эффект?, @Matthew Curlis

Смотрите измененный ответ., @Nick Gammon