Прерывание по таймеру в СТС режиме ATMEGA328 кварц 16 мгц (UNO)

привет всем , попытался сделать таймер с точностью 1 мкс в режиме СТС , то есть на таймере1 (65535 тактов заполнения таймера , для точности 1 мкс нужно 16 тактов + вкл выключение , но точности менее 5 мкс получить не удалось таким способом , меняя количество тактов для прерывания СТС режима , значение счетчика меняется только при значениях выше 200-500 тактов или даже выше ( проверял на 2600 там меняется ) т. е. что 20 тактов что 5 , все равно получаю такую погрешность , код прикрепляю ниже

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

LiquidCrystal_I2C lcd(0x27 ,16,2);
volatile unsigned long T, myTimer, time_old ;
volatile boolean flag = 0 ;
int n = 0;
void setup() {
  lcd.init(); 
  lcd.backlight(); // подсветка 
  lcd.clear();
  pinMode(3,INPUT_PULLUP);
  attachInterrupt(1 , myinterrupt, RISING );// включаем режим аппаратного прерывания
  cli(); // выключения прерываний 
  TCCR1A = 0 ;
  TCCR1B = 0;
  TCNT1 = 0;
  OCR1A = 14;
  TCCR1B |= (1 << WGM12); // режим СТС
  TCCR1B |= (1 << CS10); // без подделителя
  TIMSK1 |= (1 << OCIE1A); // настройка прерывания режима СТС
  sei();// включение прерываний 
}

void loop() {
  if (flag) {
    float f = 1000000/T;
    lcd.clear();
    lcd.setCursor(4,0);
    lcd.print(T);
    delay(100);             // выводы на дисплей нужных значений 
    cli();
    flag = 0;
    TCCR1B |= (1 << WGM12);
    TCCR1B |= (1 << CS10); // включение прерываний после их отключения в самом прерывании ( чтобы таймер не мешал выполнению loop )
    TIMSK1 |= (1 << OCIE1A);
    sei();
  }

}
void myinterrupt(){
  if(!flag){  
    n++; // счетчик фронта , 1 или 2 

  }
  if (n > 3){
    n = 0;
  }
}

ISR(TIMER_COMPA_vect){

  myTimer++
  if ( n == 1){
    time_old = myTimer; // время первого фронта 
    n = 2;
  }
  else if (n == 3){
    T= myTimer - time_old ; // время второго фронта 
    flag = 1 ;
    n = 0;
    TCCR1B = 0;
  }
}

А точность чего?
Длительность импульсов можно настраивать с дискретностью 62.5 нс, а получить прерывание с частотой 1 МГц, очевидно, не получится.

Вы забываете что аппаратные прерывания обрабатываются функциями,вызов которых занимает достаточно много времени.
Пока обрабатывается myinterrupt(),уже наступает прерывание по сравнению ISR(TIMER_COMPA_vect).
Сами флаги прерываний,и,если соответствующим образом настроены выходы OCRx, наступают с точностью до такта.

Вот как раз,видимо, обработка прерываний в функциях именно столько и занимает.
Варианты:

  1. Переработать архитектуру
  2. Взять МК помощней(например stm32)(хотя спорно,т.к. там тоже есть нюансы)
  3. Изменить концепцию с МК на “железную” логику или ПЛИС.

Точность таймера , предполагается за 16 тактов проходит 1 мкс, но на самом деле прерывание срабатывает не в 1 мкс а в 5 мкс , хочу узнать можно ли сделать счетчик времени с такой точности и как

ТС, Дорогой, если тебе нужно измерить величину периода сигнала, то есть время между двумя фронтами, то нужно просто разместить заказ в “Ищу исполнителя”. Как раз недавно такое писал, поэтому за пару тысяч рублей “отдам в хорошие руки”. :wink:
Точность порядка микросекунды. Если писать очень аккуратно и входить в прерывание одинаково всегда - то точность два такта- вполне достижима. То есть задержка при входе в прерывание - порядка 50 тактов, но она одинаковая, значит можно считать только разницу, понимаешь?
И да, писать так как ты написал - не стоит. Зачем тебе КомпА вектор? Для каких целей? Чтобы время считать не мучь таймер, пусть крутится, только его счетчик забирай по прерыванию от входа и считай разницу.

Так если считать время надо,тогда режим захвата по входу ICR нужен. Даташит в помощь. Там разжевано до немогу.

Не путайте таймер и прерывание. Таймер можно запрограммировать на 8 МГц, а вызывать прерывание с частотой выше 150-200 кГц вряд ли получится.

Так надо всё таки выдавать точный сигнал или измерять его?
Если выдавать,то я бы за основу взял ассемблерные вставки файла delay.h в стандартном комплекте в avr-gcc. Там точные циклы здержки релизованы.

Владдракула правильно расписал задачу ,
На вход 3 приходит сигнал определенной частоты , нужно измерять период этого сигнала по 1 волне , с точностью до 1 мкс
А таймер нужен чтобы отчитывать время фронтов этого сигнала , надеюсь правильно расписал

Не выйдет. Вернее, мошт выйдет, но на другом контроллере

На PIC-контроллерах полно схем частотомеров до 50МГц…1ГГц.
Честно говоря никогда не задумывался как это там реализовано - или микроконтроллеры “заточены” под это дели или алгоритмы какие “хитрые”…

С прерываниями где то 2 мкс, меньше не получится. И то нужно изгаляться с обработчиком.
Без прерываний - 1 мкс на асм вполне реально. Dimax расписывал на старом форуме. Только ограничение на длину измеряемого импульса было.
Только речь о длительности импульса, а не о периоде. С этим ещё повозиться нужно.

А частотомеры в ПИКах работают на высоких частотах за счёт высокоскоростного предделителя таймера, значение которого вычисляется слегка по хитрому.

1 лайк

на аттини тоже высокоскоростной, не пытал, но уверен 100 мегагерц пролезет

Да вы чего? Замер времени производится аппаратно таймером в режиме ICR.

  1. Первый фронт,по прерыванию запоминаем регистр ICR
  2. По второму смотрим разницу значений регистра ICR.
    Всё!

Какие пределы измеряемой частоты, от и до?

очень интересно, как вы это себе представляете?
осциллограмму сигнала покажете?

От 1 до 750 Гц

c точностью 1 мкс?

1 лайк

С точностью 1 ppm?

Тогда метод с Input Capture Register (ICR1) подходит.
(Нужна ли такая точность - это другой вопрос)