прерывания по таймеру в atmega 328p

Тема в разделе "Микроконтроллеры AVR", создана пользователем Мурад, 23 мар 2017.

  1. Мурад

    Мурад Нерд

    Сделал программу в atmel studio 7. программа должно каждую секунду инвертировать значение пина D2, но она почему-то отказывается работать. цель программы была в проверке и создании прерывания, которое считает миллисекунды. при загрузки программы в atmega 328p он устанавливает пин D2 в высокий уровень и всё.Частота кварца —16МГц. отладку производить пока не умею, поэтому решил обратиться за помошью. Что надо сделать с кодом , чтоб он заработал как надо?

    Код (C++):
    #include <avr/io.h>
    #include <avr/interrupt.h>
    unsigned long milis=0;
    #define interval 1000

    ISR( TIMER1_COMPA_vect  ) //каждую миллисекунду прибавлять 1 к milis
    {
    milis++;
    }

    int main()
    {
    unsigned long past=0;

        /***Настраиваем таймер***/
        TCCR1A = 0x00;
        TCCR1B = (0 << CS12)|(1 << CS11)|(1 << CS10)|(1 << WGM12); //предделитель clk/64, режим таймера СТС
        TCNT1 = 0x00;
        OCR1A = 250; // максимальный предел счета
        TIMSK1 |= (1 << OCIE1A); // разрешение прерывания по совпадению
        //////////////////////////


            DDRD = (1 << 2);
            PORTD=(1 << 2);
    sei();
        while (1) {              
        if((milis - past )>= interval){ //через каждые 1000 миллисекунд инвертировать пин D2
        past=milis;
        PORTD^=(1<<2);
            }}
        return 0;
    }
     
     
    Последнее редактирование: 24 мар 2017
  2. ostrov

    ostrov Гуру

    Первое что попробуйте:
    Код (C++):
    PORTD = PIND^(1<<2);
     
  3. Мурад

    Мурад Нерд

    не помогло. Но я тут провёл несколько тестов и выяснил, что ,возможно, проблема заключается в переменных, но volatile не помогло
     
    Последнее редактирование: 24 мар 2017
  4. ostrov

    ostrov Гуру

    Уверены, что таймер правильно настроен? У меня нет сейчас таблицы перед глазами.
     
  5. Мурад

    Мурад Нерд

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

    вот тело
    Код (C++):
     LSR(TIMER1_COMPA_vect){
    PORTD^=(1<<2);
    }
     
  6. ostrov

    ostrov Гуру

    LSR?

    Попробуйте еще interval сделать переменной unsigned long.
     
  7. serg_admin

    serg_admin Гик

    А как вы считаете частоту сработки прерывания?

    16 000 000 /64 /250 = 1000 раз в секунду.

    В результате яркость диода немного снизится. Мигать он не будет (если не использовать высокоскоростные камеры :)).
     
  8. serg_admin

    serg_admin Гик

    Код (C++):
    109   TCCR1A = 0; // CTC mode - TOP значения таймера в регистресе OCR1A.
    110   TCCR1B = _BV(WGM12); // CTC mode.
    111   // Делитель частоты счетчика 8 (CS12:10 - 010).
    112   // При частоте 16 MHz - 0,5 мкС один такт.
    113   TCCR1B |= _BV(CS11);
    114   // Из расчета 0,5 мкС на такт, ставим длину таймера 25 мили секунд.
    115   // 40 раз в секунду. 50000 *0,5 = 25 000.
    116   OCR1A  = 50000;
    117   TIMSK1 = _BV(OCIE1A); // Включить прерывание TOP таймера.
     
     
  9. Мурад

    Мурад Нерд

    прерывание вызывается через каждую миллисекунду, а в основном цикле через каждые 1000 миллисекунд светодиод переключается. делитель был настроен в моём скетче на 64. светодиод горел довольно ясно
     
  10. Мурад

    Мурад Нерд

    я немного изменил скетч и залил его в arduino nano через arduino ide, и всё работало нормально, но ,залив тот же скетч из через программатор в рабочую arduino micro, программа уже не выполнялась как надо. компилировал скетч в atmel studio 7, оптимизацию отключал. С чем это может быть связанно?
    Код (C++):
    #define F_CPU 16000000UL
    #include <avr/io.h>
    #include <avr/interrupt.h>
    volatile unsigned long milis,past;
    #define interval 1000
    #define pin 5


    ISR(TIMER1_COMPA_vect){
      milis++;
    }

    int main()
    {
      DDRB = (1 << pin);
      PORTB=(1 << pin);

      /***Настраиваем таймер***/
      TCCR1A = 0x00;
      TCCR1B = (0 << CS12)|(1 << CS11)|(1 << CS10)|(1 << WGM12); //предделитель clk/64, режим таймера СТС
      TCNT1 = 0x00;
      OCR1A = 250; // максимальный предел счета
      TIMSK1 |= (1 << OCIE1A); // разрешение прерывания по совпадению
      //////////////////////////
     
     
      sei();
      while (1){
        if((milis-past)>interval){
          past=milis;
          PORTB ^=(1<<pin);
        }}
        return 0;
      }
     
  11. serg_admin

    serg_admin Гик

    Бывает, что не тот процессор выбрал.

    Я в таких случаях делаю проверку до куда код доходит.
    Например выставляешь HIGH на какой-нибудь PIN в прерывании и смотришь - прерывание срабатывает вообще

    Еще вариант
    Код (C++):
    OCR1A = 65000;
    Код (C++):
    ISR(TIMER1_COMPA_vect){
      milis++;
      PORTB ^= (1<<pin);
    }
    Тогда визуально можно будет увидеть "моргание" PINа
     
  12. serg_admin

    serg_admin Гик

    И еще:
    Попробуй
    Код (C++):
    TIMSK1 |= (1 << OCIE1A);
    Исправить на
    Код (C++):
    TIMSK1 = (1 << OCIE1A);
     
  13. koteika

    koteika Гик

    Вот настройка таймера по прерыванию в 1 с, но для атмеги 8. Под 328 тоже должно пойти..

    Код (C++):
    void timerInit()
    {
        //TCCR1A |=
        TCCR1B |= (1<<CS12) | (1<<WGM12); //256, ctc
        //TCCR1C |=
        //TCNT1 = 0x00;
        OCR1A |= 31249; // 1 c
        TIMSK |= (1<<OCIE1A);
    }
    Само прерывание
    Код (C++):
    ISR(TIMER1_COMPA_vect)
    {
    //Делаем что надо
    }
    Так не по фэншую, по фэншую запись 1 в регистр:
    Код (C++):
    TIMSK |= ....
    запись 0 в регистр:
    Код (C++):
    TIMSK &= ~(тут что нужно, например (1<<OCIE1A))
     
  14. mcureenab

    mcureenab Гуру

    Зачем переключение пина делать на прерывании? Пин можно настроить, чтобы таймер его сам переключал, например по достижению порога. Это гораздо точнее.

    Пример программирования таймера есть тут

     
  15. serg_admin

    serg_admin Гик

    Ядро ардуино то же настраивает таймеры для работы analogWrite() поэтому если не сбросить значения остальных битов результат может отличаться от ожидаемого. Отчего в функции setup() надежнее именно
    Код (C++):
    TIMSK1 = (1 << OCIE1A);
     
  16. DetSimen

    DetSimen Guest

    гарантированная 1 миллисекунда
    Код (C++):
       
        TCCR1A = 0; TCCR1B = 0;
        TCNT1 = 0;

        OCR1A = 1999;
        TCCR1B |= (1 << WGM12);
        TCCR1B |= (1 << CS11);// | (1 << CS10);
        TIMSK1 |= (1 << OCIE1A);
     
    и потом
    Код (C++):
    ISR(TIMER1_COMPA_vect)
    {
        OCR1A = 1999;
    }
     
  17. kipalex

    kipalex Нуб

    Подскажите в чем ошибка, перепробовал разные варианты. Суть в том, чтобы как только сработала кнопка порт был активен 20 секунд (в идеале минуту или больше), по факту получается 6 секунд.

    Код (C++):
    // Set prescale to 256 and start the timer
    TCCR1B |= (1 << WGM12) | (1 << CS12) | (0<<CS11) | (0<<CS10);
    TCCR1A = 0;
    // set compare for 1 second interrupts
    OCR1A = 0x7A11;
    // setup for compare interrupts
    TIMSK1 |= (1<<OCIE1A);
    sei();
    while (1)
        {
            switch(get_key())
            {
                // Processing reaction of the button 1
                case KEY1:
                ALARM_OUT_EN;
                seconds = 20;
                break;
            }
            // Processing timer expired
            if (seconds == 0)
            {
                seconds = -1;
                ALARM_OUT_STOP;
            }

        }
    }

    ISR(TIMER1_OVF_vect)
    {
        if (seconds > 0)
        seconds--;
    }
    Если делать через ISR(TIMER1_COMPA_vect) то порт включается и не выключается.
     
  18. parovoZZ

    parovoZZ Гуру

    Обычно, когда я привожу код, я рассказываю подробно, что происходит в каждой строчке. А здесь даже комментарии на ангельском. ДЛЯ КОГО???
     
  19. kipalex

    kipalex Нуб

    А что в коде не понятно? Английский? А Atmel Studio с русским не очень. ALARM_OUT_EN или ALARM_OUT_STOP не понятно так это я специально убрал смысл если говорю работает. Для Вас лично:
    Код (C++):
    #define ALARM_OUT_EN (PORTB|=(1<<PORTB2))
    #define ALARM_OUT_STOP (PORTB&=(~1<<PORTB2))
    Опрос кнопки switch(get_key()) привести он как бы стандартный через case.

    Что происходит в какой строке и так понятно. комментарии на английском.
     
  20. DetSimen

    DetSimen Guest

    Нада
    #define ALARM_OUT_STOP (PORTB &= ~(1<<PORTB2))
     
    Igor68 нравится это.