Еще один отсчет таймера

Kunzo
Offline
Зарегистрирован: 22.06.2017

Использую таймер для генерациии прерывания на частоте 250 ГЦ

 void set_timer_T2A(void)
  {
      TCCR2B=0; //  Т2 стоп
      TCNT2=0;   //  счётный регистр
      OCR2B=0;
      TIFR2&=0xff; 
      TCCR2A=(1<<WGM21); //Mode2(CTC)
      OCR2A=249; // 500 Гц 
      TIMSK2=(1<<OCIE2A); //прерывание по совпадению с OCR2A
      TCCR2B=(1<<CS22)|(1<<CS21); // делитель на 256
  }
  
  ISR (TIMER2_COMPA_vect) { ; ;  }
  

Как  описать настройку таймера T2B, чтобы он вызывал  другое прерывание,

ISR (TIMER2_COMPB_vect) { ; ;  }

по частоте  синхронное с T2A, но исполняемое  при значении OCR2B=83 и при этом T2 бы не начинал отсчет заново как в случае с OCR2A=249, а вызывал его прерывание TIMER2_COMPA  и только после этого перезапускался ?

Те частота вызовов нового и старого прерываний  должна остаться без изменения.

 

King Size
Offline
Зарегистрирован: 22.06.2017

Нахрена такие сложности, при входе в аервое прерывание запустите другой таймер, который через 83 клока даст вам прерывание . И на этом  всё !

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Kunzo пишет:

при этом T2 бы не начинал отсчет заново как в случае с OCR2A=249, а вызывал его прерывание TIMER2_COMPA  и только после этого перезапускался ?

Частота тут неизменна, она жёстко задана регистром OCR2A.  Таймер не в состоянии начать отчёт заново не досчитав до OCR2A.   Стало быть если вы допишите OCR2B=83; TIMSK2|=1<<OCIE1B; то второе прерывание будет работать с той же частотой, но сдвинуто по времени на OCR2A-OCR2B тактов.

Kunzo
Offline
Зарегистрирован: 22.06.2017

dimax пишет:
Частота тут неизменна, она жёстко задана регистром OCR2A.
Мне как раз нужна одинаковая частота, но сдвиг по времени. 

OCR2A и OCR2B не равноценны, частоту задает OCR2A, а не OCR2B?

Можете тыкнуть на пункт в описании от Microchip (владелец компании  Atmel) или книгу Евстифеева?

 

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Kunzo, даташит, страница 155 Table 18-8. Mode 2. Графа "Top" говорит о том, что будет конец счёта.  Регистры не равноценны. Регистр OCR2A в данном случае работает в 2х качествах -как число, до которого считает счётчик, и как число при  сравнении с которым произойдёт прерывание. Регистр OCR2B только в одном качестве.

Kunzo
Offline
Зарегистрирован: 22.06.2017

dimax пишет:
Kunzo, даташит, страница 155 Table 18-8. Mode 2. Графа "Top" говорит о том, что будет конец счёта.  Регистры не равноценны.
Спасибо, сделал, но в тесте первый отсчет таймера ( dT=796) получается кривым по времени. Что-то сделал  не правильно ?

  volatile int ndx=0;
# define SL 56
  volatile byte LogSequense[SL];
  volatile int LogPeriod[SL];
  volatile bool printed=false, writed=false;
  
uint64_t T1, T2, TA=0, TB=0;
  void setup(void)
  {  
  int i; for(i=0; i<SL; i++) { LogSequense[i]=0xFFFF; LogPeriod[i]=0; }
  Serial.begin(115200);
  Serial.println("Start");
  T1=micros();
  Serial.print(" Start time=");Serial.println((unsigned int)T1);
  setup_timers();
  } 

  void loop(void)
  {
   int i;
    T2=micros();
    if( (writed==true) & (printed==false))
      { 
      Serial.print(" Summ. time=");Serial.print((unsigned int)(T2-T1));Serial.println(" mic"); 
      for(i=0; i<SL; i++) 
          { Serial.print(" i=");    Serial.print(i);
            Serial.print(" IRQn="); Serial.print(LogSequense[i]); 
            Serial.print(" dT=");   Serial.print(LogPeriod[i]);
            Serial.println("");
          }
      printed=true;
      }
  } 


  void setup_timers(void)
  {
    TCCR2B=0;   
    TCNT2=0; 
    TIFR2&=0xff ; 
    TCCR2A=(1<<WGM21);
    OCR2A=200;  
    TIMSK2=(1<<OCIE2A); // комп с OCR2A
    OCR2B=30; 
    TIMSK2|=1<<OCIE1B; //комп с OCR2B
    TCCR2B=(1<<CS22)|(1<<CS20); 
   }
  
  
ISR (TIMER2_COMPB_vect) // прерывание  B выполняется первым  
{   
TB=micros(); 
if( ndx>=SL) {writed=true; return;}  
else;
LogSequense[ndx]=100+ndx; 
LogPeriod[ndx]=TB-TA;
ndx++;
}


ISR (TIMER2_COMPA_vect)// прерывание A выполняется вторым
{
TA=micros();
if( ndx>=SL) {writed=true; return;}  
else;
LogSequense[ndx]=200+ndx; 
LogPeriod[ndx]=TA-TB;
ndx++;
}

 

Start
 Start time=216
 Summ. time=45600 mic
 
i=0 IRQn=100 dT=796
 i=1 IRQn=201 dT=1360
 i=2 IRQn=102 dT=248
 i=3 IRQn=203 dT=1360
 i=4 IRQn=104 dT=248
 i=5 IRQn=205 dT=1360
 i=6 IRQn=106 dT=248
 i=7 IRQn=207 dT=1360
 i=8 IRQn=108 dT=248
 i=9 IRQn=209 dT=1360
 i=10 IRQn=110 dT=248
 i=11 IRQn=211 dT=1360
 i=12 IRQn=112 dT=248
 i=13 IRQn=213 dT=1360
 i=14 IRQn=114 dT=248
 i=15 IRQn=215 dT=1360
 i=16 IRQn=116 dT=248
 i=17 IRQn=217 dT=1360
 i=18 IRQn=118 dT=248
 i=19 IRQn=219 dT=1364
 i=20 IRQn=120 dT=244
 i=21 IRQn=221 dT=1360
 i=22 IRQn=122 dT=248
 i=23 IRQn=223 dT=1360
 i=24 IRQn=124 dT=248
 i=25 IRQn=225 dT=1360
 i=26 IRQn=126 dT=248
 i=27 IRQn=227 dT=1360
 i=28 IRQn=128 dT=248
 i=29 IRQn=229 dT=1360
 i=30 IRQn=130 dT=248
 i=31 IRQn=231 dT=1360
 i=32 IRQn=132 dT=248
 i=33 IRQn=233 dT=1360
 i=34 IRQn=134 dT=248
 i=35 IRQn=235 dT=1360
 i=36 IRQn=136 dT=248
 i=37 IRQn=237 dT=1360
 i=38 IRQn=138 dT=248
 i=39 IRQn=239 dT=1360
 i=40 IRQn=140 dT=248
 i=41 IRQn=241 dT=1360
 i=42 IRQn=142 dT=248
 i=43 IRQn=243 dT=1360
 i=44 IRQn=144 dT=248
 i=45 IRQn=245 dT=1360
 i=46 IRQn=146 dT=248
 i=47 IRQn=247 dT=1360
 i=48 IRQn=148 dT=248
 i=49 IRQn=249 dT=1360
 i=50 IRQn=150 dT=248
 i=51 IRQn=251 dT=1360
 i=52 IRQn=152 dT=248
 i=53 IRQn=253 dT=1360
 i=54 IRQn=154 dT=248
 i=55 IRQn=255 dT=1360

а если добавить задержку, то первый отсчет вообще печатается ка отрицательный

Serial.print(" Start time=");Serial.println((unsigned int)T1);
  delay(2000); //-----------------------------------------------------------------------------------
  setup_timers();
  

 

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Kunzo, вы недостаточно ясно описали что не так. Из того, что бросается в глаза -TIFR лучше обнулять после настройки TIMSK, ибо после его конфигурации сразу может сработать прерывание. Ну и можно на всякий случай перед всей этой конструкцией запретить прерывания. Т.е. запрет, потом настройка таймера, потом  разрешение прерываний, и в конце всей очереди T1=micros.

Kunzo
Offline
Зарегистрирован: 22.06.2017

dimax пишет:
Kunzo, вы недостаточно ясно описали что не так. Из того, что бросается в глаза -TIFR лучше обнулять после настройки TIMSK, ибо после его конфигурации сразу может сработать прерывание. Ну и можно на всякий случай перед всей этой конструкцией запретить прерывания. Т.е. запрет, потом настройка таймера, потом  разрешение прерываний, и в конце всей очереди T1=micros.

Изменил все указанное, только  местоположение T1=micros(); менять не стал тк он привязан к Serial.print(), который распечатывает его значение.  Если выполнить  Serial.print() после инициализации таймера, то возможно   вывод в COM сдвинет (предположение) по времени входы в прерывания компараторов A и  B ?

 volatile int ndx=0;
# define SL 56
  volatile byte LogSequense[SL];
  volatile int LogPeriod[SL];
  volatile bool printed=false, writed=false;
  
uint64_t T1, T2, TA=0, TB=0;
  void setup(void)
  {  
  int i; for(i=0; i<SL; i++) { LogSequense[i]=0xFFFF; LogPeriod[i]=0; }
  Serial.begin(115200);
  Serial.println("Start");
  T1=micros();
  Serial.print(" Start time=");Serial.println((unsigned int)T1);
 
  noInterrupts();  setup_timers(); interrupts();
 
  } 

  void loop(void)
  {
   int i;
    T2=micros();
    if( (writed==true) & (printed==false))
      { 
      Serial.print(" Summ. time=");Serial.print((unsigned int)(T2-T1));Serial.println(" mic"); 
      for(i=0; i<SL; i++) 
          { Serial.print(" i=");    Serial.print(i);
            Serial.print(" IRQn="); Serial.print(LogSequense[i]); 
            Serial.print(" dT=");   Serial.print(LogPeriod[i]);
            Serial.println("");
          }
      printed=true;
      }
  } 


  void setup_timers(void)
  {
    TCCR2B=0;   
    TCNT2=0; 
    //-------------------------------------- TIFR2&=0xff ; 
    TCCR2A=(1<<WGM21);
    OCR2A=200;  
    TIMSK2=(1<<OCIE2A); // комп с OCR2A
    OCR2B=30; 
    TIMSK2|=1<<OCIE1B; //комп с OCR2B
    TCCR2B=(1<<CS22)|(1<<CS20); 
    TIFR2&=0xff ; 
   }
  
  
ISR (TIMER2_COMPB_vect) // прерывание  B выполняется первым  
{   
TB=micros(); 
if( ndx>=SL) {writed=true; return;}  
else;
LogSequense[ndx]=100+ndx; 
LogPeriod[ndx]=TB-TA;
ndx++;
}


ISR (TIMER2_COMPA_vect)// прерывание A выполняется вторым
{
TA=micros();
if( ndx>=SL) {writed=true; return;}  
else;
LogSequense[ndx]=200+ndx; 
LogPeriod[ndx]=TA-TB;
ndx++;
}

dimax пишет:
Kunzo, вы недостаточно ясно описали что не так.
Насторожило что первый отсчет таймера приходит через 796 мксек, а не 248 как остальные. По алгоритму вообще ожидал ноль тк TA=TB=0;           Где-то прячется ошибка.

Start
 Start time=216
 Summ. time=45600 mic
 i=0 IRQn=100 dT=796 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 i=1 IRQn=201 dT=1360
 i=2 IRQn=102 dT=248
 i=3 IRQn=203 dT=1360
 i=4 IRQn=104 dT=248
 i=5 IRQn=205 dT=1360
 i=6 IRQn=106 dT=248
 i=7 IRQn=207 dT=1360
 i=8 IRQn=108 dT=248

А если добавить задержку, то результат еще более изменяется, я писал об этом выше

Serial.print(" Start time=");Serial.println((unsigned int)T1);
  delay(2000); //-----------------------------------------------------------------------------------
  setup_timers();

 

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Kunzo, не понимаю что вам не понятно..  У вас первым срабатывает прерывание COMPB, и в нём вы уже делаете операцию TB-TA , а TA на этот момент =0. Т.к. второе прерывание ещё не сработало.   В резульате вы имеете то что запрограммировано -первый отсчёт -это время от глобального старта микрос до прерывания.

Kunzo
Offline
Зарегистрирован: 22.06.2017

dimax пишет:
Kunzo, не понимаю что вам не понятно..  У вас первым срабатывает прерывание COMPB, и в нём вы уже делаете операцию TB-TA
Вы правы, спасибо.

Kunzo
Offline
Зарегистрирован: 22.06.2017

dimax, поскольку мне был нужен еще один таймер, я добавил прерывание от T0.  Инициализировал однотипно  с T2, но  T0 испортил работу прежней программы. 

В чем конфликт не знаю.   МК Atmega328P -  вместо T0 придется использовать T1 ? 

В программе еще будут задействованы библиотечные функции: аппаратный SPI, аппаратный Serial COM1 на время отладки и  аппаратный I2C для LCD.

 volatile int ndx=0;
# define SL 56
  volatile byte LogSequense[SL];
  volatile int LogPeriod[SL];
  volatile bool printed=false, writed=false;
  
uint64_t T1, T2, TA=0, TB=0;
  void setup(void)
  {  
  int i; for(i=0; i<SL; i++) { LogSequense[i]=0xFFFF; LogPeriod[i]=0; }
  Serial.begin(115200);
  Serial.println("Start");
  T1=micros();
  Serial.print(" Start time=");Serial.println((unsigned int)T1);
 
  noInterrupts();  setup_timers(); interrupts();
 
  } 

  void loop(void)
  {
   int i;
    T2=micros();
    if( (writed==true) & (printed==false))
      { 
      Serial.print(" Summ. time=");Serial.print((unsigned int)(T2-T1));Serial.println(" mic"); 
      for(i=0; i<SL; i++) 
          { Serial.print(" i=");    Serial.print(i);
            Serial.print(" IRQn="); Serial.print(LogSequense[i]); 
            Serial.print(" dT=");   Serial.print(LogPeriod[i]);
            Serial.println("");
          }
      printed=true;
      }
  } 


  void setup_timers(void)
  {
    noInterrupts();
 //// timer 2 --------------------------------------
    TCCR2B=0;   
    TCNT2=0; 
    TCCR2A=(1<<WGM21);
    OCR2A=200;  
    TIMSK2=(1<<OCIE2A); // комп с OCR2A
    OCR2B=30; 
    TIMSK2|=1<<OCIE1B; //комп с OCR2B
    TCCR2B=(1<<CS22)|(1<<CS20); 
    TIFR2&=0xff ;
    
 ///// timer 0 ------------------------------------
     TCCR0B=0;   
    TCNT0=0; 
    TCCR0A=(1<<WGM21);
    OCR0A=150;  
    TIMSK0=(1<<OCIE0A); //по комп OCR0A 
    OCR2B=15; 
    TIMSK0|=(1<<OCIE0B); // по комп OCR0B
    TCCR0B=(1<<CS22)|(1<<CS20); 
    TIFR0&=0xff ; 
   
    interrupts();
   }
  
ISR (TIMER0_COMPB_vect){ return;}  // пока пустышка
ISR (TIMER0_COMPA_vect){ return;} // пока пустышка
 
  
ISR (TIMER2_COMPB_vect)   
{   
TB=micros(); 
if( ndx>=SL) {writed=true; return;}  
else;
LogSequense[ndx]=100+ndx; 
LogPeriod[ndx]=TB-TA;
ndx++;
}


ISR (TIMER2_COMPA_vect)
{
TA=micros();
if( ndx>=SL) {writed=true; return;}  
else;
LogSequense[ndx]=200+ndx; 
LogPeriod[ndx]=TA-TB;
ndx++;
}

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Kunzo, Timer0 использует ардуиновские системные функции (миллисы, микросы, и прочее)

Kunzo
Offline
Зарегистрирован: 22.06.2017

Тогда нужно использовать оставшийся T1 если библиотечные функции: аппаратный SPI, аппаратный Serial COM1 на время отладки и  аппаратный I2C для LCD на него не претендуют ?

Кстати,  почему он в datasheet называется 3,4-16 битным  и ATmega328PB устроена также как ATmega328P AU ? 

Цитата:
ATmega328PB   19.   TC1, 3, 4 - 16-bit Timer/Counter ...  © 2017 Microchip Technology Inc. Datasheet Complete 40001906A-page 133