Правильное включение таймеров T2, T3, T4, T5

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

Здравствуйте! Прошу помощи по работе Таймеров на Arduino Mega. Вопрос в том, что могу  ли я, например, в обработчике прерывания переполнения Таймера_2 отключить работу Т2 и включить в работу Таймер Т3? Хочу, чтобы код работал следующим образом: циклически цепочкой работают по очереди таймеры 2, 3, 4, 5.

1) Таймер_2 работает, моргает PORTC_4 пару раз, отображает светодиодом на пине PORTC_0 свою работу.

2) В прерывании переполнения Таймер_2 выключаем  TCCR2B = 0x00; TIMSK2 = 0x00; , гасим светодиод PORTC_0. В этом же прерывании включаем Таймер_3, зажигаем светодиод PORTC_1, отображающий работу Таймера_3, моргаем уже PORTC_5...

3) Далее по кругу T3 --> T4; T4 --> T5; T5 --> T2; .......

В моём коде работает только Таймер Т2,(светится только диод PORTC_0, моргает только PORTC_4) я так понимаю Т3 не инициализируется в обработке прерывания Т2. 

Прилагаю код:

#include "Main_Var.h"

ISR(TIMER2_OVF_vect)
{// вкл диод индикатирующий работающий Таймер_2
  PORTC &= ~(1 << 0);    
  count_led++;
// ********* блок моргания PORTC_4 ********* 
  if (count_led == 183)
     count_led = 0;
  if (count_led < 90)
    {PORTC |= (1 << 4);} 
  else 
    PORTC &= ~(1 << 4);
// блок отслеживания переполнения переменной count_T2, 
// выключения Таймера_2 и 
// последующего включения Таймера_3
  count_T2++;
  if (count_T2 == 550)  // 
    {count_T2 = 0;
     TCCR3B = 0x01;   // clk/1
     TIMSK3 = 0x01;   // вкл Таймер_3
     TCCR2B = 0x00;   // откл
     TIMSK2 = 0x00;   // Таймер_2
     count_led = 0;
// ВЫКЛ диод отображающий работающий Таймер_2     
     PORTC |= (1 << 0);           
    }
 }


ISR(TIMER3_OVF_vect)
{// вкл диод индикатирующий работающий Таймер_3
  PORTC &= ~(1 << 1);
  count_led++;
// ********* блок моргания PORTC_5 ********* 
  if (count_led == 183)
     count_led = 0;
  if (count_led < 90)
    PORTC |= (1 << 5);
  else
    PORTC &= ~(1 << 5);
// блок отслеживания и выключения Таймера_3 и 
// последующего включения Таймера_4    
  count_T3++;
  if (count_T3 == 550)
    {count_T3 = 0;
     TCCR4B = 0x01;
     TIMSK4 = 0x01;   
     TCCR3B = 0x00;    
     TIMSK3 = 0x00;
     count_led = 0;
// ВЫКЛ диод индикатирующий работающий Таймер_3
     PORTC |= (1 << 1);
    }
}

ISR(TIMER4_OVF_vect)
 {PORTC &= ~(1 << 2);
  count_led++;
  if (count_led == 183)
     count_led = 0;
  if (count_led < 90)
    PORTC |= (1 << 6);
  else PORTC &= ~(1 << 6);
  count_T4++;
  if (count_T4 == 550)
    {count_T4 = 0;
     TCCR5B = 0x01;
     TIMSK5 = 0x01;  
     TCCR4B = 0x00;
     TIMSK4 = 0x00;
     count_led = 0;
     PORTC |= (1 << 2);
    }
}

ISR(TIMER5_OVF_vect)
 {PORTC &= ~(1 << 3);
  count_led++;
  if (count_led == 183)
     count_led = 0;
  if (count_led < 90)
    PORTC |= (1 << 7);
  else PORTC &= ~(1 << 7);
  count_T5++;
  if (count_T5 == 550)
    {count_T5 = 0;
     TCCR2B = 0x06;
     TIMSK2 = 0x01; 
     TCCR5B = 0x00;
     TIMSK5 = 0x00;
     count_led = 0;
     PORTC |= (1 << 3);
    }
}

 

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

Пункт 2 чуток неверно написал...

В прерывании ISR(TIMER2_OVF_vect) откл T2, вкл T3 и уже в ISR(TIMER3_OVF_vect) зажигаем PORTC_1 и моргаем PORTC_5

 

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Не наблюдаю описания переменных.

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

И вообще, я даже написал так

ISR(TIMER2_OVF_vect)
{// вкл диод индикатирующий работающий Таймер_2
  PORTC &= ~(1 << 0);    
  count_T2++;
  if (count_T2 == 550)  // 
    {count_T2 = 0;
     TCCR3B = 0x01;   // clk/1
     TIMSK3 = 0x01;   // вкл Таймер_3
     TCCR2B = 0x00;   // откл
     TIMSK2 = 0x00;   // Таймер_2
     count_led = 0;
// ВЫКЛ диод отображающий работающий Таймер_2     
     PORTC |= (1 << 0);           
    }
 }

Далее должен работать Таймер Т3, но этого не происходит - горит диод только PORTC_0, отображающий работу Таймера Т2...

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019
#ifndef MAIN_VAR_H_
#define MAIN_VAR_H_

#define PIN_LED 7

unsigned char count_led, count_T2, count_T3, count_T4, count_T5;

#endif 

Это файл Main_Var.h

 

 

sadman41
Онлайн
Зарегистрирован: 19.10.2016

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

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

Точно, теперь всё работает!!! Спасибо большое за помощь! А то у меня чуток опыта не хватает, тормоз происходит на "детских ошибках" )

 

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

AlexBajdin59rus пишет:

unsigned char count_led, count_T2, count_T3, count_T4, count_T5;
...
// блок отслеживания переполнения переменной count_T2, 
// выключения Таймера_2 и 
// последующего включения Таймера_3 
count_T2++;
 if (count_T2 == 550) 

Ну и как count_T2 может стать равен 550 если максимум на что он способен это 255?

 

 

 

 

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

Теперь у меня ещё один вопрос по Таймерам... Допустим написал такую функцию:

void Stop_All_Timer()
{
  cli();
  TCCR1B = 0x00;
  TIMSK1 = 0x00;
  TIFR1 |= (1 << 0); 
  TCCR2B = 0x00;
  TIMSK2 = 0x00;
  TIFR2 |= (1 << 0);
  TCCR3B = 0x00;
  TIMSK3 = 0x00;
  TIFR3 |= (1 << 0);
  TCCR4B = 0x00;
  TIMSK4 = 0x00;
  TIFR4 |= (1 << 0);
  TCCR5B = 0x00;
  TIMSK5 = 0x00;
  TIFR5 |= (1 << 0);
  sei();
}

Допустим, моя же ситуация с моим кодом, приведённым выше, один таймер выкл/вкл другой таймер.

Насколько я понимаю, командой TCCR1B = 0x00; я отключаю тактирование Таймера Т1, --> Т1 останавливается.

1) Регистры TCNT1H и TCNT1L остаются в тех значениях, до которых они досчитались, верно? Теперь при следующем включении Таймера Т1 отсчёт начинается с этих значений? Тогда мне нужно принудительно обнулять эти регистры?

Командой TIMSK1 = 0x00; я отключаю возможность вызова обработчика прерывания по переполнению Т1. 

2) Если я не выполняю эту команду (TIMSK1 = 0x00;) И, допустим, во время выполнения какого-нибудь обработчика прерывания (например ISR(TIMER3_COMPA_vect)) установился флаг переполнения Таймера Т1, то после выполнения обработчика ISR(TIMER3_COMPA_vect) запустится обработчик ISR(TIMER1_OVF_vect). Тогда, чтобы этого не произошло, я принудительно сбрасываю флаг TOV1 командой  TIFR1 |= (1 << 0);. Я правильно понимаю это всё?

Спасибо!

sadman41
Онлайн
Зарегистрирован: 19.10.2016

AlexBajdin59rus пишет:

1) Регистры TCNT1H и TCNT1L остаются в тех значениях, до которых они досчитались, верно? Теперь при следующем включении Таймера Т1 отсчёт начинается с этих значений? Тогда мне нужно принудительно обнулять эти регистры?

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

Однако, в ходе экспериментов в Correct Phase PWM mode у меня сложилось впечатление, что при запуске таймера счёт всегда начинается с BOTTOM. Но никаких документальных подтверждений этому факту я пока не видел.

AlexBajdin59rus пишет:

2) Если я не выполняю эту команду (TIMSK1 = 0x00;) И, допустим, во время выполнения какого-нибудь обработчика прерывания (например ISR(TIMER3_COMPA_vect)) установился флаг переполнения Таймера Т1, то после выполнения обработчика ISR(TIMER3_COMPA_vect) запустится обработчик ISR(TIMER1_OVF_vect). Тогда, чтобы этого не произошло, я принудительно сбрасываю флаг TOV1 командой  TIFR1 |= (1 << 0);. Я правильно понимаю это всё?

Строго говоря - поведение определяется тем состоянием TIMSKn, которое было до момента, когда не был выполнен его сброс.  

А так - всё звучит логично.

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

sadman41 пишет:

Строго говоря - поведение определяется тем состоянием TIMSKn, которое было до момента, когда не был выполнен его сброс.  

Немного не понял Вашу мысль, можно по-подробнее... Моя в простом понимании TOV1 = 1, будет обработчик, TOV1 = 0, обработчика не будет.

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Мысль проста - если где-то и когда-то (вне функции "остановки") TOV был разрешён (или запрещён), а TIMSKn его не замаскировало (не выполнено TIMSKn = 0x00), то его состояние определяется тем моментом, когда последний раз изменялся регистр TIMSK.

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Провёл эксперимент с о счётчиком таймера:

#include <util/atomic.h>

// TCNT1 growed up to 0xFFFF on first run (OCR1x update point on mode #10) if not defined
#define DROP_TCNT_AT_FIRST_RUN

const uint16_t timerTop = 2000;            
const uint16_t timerDuty = timerTop / 4;   // PWM duty ~25%
const uint32_t timerMaxDelay = 300;

volatile uint8_t tcntCountdown = false, tcntOVF = false, tcntCOMPA = false;

// prescaler /1024
#define TIMER_START ((1 << WGM13) | (1 << CS12) | (1 << CS10))
#define TIMER_STOP  0x00

ISR(TIMER1_OVF_vect) {
  // TCNT1: BOTTOM -> TOP(OCR1A)
  tcntCountdown = false;
  tcntOVF = true;
}
ISR(TIMER1_COMPA_vect) {
  // TCNT1: TOP(OCR1A) -> BOTTOM
  tcntCountdown = true;
  tcntCOMPA = true;
}

void setup() {
  Serial.begin(115200);
  randomSeed(analogRead(A0));
  random(timerMaxDelay); // dry shoot need on generation start

  pinMode(10, OUTPUT);
  TCCR1A = TCCR1B = 0x00;
  // Channel A, pin 9, off
  // Channel B, pin 10, on
  // Clear OC1B on compare match when up-counting. Set OC1B on compare match when down counting.
  TCCR1A |= (0 << COM1A1) | (0 << COM1A0) | (1 << COM1B1) | (0 << COM1B0);

  // Mode #9: PWM, phase and frequency correct, TOP = OCR1A
  //TCCR1A |= (1 << WGM10);
  // Mode #10: PWM, phase correct, TOP = OCR1A
  TCCR1A |= (1 << WGM11) | (1 << WGM10);

  // Allow OVF and COMPA interrupts
  TIMSK1 = (1 << TOIE1) | (1 << OCIE1A);
  
  // Set Frequency and duty
  OCR1A = timerTop;
  OCR1B = timerDuty;

#if defined(DROP_TCNT_AT_FIRST_RUN)
  TCNT1 = 0x00;
#endif
  Serial.println("ms\tTCNT(S)\tTCNT(F)\tDir\tOVF\tCOMPA");
}


void loop() {
  uint16_t timerCNTOnStart, timerCNTOnStop;
  uint32_t timerDelay = 15;
  //uint32_t timerDelay = random(timerMaxDelay);

  Serial.print(timerDelay); Serial.print("\t");
  TCCR1B = TIMER_START;
  delay(1);
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
    timerCNTOnStart = TCNT1;
  }
  delay(timerDelay);
  TCCR1B = TIMER_STOP;
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
    timerCNTOnStop = TCNT1;
  }

  Serial.print(timerCNTOnStart); Serial.print("\t"); Serial.print(timerCNTOnStop); Serial.print("\t"); Serial.print(tcntCountdown ? 'v' : '^');
  Serial.print("\t"); Serial.print(tcntOVF ? '*' : ' '); Serial.print("\t"); Serial.print(tcntCOMPA ? '*' : ' ');
  Serial.println();
  if (tcntOVF) {
    tcntOVF = false;
    delay(2000);
  }
  if (tcntCOMPA) {
    tcntCOMPA = false;
    delay(2000);
  }
}

Начинает идти с того момента, на котором остановили тактирование:

ms	TCNT(S)	TCNT(F)	Dir	OVF	COMPA
15	15	249	^	*	 
15	265	499	^	 	 
15	515	749	^	 	 
15	765	952	v	 	*
15	936	702	v	 	 
15	686	452	v	 	 
15	436	202	v	 	 
15	186	49	^	*	 
15	64	299	^	 	 
15	315	550	^	 	 
15	565	800	^	 	 
15	816	902	v	 	*
15	886	652	v	 	 
15	636	401	v	 	 
15	386	151	v	 	 
15	135	99	^	*	 
15	115	349	^	 	 
15	365	600	^	 	 
15	615	850	^	 	 
15	866	852	v	 	*