Запретить прерывание внутри функции обработки прерывания?

ustas
Offline
Зарегистрирован: 12.03.2012

Есть некоторая система, построена на mega

Есть два обработчика прерывания - один работает по таймеру, второй - "слушает" пин.

Оба прерывания нужны, оба прерывания важны.

Первое прерывание используется для "графики", второе - для приема данных по радиоканалу.

Сейчас работает, но, когда приходит радиосигнал - есть нарушения в графике (помаргивания и т.п.) - очевидно, что во время отработки прерывания по таймеру возникло аппаратное прерывание и это нарушило "тайминги" и динамическая индикация отрабатывает с косяками.

Попробовал сделать так:

ISR(TIMER1_COMPA_vect)          
{
  noInterrupts();
				// ..............
				// что-то тут делаем
				// ..............
  interrupts();
}

Не сработало - вижу те же "помаргивания". Как обойти?

Adno
Offline
Зарегистрирован: 21.09.2012

Лучше откладывать обработку, а не запрещать - тогда контролируешь ситуацию, можешь отложить обработку.
Используя флаги активности прерываний, в каждом обработчике знаешь активно ли другое, и просто делаешь выход. Вряд ли это поможет от дерганий.

Но с графикой ты попал, попробуй посчитать частоты расвертки. Может выйдешь на 21-ый кадр 8=) Глаз вещь темная, исследованию не подлежит. Порой такое увидишь!

Adno
Offline
Зарегистрирован: 21.09.2012

А на графику лучше отдельный камень иметь, во!

__Alexander
Offline
Зарегистрирован: 24.10.2012

при выполнении любого прерывания, все остальные автоматически отключаются. даже если оно и произойдет внутри другого, то поставиться лишь флаг в соотв. регистре. а уже после выхода из прерывания, провериться флаг и выполниться (если было несколько разных прерываний, то все, согласно векторов). можете попробовать разве что сбросить флаг другого прерывания. как правило сброс осуществляется записью единицы, т.е. флаг наличия прерывания единица, для сброса в эту единицу надо записать единицу.

флаг регистров внешних прерываний называется EIFR. пишите в него просто 0xFF и пробуйте.

ISR(TIMER1_COMPA_vect)          
{

				// ..............
				// что-то тут делаем
				// ..............
EIFR = 0xFF;
}

 

Evgen
Evgen аватар
Offline
Зарегистрирован: 10.06.2011

Скорей всего внешнее прерывание, которое обрабатывает прием данных по радио каналу, выполняется слишком долго. Как уже выше писали, при выполнении одного обработчика прерывания все остальные запрещаются. Вот поэтому динамическая индикация и подвисает во время приема данных. Что делать? Уменьшать время выполнения обработчика на прием данных.

ustas
Offline
Зарегистрирован: 12.03.2012

__Alexander, спасибо! Попробую. 

Проблема, похоже, как раз в том, что при работе внешнего прерывания "смещается" по времени исполнение прерывания по таймеру, что и приводит к "визуальным эффектам".

Evgen, да, похоже так и есть, но тут я упираюсь работу с модулем nrf24l01 - и ускорить получение данных от него я не могу :(

ustas
Offline
Зарегистрирован: 12.03.2012

__Alexander пишет:

флаг регистров внешних прерываний называется EIFR. пишите в него просто 0xFF и пробуйте.

ISR(TIMER1_COMPA_vect)          
{

				// ..............
				// что-то тут делаем
				// ..............
EIFR = 0xFF;
}

 

Не помогло... может, лучше будет проделать аналогичную манипуляцию с флагом прерывания по таймеру? __Alexander, подскажите, какой это флаг?

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017
bool FlagOtscheta=false;
unsigned long TimeOtscheta;

void setup() {
Serial.begin(9600);
pinMode(10,OUTPUT);   
attachInterrupt(0, bos, RISING);
}


void bos(){
digitalWrite(10, HIGH);
FlagOtscheta=true;
TimeOtscheta=millis();
}


void loop() {
  
if (FlagOtscheta && millis()-TimeOtscheta>=3000){
FlagOtscheta=false;
digitalWrite(10,LOW);
}


}

Почему не происходит выполнение кода в loop ?

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

А где INPUT_PULLUP или INPUT? И опять Вы забыли про volatile и ATOMIC_BLOCK.

AlexanderNO
Offline
Зарегистрирован: 08.11.2018

А что это за процесс? Чему равна частота?

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

sadman41 пишет:

А где INPUT_PULLUP или INPUT? И опять Вы забыли про volatile и ATOMIC_BLOCK.

Про volatile  поняла. Будет volatile bool FlagOtscheta=false;

INPUT_PULLUP мне не нужно наверное, так как у меня есть внешний подтягивающий резистор.

Про ATOMIC_BLOCK не знаю ничего.

Как я понимаю, все дело в таймере....?

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

Вспомните свой вопрос про тахометр. Там и тема ATOMIC и volatile для long переменных были разобраны.

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017
Так как прерывания запрещены внутри обработчика и из-за того, что Arduino IDE использует прерывания для чтения и записи данных через Serial порт, а также для увеличения счетчика, использующего millis и delay — не пытайтесь использовать эти функции внутри обработчика прерываний.

Разобралась....

Так правильно будет?

volatile bool FlagOtscheta=false;
volatile bool FlagTimer=false;

unsigned long TimeOtscheta;

void setup() {
Serial.begin(9600);
pinMode(10,OUTPUT);  
attachInterrupt(0, bos, RISING);
}

void bos(){
FlagTimer=true;
}



void loop() {

if (FlagTimer){
FlagTimer=false;
digitalWrite(10, HIGH);
TimeOtscheta=millis(); 
FlagOtscheta=true;
}

  
if (FlagOtscheta && millis()-TimeOtscheta>=3000){
FlagOtscheta=false;
digitalWrite(10,LOW);
}


}

 

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

Формально - правильно, но избыточно и не сильно надежно.

millis() в ISR не тикает - это верно. Но это не может помешать считать в обработчике его последнее значение для того, чтобы понять, на какой миллисекунде прервался ход выполнения основного цикла.

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017
volatile bool FlagOtscheta=false;
volatile bool FlagTimer=false;

unsigned long TimeOtscheta;

void setup() {
Serial.begin(9600);
pinMode(10,OUTPUT);  
attachInterrupt(0, bos, RISING);
}

void bos(){
FlagTimer=true;
}



void loop() {

if (FlagTimer){
FlagTimer=false;
Serial.println (millis());
digitalWrite(10, HIGH);
TimeOtscheta=millis(); 
FlagOtscheta=true;
}

  
if (FlagOtscheta && millis()-TimeOtscheta>=3000){
FlagOtscheta=false;
Serial.println (millis());
digitalWrite(10,LOW);
}


}

Вроде правильно таймер работает, во время выключается

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

До тех пор, пока ISR на середине блокирующей функции не будет вызван. Тогда ваша релюшка будет клацать с отставанием. Найдите тему про тахометр, не ленитесь.

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017
volatile bool FlagOtscheta=false;
volatile bool FlagTimer=false;

unsigned long TimeOtscheta;

void setup() {
Serial.begin(9600);
pinMode(10,OUTPUT);  
attachInterrupt(0, bos, RISING);
}

void bos(){
FlagTimer=true;
}



void loop() {

if (FlagTimer){
noInterrupts();//Запретили прерывание
FlagTimer=false;
Serial.println (millis());
digitalWrite(10, HIGH);
TimeOtscheta=millis(); 
FlagOtscheta=true;
interrupts();//Разрешили прерывание

}

  
if (FlagOtscheta && millis()-TimeOtscheta>=3000){
FlagOtscheta=false;
Serial.println (millis());
digitalWrite(10,LOW);
}


}

Вот так?))

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

https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/

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

#include <util/atomic.h>

volatile uint8_t isrIsRised;  // = 0x00 (false) on default
volatile uint32_t isrRisingTime; // = 0x00 on default
const uint8_t interruptPin = 2;
const uint8_t ledPin = 10;

void bos() {
  isrRisingTime = millis();
  digitalWrite(ledPin, HIGH);
  isrIsRised = true;
}

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  pinMode(interruptPin, INPUT);
  attachInterrupt(digitalPinToInterrupt(interruptPin), bos, RISING);
}

void loop() {

  uint32_t ledOnTime;

  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
    ledOnTime = isrRisingTime;
  }

  if (isrIsRised && millis() - ledOnTime >= 3000) {
    isrIsRised = false;
    digitalWrite(ledPin, LOW);
    Serial.println (millis());
  }
}

 

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Спасибо. Буду изучать.

 

Еще вопрос. Подскажите как осуществить следующий алгоритм:

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

 

https://youtu.be/jAFOeNKw3ww

 

volatile bool FlagOtscheta=false;
volatile bool FlagRIS=false;
volatile bool FlagOnf=false;
unsigned long TimeOtscheta;

void OffOn(){
FlagRIS=true;
}

void setup() {
Serial.begin(19200);
pinMode(10,OUTPUT);  
pinMode(2,INPUT);  
attachInterrupt(0,OffOn,RISING);
}










void loop() {


if (FlagRIS && !FlagOnf){
FlagRIS=false;
FlagOnf=true;
Serial.println ("Otschet");
digitalWrite(10, HIGH);
TimeOtscheta=millis(); 
FlagOtscheta=true;
}

if (FlagOtscheta && millis()-TimeOtscheta>=3000){
FlagOtscheta=false;
Serial.println ("Off");
digitalWrite(10,LOW);
}



}

 

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

А так же:

attachInterrupt(0,OnOff,FALLING );
attachInterrupt(0,OffOn,RISING);

Можно ли использовать два раных условия в прерываниях?

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

Irinka пишет:

А так же:

attachInterrupt(0,OnOff,FALLING );
attachInterrupt(0,OffOn,RISING);

Можно ли использовать два раных условия в прерываниях?

void bos() {
  if (LOW == digitalRead(interruptPin)) {
    // FALLING: CHANGE from HIGH to LOW
    digitalWrite(ledPin, LOW);
    
  } else {
    // RISING: CHANGE from LOW to HIGH 
    digitalWrite(ledPin, HIGH);    

  }

  isrRisingTime = millis();
  isrIsRised = true;
}

void setup() {
...
  attachInterrupt(digitalPinToInterrupt(interruptPin), bos, CHANGE);
...
}

 

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Большое Спасибо

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Частично не в тему...

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

Переход на пине D2 с LOW на HIGH отслеживаю attachInterrupt(0,bos,RISING);

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

Как бороться?

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

Непременно будут. Особенно на прерывании ))

Ставьте или какой-нить триггер Шмитта или делайте программный антидребезг (без прерываний). Хотя, наверное, если через оптопару-резистор заводит - это несколько подправит ситуацию. Впрочем, я тоже с интересом почитаю, что Вам  умные люди посоветуют.

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Дело еще в том, чтобы для машины это переключение было незаметным, чтобы она продолжала работать, так что задержка на проверку дребезга нужна минимальная.

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017
byte P10 = (1 << 2);//10
byte P2 = (1 << 2);//2
#define D10_OUTPUT    (DDRB  |=  P10)
#define D2_INPUT      (DDRD  &= ~P2)
#define D10_HIGH    (PORTB |=  P10)
#define D10_LOW     (PORTB &= ~P10)
#define D2_READ     (PIND & P2)

volatile bool FlagPrep=false;
volatile bool FlagProv=false;
volatile bool FlagOtscheta=false;
volatile bool FlagRp=false;
unsigned long TimePrer;
unsigned long TimeOtscheta;


void setup() {
Serial.begin(19200);
D10_OUTPUT;
D2_INPUT;
attachInterrupt(0,preruv,RISING);
}

void preruv(){
FlagPrep=true; 
detachInterrupt(0);  
}



void loop() {

if (FlagPrep){
FlagPrep=false; 
TimePrer=millis();
FlagProv=true;
}

if (FlagProv && millis()-TimePrer>=100 && D2_READ){
FlagProv=false; 
D10_HIGH;
Serial.println("Otschet");
FlagOtscheta=true;
TimeOtscheta=millis();
}else{
attachInterrupt (0,preruv,RISING); 
}

if (FlagOtscheta && millis()-TimeOtscheta>=3000){
FlagOtscheta=false;
D10_LOW;
Serial.println("Stop");
TimeOtscheta=millis();
FlagRp=true;
}
if (FlagRp && millis()-TimeOtscheta>=300){
FlagRp=false;
attachInterrupt (0,preruv,RISING); 
}


}

Как то так...только, как указано выше, избыточно...)

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

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

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

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

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

Без разницы. Прерывание тут - оверкилл.

SLKH
Offline
Зарегистрирован: 17.08.2015

Irinka пишет:

Частично не в тему...

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

Переход на пине D2 с LOW на HIGH отслеживаю attachInterrupt(0,bos,RISING);

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

Как бороться?

Эмм... а в чем смысл применения оптрона без гальванической развязки входа и выхода? чем полученный трёхполюсник лучше одинокого транзистора?

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017
void loop() {
if (!D2_READ){ 
if (!FlagD2off && !D2read){
FlagD2off=true; 
D2read=true;
}
}else{
if (FlagD2off){
delay(100);
if (D2_READ){
FlagD2off=false;
D10_HIGH;
Serial.println("Otschet");
FlagOtscheta=true;
TimeOtscheta=millis();
}
}
}

if (FlagOtscheta && millis()-TimeOtscheta>=3000){
FlagOtscheta=false;
D10_LOW;
Serial.println("Stop");
TimeOtscheta=millis();
delay(100);
D2read=false;
}
}

Без прерывания

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Или так, места занимает меньше:

byte a=0;

void loop() {
if (!D2_READ){ 
if (a==0)a=1;
}else{
if (a==1){
delay(100);
if (D2_READ){
a=2;
D10_HIGH;
Serial.println("Otschet");
TimeOtscheta=millis();
}
}
}

if (a==2 && millis()-TimeOtscheta>=3000){
a=3;
D10_LOW;
Serial.println("Stop");
delay(100);
a=0;
}

}

 

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

delay() можете сами поменять на неблокирующий код. Я там дырки в нумерации шагов оставил.

void loop() {
  uint8_t systemState = 0x00;

  while (true) {
    switch (systemState) {
      // Start state machine
      case 0x00:
        digitalWrite(10, LOW);
        break;

      // Wait for button press
      case 0x01:
        if (LOW == digitalRead(2)) {
          systemState = 0x02;
        }
        break;

      // Make dumb debounce
      case 0x02:
        delay(100);
        systemState = (LOW == digitalRead(2)) ? 0x04 : 0x01;
        break;

      // Wait for button release
      case 0x04:
        if (HIGH == digitalRead(2)) {
          digitalWrite(10, HIGH);
          systemState = 0x05;
        }
        break;

      // Make pause before restart state machine
      case 0x06:
        delay(3000);
        systemState = 0x00;
        break;
    } // switch (systemState) 
  } // while (true)
} // loop

 

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Не работает. Но смысл я поняла. Спасибо.

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017
void loop() {
while (true) {

  
switch (systemState) {
case 0x00:
if (!digitalRead(2))  systemState = 0x01;
break;

case 0x01:
if (digitalRead(2)) {
delay(100);
if (digitalRead(2)) {
digitalWrite(10, HIGH);
Serial.println("Otschet");
TimeOtscheta=millis();
systemState = 0x02;
}
}
break;




case 0x02:
if (millis()-TimeOtscheta>=3000){
D10_LOW;
Serial.println("Stop");
TimeOtscheta=millis();
delay(100);
systemState = 0x00;
}
break;
 } // switch (systemState) 
  } // while (true)
  } // loop

 

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Несколько вопросов:

Стоит ли избавляться от паузы 100 мс, или она не столь значительна?

 Почему Вы пишите if (HIGH == digitalRead(2)) {, а не if (DigitalRead(2)) {

Для наглядности или что-то иное?

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

Irinka пишет:

Несколько вопросов:

Стоит ли избавляться от паузы 100 мс, или она не столь значительна?

 Почему Вы пишите if (HIGH == digitalRead(2)) {, а не if (DigitalRead(2)) {

Для наглядности или что-то иное?

Не работает, потому что в case 0x00 нет выпрыгивания на case 0x01 через systemState. Прошляпил-с. С case просто наглядней переходы в состояния. Через месяцок вы на свои if-ы будете смотреть и думать - как же тут флаги перекидываются...

От паузы избавляться не стоит - она же дает время успокоиться дребезжащим контактам. Но переписать на по типу blink без delay стоит. Если, конечно, предполагается что-то еще вне свича делать постоянно.

Пишу для выработки привычки. На if(variable = LOW) компилятор не обругается, а вот на if(LOW = variable) - обматерится. В обоих случая выражение записано неверно с точки зрения использования оператора сравнения. Дешевый способ обнаружить, где затупил или закопипастил неверно. 

while (true) в лупе исключительно с целью не связываться с глобальными/статическими переменными. Ну и немного ускорить цикл (на копейки), выкинув то, что за пределами лупа используется. В вашем случае конструкция применена бессмысленно.

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Спасибо.

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

sadman41 пишет:

Не работает, потому что в case 0x00 нет выпрыгивания на case 0x01 через systemState. Прошляпил-с. С case просто наглядней переходы в состояния. Через месяцок вы на свои if-ы будете смотреть и думать - как же тут флаги перекидываются...

case 0x06:
34
        delay(3000);
35
        systemState = 0x00;
36
        break;
37
    } // switch (systemState)
38
  } // while (true)
39
} // loop

А еще не 0x06, а 0x05 =)))))))