Жесть с прерываниями на ATmega328

Тема в разделе "Микроконтроллеры AVR", создана пользователем Redfern89q, 13 янв 2019.

  1. Redfern89q

    Redfern89q Нуб

    Доброго времени суток!)) Есть простой код

    Код (C++):
    ISR (INT0_vect) {
        if (INT0_CNT == 33) INT0_CNT = 0;
        INT0_CNT++;
       
        USORT_Transmit(INT0_CNT);
    }
    при первом приходе 34х прерываний в терминал выводит все как нужно:
    при втором приходе 34х прерываний, в терминал выскакивает это:
    но стоит убрать условие if(INT0_CNT ==33) INT0_CNT =0;, как отсчет идет нормально и без сбоев, но без обнуления((((
     
  2. parovoZZ

    parovoZZ Гуру

    Переменная INT0_CNT с квалификатором volatile?
    Эта функция что делает? Она зависит от прерываний?
    34 там не должно быть.
     
  3. Redfern89q

    Redfern89q Нуб

    да, volatile есть volatile int INT0_CNT = 0; эта функция выводит в uart один байт данных
     
  4. parovoZZ

    parovoZZ Гуру

    если где-то в ней есть
    Код (C++):
    sei();
    то всё - приплыли.
     
  5. Redfern89q

    Redfern89q Нуб

    не, нету))
    Код (C++):
    void USORT_Transmit( unsigned char data ) {
        while ( !( UCSR0A & (1 << UDRE0)) );
        UDR0 = data;
    }
     
  6. parovoZZ

    parovoZZ Гуру

    частота на UART какая? Дребезг на INT0 возможен?
     
  7. Redfern89q

    Redfern89q Нуб

    частота по стандарту )) 9600 ) дребезга точно нету.. это ИК-приемник, подключенный к МК. Прерывание нужно для декодировки протокола NEC с пульта))))
     
  8. Redfern89q

    Redfern89q Нуб

    кароче вот как вся программа выглядит... функцию USORT_Transmit я вывел из прерывания.. все равно та же жесть

    Код (C++):
    /*
    * led_clock.c
    *
    * Created: 24.12.2018 19:24:51
    * Author : Redfern.89
    */


    // Частота проца в герцах
    #ifndef F_CPU
    #define F_CPU    16000000UL
    #endif

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

    // Управление таймером 0 для IRDA
    #define TIMER0_START                    {    TCCR0B |= (1 << CS01) | (1 << CS00);                        }
    #define    TIMER0_STOP                   {    TCCR0B = 0x00;                                                           }

    /* Протокол NEC */
    volatile int NEC_IR_DONE                             = 0;
    volatile unsigned long int NEC_SCLK            = 0;      
    const static int NEC_MAX_RESET_OVF       = 1200;
    const static int NEC_PACKET_LENGTH       = 32;
    unsigned char NEC_IR_SIGNAL[34];
    volatile int INT0_CNT                                     = 0;

    #define FOSC 16000000L
    #define BAUD 9600L
    #define MYUBRR FOSC/16/BAUD-1

    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                                    USART functions
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

    void USORT_Init(unsigned char ubrr){
        UBRR0H = (unsigned char)(ubrr>>8);
        UBRR0L = (unsigned char)ubrr;
        /*Enable receiver and transmitter */
        UCSR0B = (1<<RXEN0)|(1<<TXEN0);
        /* Set frame format: 8data, 2stop*/
        UCSR0C = (1<<USBS0)|(3<<UCSZ00);
    }

    void USORT_Transmit( unsigned char data ) {
        while ( !( UCSR0A & (1 << UDRE0)) );
        UDR0 = data;
    }

    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                                NEC 2.0 functions
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */


    // Таймер обработки длительности импульсов
    ISR (TIMER0_OVF_vect) {
        TCNT0 = 0xF0;
        if (++NEC_SCLK >= NEC_MAX_RESET_OVF) {
            NEC_SCLK = 0;
            TIMER0_STOP;
        }
    }

    // Обработчик внешнего прерывания
    ISR (INT0_vect) {
        NEC_IR_SIGNAL[INT0_CNT] = INT0_CNT;
     
        if (INT0_CNT == 33) {
            INT0_CNT = 0;
            NEC_IR_DONE = 1;
        }
        INT0_CNT++;
    }

    int main(void) {

        asm("CLI");
        // Инициализация внешнего прерывания (на спад)
        EICRA |= (1 << ISC01) | (0 << ISC00);
        EIMSK |= (1 << INT0);
        EIFR |= (1 << INTF0);
        PCICR = 0x00;
     
        // Инициализация таймера/счестчика 0 для подсчета временного интервала между импульсами
        TCCR0B = 0x00;//(1 << CS00) | (1 << CS01);
        TIMSK0 |= (1 << TOIE0);
        TCNT0 = 0xF0;
     
        USORT_Init(MYUBRR);
        asm("SEI");
     
        while (1)  {
            if (NEC_IR_DONE) {
             
                for (int i = 0; i < 33; i++) {
                    USORT_Transmit(NEC_IR_SIGNAL[i]);
                }
             
                NEC_IR_DONE = 0;
            }
        }
     
        return 0;
    }

     
     
  9. parovoZZ

    parovoZZ Гуру

    Как в последнем листинге - так делать нельзя. Либо останавливать проц (IDLE), либо перед входом в цикл запрещать прерывания, а разрешить только после обнуления флага.
     
  10. Airbus

    Airbus Радиохулиган Модератор

    Попробуйте убрать ассемблерные вставки CLI/SEI
     
  11. Asper Daffy

    Asper Daffy Иксперд

    Да, и NEC_IR_SIGNAL неплохо бы "заволатилить"
     
    Airbus нравится это.
  12. parovoZZ

    parovoZZ Гуру

    Работать ничего не будет, ибо I бит в sreg сброшен и прерывания запрещены глобально.
     
  13. MakD

    MakD Нерд

    Доброго времени суток! Столкнулся с небольшой проблемой и не вполне понимаю в какую сторону копать. Буду признателен за пинок в правильную сторону...
    Схема. Есть Iskra Mini. Третий пин подключен к земле (пока все на макетной плате). При его размыкании должно уходить сообщение на некоторое устройство. Ввиду того, что размыкание контакта имеет дребезг, хотел программно его устранить по приведенной ниже схеме. То есть при срабатывании прерывания - отключаю само прерывание, в лупе провожу обработку прерывания и после некоторой задержки взвожу снова. Но код рабочий только один раз. То есть attachInterrupt() в лупе не отрабатывает. Проверяю тупо отсоединением земли на третьей ноге. Переход уровня с Low на High точно присутствует.
    Вот выдержки кода (опущена функция send2server())
    Код (C++):

    const     int     SECURITY_OPER_ID = 1010;
    const     int     SECURITY_PIN = 3;
    volatile  int     operId = 0;
    void setup() {
      pinMode(SECURITY_PIN, INPUT_PULLUP);
      attachInterrupt(1, securityInt, RISING);
    }
    void loop(){
      switch(operId){
        case SECURITY_OPER_ID :
          send2server(operId, 1);
          delay(200);
          attachInterrupt(1, securityInt, RISING);
          break;
      }
      operId = 0;
    }
    void securityInt(){
      detachInterrupts(1);
      operId = SECURITY_OPER_ID;
    }
     
     
  14. Daniil

    Daniil Гуру

    У вас прерывание по событию перехода уровня на пине с 0 -> 1.

    Вы отловили первое прерывание, подождали, сигнал успокоился, включаете прерывание, но оно не срабатывает, потому что нет перехода 0 -> 1, есть просто 1 на пине.

    Вам нужно прерывание не Rising, а какое-то другое, которое реагирует на постоянный уровень, но в таком случае после включения прерывания повторно прерывание будет происходить постоянно, пока будет 1.

    Вашу задачу можно решить проще, без прерываний - посмотрите код борьбы с дребезгом без delay.

    Или поставьте буферный триггер Шмитта перед пином.
     
  15. MakD

    MakD Нерд

    Нет, не совсем так. Я размыкаю контакт с землей, на третьем пине возникает 5В. Первое прерывание срабатывает. Возвращаю контакт на землю. Вот он 0 на третьем пине. После чего снова размыкаю контакт с землей, но прерывания уже нет. Если убрать все танцы с detachInterrupts() в функции прерывания и attachInterrupts() в loop'е, то повторное и т.д. прерывания срабатывают.
     
  16. parovoZZ

    parovoZZ Гуру

    кто скажет - функция securityInt вызывается как callback из обработчика прерывания или же после него? Как эта абдурина устроена? Там есть толи баг, тjли фича - из обработчика прерываний невозможно изменить другие прерывания. Взрослая артиллерия подтянется - расскажет.
    ТС, а что мешает вручную рулить прерываниями через регистры? Там либо GIMSK будет, либо EICRA/EIMSK. Абдурина никак не настроена для таких тонких нюансов.
     
  17. AlexU

    AlexU Гуру

    Направление мыслей правильное, но вот реализация подкачала.
    Но для начала, правильно понимаю, что в обработчике буковка 's' -- detachInterrupts(1); -- это опечатка в сообщении на форуме и в реальном коде её нет?
    А теперь к вопрсу.
    Есть такое понятие как флаг прерывания. Сначала взводится флаг, а потом вызывается обработчик прерывания. Так вот эти флаги могут возводится не зависимо от того, активирована обработка прерывания или нет. Если флаг уже был возведён и Вы активируете обработку прерывания, то обработчик сразу будет вызван (там есть особенности -- детали надо смотреть в документации на контроллер).
    И что происходит в Вашем случае: Вы отключаете вход от земли; возводится флаг прерывания; вызывается обработчик прерывания, он отключает обработку прерывания, устанавливает переменную и при этом сбрасыватеся флаг прерывания; дребезг контактов заставляет опять поднять флаг прерывания; в функции 'loop()' срабатывает условие оператора 'switch'; выполняются соответствующие действия и включается обработка прерывания; т.к. флаг возведён вызывается обработчик прерывания, который отключает обработку прерывания и устанавливает значение переменной 'operId'; после обработки прерывания программа продолжает свою работу с того места, где была прервана на обработку прерывания -- внутри тела оператора 'switch' (это не совсем правда, но примерно в этом месте); далее выполняется строчка кода 'operId = 0;'; ну вот и всё...
    Два варианта решения:
    1. сбрасывать флаг прерывания перед его активацией (EIFR = b00000011 // последние два бита отвечают за прерывания INT1 и INT0);
    2. с дребезгом бороться внутри обработчика прерывания.
     
    Сусемьбек и MakD нравится это.
  18. AlexU

    AlexU Гуру

    Функция securityInt вызывается как callback.
    А что касается остального, то лучше бы промолчал....
     
  19. DetSimen

    DetSimen Guest

  20. parovoZZ

    parovoZZ Гуру

    Все эти флаги взводятся всегда, когда разрешена работа того или иного блока. Если прерывания не разрешены, то сброс флагов - задача программиста. Если разрешены - сбрасываются автоматически в обработчике.
    Если флагов несколько, то первым в обработчик в AVR уходит тот, у кого меньше адрес. На это уходит 4 такта. Затем возврат из обработчика, обязательное выполнение одной следующей инструкции и снова уход на следующий обработчик.

    Да, да, да)) Столкнулся с этим на Attinyх4 - из глубокого сна по INT0 МК не выходит. Перевел на PCINT (на той же ноге INT0=) ) - влет.