Сбой программного таймера.

Jaeger
Jaeger аватар
Offline
Зарегистрирован: 23.03.2018
Добрый день Форум!
В силу своих скромных возможностей в программировании, не могу понять проблему
сбоя программного таймера. Суть проблемы в том, что по непонятным мне причинам
периодически происходит выполнение условия if, которое в данный момент не должно выполнятся.
Из-за этого период программного таймера становится короче. Это наглядно видно в мониторе порта.
IDE 1.8.5, Arduino Nano, бутлодырь - Оптибут.  Как проблему победить, парни?
  uint32_t  Tim_old;
  volatile uint16_t  isrCnt =0;   //счетчик тиков от таймера2

void setup() {
   
  // перенастройка таймера2 с периодом прерывания около 4 мс
  cli();//stop interrupts
  TCCR2A = 0; 
  TCCR2B = 0;
  TIMSK2 = 0;
  TCCR2B |= ((1<<CS21)|(1<<CS22));        // предделитель на 256 
  TIMSK2 |= (1<<TOIE2);                   // разрешаю прерывание по переполнению T2
  sei();//allow interrupts
    
  Serial.begin(115200);                   // инициализируем порт
  pinMode(LED_BUILTIN, OUTPUT);           // initialize digital pin LED_BUILTIN as an output.
  Tim_old = millis();
}

//*******************************************************
void loop() {
       if (isrCnt >= 300) {
            Serial.print(isrCnt);
            isrCnt = 0;
            Serial.print(" ");                          
            Serial.println(millis()-Tim_old);
            Tim_old = millis();
            digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); //мигание LED
                            }     
}
//********************************************************

   // обработчик прерывания по переполнению T2 4,096 мс
ISR(TIMER2_OVF_vect) {      
    isrCnt++; 
}

Монитор порта: первая ц. значения счетчика прерываний, вторая период в мс.

300 1228
300 1229
300 1229
300 1229
300 1229
256 1048	сбой
300 1229
300 1229
300 1229
300 1228
300 1229
300 1229
300 1229
300 1229
300 1228
300 1229
300 1229
300 1229
300 1229
256 1048	сбой	
300 1229
300 1229
300 1229
300 1228
300 1229
256 1049	сбой
300 1229
300 1228
300 1229
300 1229
300 1229
256 1048	сбой
300 1229
300 1229

 

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Прерывания тонкая вещь.

Например, представьте себе такую ситуацию: после строки 22 Вас прервали (сериал, например, решил чего-то там поделать, он же неблокирующий). Пока он там занимался чем-то посторонним, переменная уже isrCnt "убежала". В итоге, когда Вы ёё собрались печатать в строке 23, она уже совсем не та, что была в строке 22.

Точно придумывать сценарий при котором получилось именно так, как у Вас мне просто неохота, но дело именно в этом - в том, что Вас прервали между проверкой и присвонием нуля. Как-то так. Вас пррвали и Вам от этого поплохело.

Чтобы убедиться в том, что это так и есть (независимо от конкретного сценария) попробуйте заменить свой loop на вот такой. Уверен, что ни одного сбоя Вы не увидите.

void loop() {
  cli();
  if (isrCnt >= 300) {
    const uint16_t cnt = isrCnt;
    isrCnt = 0;
    sei();
    Serial.print(cnt);
    Serial.print(" ");
    Serial.println(millis() - Tim_old);
    Tim_old = millis();
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); //мигание LED
  } else sei();
}

 

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

Jaeger, для корректного вычисления в лупе  if (что-то меняющееся в прерывании)  перед этим вычислением нужно прерывания запретить.

Jaeger
Jaeger аватар
Offline
Зарегистрирован: 23.03.2018
Точно, так и есть ни одного сбоя. Походу "помеха" от Таймера0, потому как сериал я отключал
полностью, а проблема оставалась. И еще, "помеха" пропадала если константа была <= 256.
Спасибо, парни!
GarryC
Offline
Зарегистрирован: 08.08.2016

Ну не могу я понять это желание постоянно запретить прерывания. То есть понятно, что это проще всего, и "работает же" но есть же и правильные способы без запрета.

В классной книге "Art of multirocessing programming" есть немало решений подобных задач.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Если Вы про Хёрлихи и Шэвита, то книга-то конечно неплохая, но не для данной задачи. Задача вырожденная и потому здесь будет как в старом анекдоте:

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

Само же по себе её изучение ... ну, не знаю, профессионалу (или профильному студенту) необходимо, а хоббисту - по-моему излишество, тем более, для своего понимания она требует определённого уровня IT-культуры, которым 99% любителей просто не владеют. Т.е. для того, чтобы эту книгу понять, им потребуется изучить много чего предварительно.

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

GarryC, ну так привели бы хоть один пример, если их немало.  В той же функции millis() первая команда -cli

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

dimax, если хотите посмотреть книгу, "их есть у меня". Только в применении к данной задаче, см. мой комментарий выше.

b707
Offline
Зарегистрирован: 26.05.2017

ЕвгенийП пишет:

dimax, если хотите посмотреть книгу, "их есть у меня".

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

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Только GarryC название привёл немного неправильно. Но, впрочем, да, найти нетрудно.

Кстати, есть вполне приличные книги на эту тему и на русском (в смысле отечественных авторов). Причём многие из них более строгие и точные (более глубокие), чем обсуждаемая.

GarryC
Offline
Зарегистрирован: 08.08.2016

Евгений, а что из русскоязычных посоветуете ?

Особенно, если они более глубокие - для меня и эта оказалась кладезем мудрости, было бы неплохо пополнить.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015