Правильное включение таймеров T2, T3, T4, T5
- Войдите на сайт для отправки комментариев
Здравствуйте! Прошу помощи по работе Таймеров на 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);
}
}
Пункт 2 чуток неверно написал...
В прерывании ISR(TIMER2_OVF_vect) откл T2, вкл T3 и уже в ISR(TIMER3_OVF_vect) зажигаем PORTC_1 и моргаем PORTC_5
Не наблюдаю описания переменных.
И вообще, я даже написал так
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...
Это файл Main_Var.h
Во-первых - volatile утеряно, во-вторых - посмотрите, каково максимальное значение для восьмибитной переменной.
Точно, теперь всё работает!!! Спасибо большое за помощь! А то у меня чуток опыта не хватает, тормоз происходит на "детских ошибках" )
Ну и как count_T2 может стать равен 550 если максимум на что он способен это 255?
Теперь у меня ещё один вопрос по Таймерам... Допустим написал такую функцию:
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);. Я правильно понимаю это всё?
Спасибо!
1) Регистры TCNT1H и TCNT1L остаются в тех значениях, до которых они досчитались, верно? Теперь при следующем включении Таймера Т1 отсчёт начинается с этих значений? Тогда мне нужно принудительно обнулять эти регистры?
Положим, что значение регистров после остановки таймера можно проверить и убедиться насчёт того, в каком положении они застывают.
Однако, в ходе экспериментов в Correct Phase PWM mode у меня сложилось впечатление, что при запуске таймера счёт всегда начинается с BOTTOM. Но никаких документальных подтверждений этому факту я пока не видел.
Строго говоря - поведение определяется тем состоянием TIMSKn, которое было до момента, когда не был выполнен его сброс.
А так - всё звучит логично.
Строго говоря - поведение определяется тем состоянием TIMSKn, которое было до момента, когда не был выполнен его сброс.
Немного не понял Вашу мысль, можно по-подробнее... Моя в простом понимании TOV1 = 1, будет обработчик, TOV1 = 0, обработчика не будет.
Мысль проста - если где-то и когда-то (вне функции "остановки") TOV был разрешён (или запрещён), а TIMSKn его не замаскировало (не выполнено TIMSKn = 0x00), то его состояние определяется тем моментом, когда последний раз изменялся регистр TIMSK.
Провёл эксперимент с о счётчиком таймера:
#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); } }Начинает идти с того момента, на котором остановили тактирование: