Таймер atmega 328p

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

  1. postal2201

    postal2201 Нерд

    Привет. Хочу считать микросекунды и миллисекунды с помощью таймера0 на 328p.

    Накидал немного кода:

    Код (C++):
    #define F_CPU 16000000

    #include <util/delay.h>
    #include <avr/io.h>
    #include <stdint.h>
    #include <avr/interrupt.h>
    #include <avr/sfr_defs.h>

    volatile uint32_t MicrosecCounter;
    volatile uint16_t MillisecCounter;


    ISR(TIMER0_COMPA_vect)
    {
      MicrosecCounter += 10;
      MillisecCounter++;
    }


    int main() {


      TIMSK0 = (1<<OCIE0A);
      OCR0A = 160;
      TCCR0A = (1<<WGM01);
      TCCR0B = (1<<CS00);

      sei ();

      while(1)  {
        ;
      }
    }
    Правильно ли я задал параметры, чтобы значение MicrosecCounter увеличивалось каждые 10us?
    Каким образом можно увеличивать MillisecCounter на 1 каждые 1000us?
     
  2. b707

    b707 Гуру

    OCR0A = 159. а не 160
    добавить счетчик и каждое сотое прерывание увеличивать MillisecCounter на единицу
     
    Последнее редактирование: 23 июн 2021
  3. postal2201

    postal2201 Нерд

    b707, спасибо. А OCR0A меньше на 1, это особенность именно 328p? Просто смотрел похожий код для Attiny10 и там для задержки в 50us был код без учета этой единицы:
    Код (C++):
    OCR0A = 400; //TOP=400, so OVF int at 8.000.000/400=20.000 Hz
     
  4. b707

    b707 Гуру

    это обычная логика. В примере для аттини тоже должно быть 399, а не 400
    причина - счетчик таймера начинает счет с нуля. поэтому чтобы получить делитель на 2 - нужно ставить OCR = 1. чтобы делить на 4 - нужно ставить OCR = 3 и так далее
    Об этом часто забывают, да и для больших чисел это не так важно, разница между 399 и 400 несущественна
     
  5. parovoZZ

    parovoZZ Гуру

    Это особенность программистов, которые не читают даташиты на МК. В даташитах все формулы есть - бери да считай.
     
  6. parovoZZ

    parovoZZ Гуру

    Не надо ничего увеличивать. Просто взять остаток от деления на 1000.
     
  7. postal2201

    postal2201 Нерд

    А можно пример?
     
  8. akl

    akl Гуру

    посмотри в файлах ардуины wiring.c как millis и micros устроено. не факт что это оптимально, там еще ради универсальности много лишнего, но в целом суть та же
     
  9. b707

    b707 Гуру

    не остаток, а целое
     
  10. postal2201

    postal2201 Нерд

    Ну это другое дело. А то я уже голову сломал каким там боком остаток от деления)
     
  11. parovoZZ

    parovoZZ Гуру

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

    На месте ТС я бы воспользовался одним из решений:
    1.софтовый таймер
    2.воспользоваться 16 или 32 битным таймером.
     
  12. postal2201

    postal2201 Нерд

    А сточки зрения производительности что будет выгоднее:
    1. Завести еще и счетчик прерывания и увеличивать MillisecCounter каждые 100 прерываний.
    2. MillisecCounter++, если остаток от деления 0.
    3. Умножать MicrosecCounter в коде на float 0.001 для получения миллисекунд.

    Про 16 битный таймер не понял, чем в данном случае он будет лучше чем 8 битный.
     
  13. parovoZZ

    parovoZZ Гуру

    16-ти битный таймер умеет считать до 999. А OCR каждый раз переписывать. Затея так себе, но зато имеем прерывания на каждый тик и чих.
     
  14. b707

    b707 Гуру

    между первыми двумя вариантами разница не очень велика, точно не скажу что выгоднее.
    Третий - заведомо проигрышный, причем в десятки раз
     
  15. AlexU

    AlexU Гуру

    Операция деления (взятия остатка) очень затратная в AVR, т.к. нет аппаратной реализации. Поэтому в обработчиках прерываний лучше не использовать деление.