прерывание и возврат к началу цикла

dimOnOff
Offline
Зарегистрирован: 21.04.2015

Добрый день, уважаемые форумчане! 

У меня такой вопрос: можно ли реализовать возврат к началу цикла програмы после снятия внешнего прерывания?  Т.е.выход из подпрограммы прерывания в начало основного цикла loop(). Нигде решения не нашел. Спасибо.

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Как правило такая постановка задачи - не корректный способ решить её.
Выход из прерывания происходит в точку, где произошло это прерывание.

Puhlyaviy
Puhlyaviy аватар
Offline
Зарегистрирован: 22.05.2013

kisoft пишет:
Как правило такая постановка задачи - не корректный способ решить её.
Выход из прерывания происходит в точку, где произошло это прерывание.

Чё не коректная. Такое прерывание называется РЕСЕТ.

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Ресет это не только ценный мех ещё и вызов setup()

Araris
Offline
Зарегистрирован: 09.11.2012

Да простят меня боги программирования и прогрессивная мировая общественность, но может быть оператор goto ? (http://arduino.ru/Reference/Goto) Ну, если уж совсем никак иначе..

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

setup можно обойти выяснив что мы туда попали не из-за пропадания-включения  питания.
Ещё lehak давно об этом писАл.
Ну а переход можно сделать разрешив WDT

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

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

 

dimOnOff
Offline
Зарегистрирован: 21.04.2015

Всем спасибо) Опробывал return. Ничего так ,вроде, работает. 

Схема такая:

int intPin = 0;
void setup() {
  attachInterrupt(intPin,pause,LOW); //функция прерывания
  pinMode(2, INPUT);         //настраиваем контакт 2 на вход(он же intPin)
}

void loop() {
  ...
  ...
  if (digitalRead(2) == LOW){delay(100); return; }
  ...
  ...
}
kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Вот вот, как всё просто ;)

Нахрена мерседес если ездить только на огород за картошкой :)

А ещё очередная загадка, когда судя по комментариям intPin должен быть пином 2, а реально, пин 0. Эхх..

 

dimOnOff
Offline
Зарегистрирован: 21.04.2015

Да нет же) Это не пин , это номер прерывания. Прерывание "0", а пин для прерывания действительно задействован "2". Кстати, плата ардуино Уно.

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Не разглядел вчера в темноте :)
В таком случае название странное, слово pin сбило с толку.

dimOnOff
Offline
Зарегистрирован: 21.04.2015

Ну в общем да) Так будет короче и понятнее)


void setup() {
  attachInterrupt(0,pause,LOW); //функция прерывания (на втором контакте UNO)
  pinMode(2, INPUT);         //настраиваем контакт 2 на вход
}

void loop() {
  ...
  ...
  if (digitalRead(2) == LOW){delay(100); return; }
  ...
  ...
}

 

Fantomas
Offline
Зарегистрирован: 20.12.2015

Здравствуйте друзья!

Небольшое вступление. Сразу же прошу прощения и сообщаю что в написании программ МК я лох педальный. На самом деле у меня вторая радиолюбительская категория и с микросхемами на обычной логике никаких проблем нет. С монтажом пайкой и прошивкой МК тоже все в порядке.

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

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

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

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

Единственным я вижу это как то после прерывания прыгать на начало цикла, почему собственно и написал сюда. Экспериментировал и return и continue и даже с goto но что то ничего не получается. Может изначально сделать как то иначе? Подскажите примерно по какому пути идти чтобы решить такую задачу? Я дальше сам допетрю.

Спасибо всем большушие!

 

Вот код 

[code]
int buttonInt = 0; // Прерывание 0 на втором пине)
int led1 = 5; // Первый светодиод
int led2 = 6; // Второй светодиод
int btn = 3; // Кнопка

volatile byte regim = 0; // Переменная для прерывания


void setup()
{
  pinMode(led1, OUTPUT); // Пятый пин на выход первый светодиод
  pinMode(led2, OUTPUT);// Шестой пин на выход второй светодиод
  attachInterrupt(buttonInt, change, RISING); // прерывание по мпадающему фронту
  Serial.begin(9600); // Для контроля
}

//обработчик прерывания
void change()
{
  regim = regim + 1; // С каждым разом увеличиваем regim на 1
  if (regim > 11) // Если regim больше 11
    regim = 0;// То сбрасываем regim в 0
}

// Тут проверкой if выбираються режимы работы светодиодов
void loop()
{
  if (regim == 0)
    Serial.println("regim 0");
  delay(250);

  //=============================

  if (regim == 1)
  {
    Serial.println("regim 1");

    delay(250);
    for (int i = 0; i < 3; i++)
    {
      digitalWrite(led1, HIGH);
      delay(50);
      digitalWrite(led1, LOW);
      delay(50);
    }
    delay(250);
    for (int i = 0; i < 3; i++)
    {
      digitalWrite(led2, HIGH);
      delay(50);
      digitalWrite(led2, LOW);
      delay(50);
    }
  }

  //==============================

  if (regim == 2)
  {
    Serial.println("regim 2");

    delay(500);
    for (int i = 0; i < 2; i++)
    {
      digitalWrite(led1, HIGH);
      delay(40);
      digitalWrite(led1, LOW);
      delay(60);
    }

    delay(500);
    for (int i = 0; i < 2; i++)
    {
      digitalWrite(led2, HIGH);
      delay(40);
      digitalWrite(led2, LOW);
      delay(60);
    }
  }

  //============================

  if (regim == 3)
  {
    Serial.println("regim 3");

    delay(500);

    for (int i = 0; i < 3; i++)
    {
      digitalWrite(led1, HIGH);
      delay(50);
      digitalWrite(led1, LOW);
      delay(80);
    }

    delay(500);

    for (int i = 0; i < 3; i++)
    {
      digitalWrite(led2, HIGH);
      delay(50);
      digitalWrite(led2, LOW);
      delay(80);
    }
  }

  //======================

  if (regim == 4)
  {
    Serial.println("regim 4");

    delay(150);

    for (int i = 0; i < 6; i++)
    {
      digitalWrite(led1, HIGH);
      delay(80);
      digitalWrite(led1, LOW);
      delay(80);
    }
    delay(150);

    for (int k = 0; k < 6; k++)
    {
      digitalWrite(led2, HIGH);
      delay(80);
      digitalWrite(led2, LOW);
      delay(80);
    }
  }

  //===========================

  if (regim == 5)
  {
    Serial.println("regim 5");

    delay(70);

    for (int i = 0; i < 7; i++)
    {
      digitalWrite(led1, HIGH);
      delay(30);
      digitalWrite(led1, LOW);
      delay(70);
    }

    delay(70);

    for (int i = 0; i < 7; i++)
    {
      digitalWrite(led2, HIGH);
      delay(30);
      digitalWrite(led2, LOW);
      delay(70);
    }
  }

  //=============================

  if (regim == 6)
  {
    Serial.println("regim 6");

    delay(250);

    for (int i = 0; i < 8; i++)
    {
      digitalWrite(led1, HIGH);
      digitalWrite(led2, HIGH);
      delay(40);
      digitalWrite(led1, LOW);
      digitalWrite(led2, LOW);
      delay(40);
    }
  }

  //==============================

  if (regim == 7)
  {
    Serial.println("regim 7");

    delay(400);

    for (int i = 0; i < 10; i++)
    {
      digitalWrite(led1, HIGH);
      digitalWrite(led2, HIGH);
      delay(30);
      digitalWrite(led1, LOW);
      digitalWrite(led2, LOW);
      delay(100);
    }
  }

  //===============================

  if (regim == 8)
  {
    Serial.println("regim 8");

    for (int i = 0; i < 255; i++)
    {
      analogWrite(led1, i);
      delay(3);
    }
    for (int i = 0; i < 3; i++)
    {
      digitalWrite(led1, HIGH);
      delay(50);
      digitalWrite(led1, LOW);
      delay(50);
    }
    for (int i = 255; i >= 0; i --)
    {
      analogWrite(led1, i);
      delay(3);
    }
    // Второй диод
    for (int i = 0; i <= 255; i++)
    {
      analogWrite(led2, i);
      delay(3);
    }
    for (int i = 0; i < 3; i++)
    {
      digitalWrite(led2, HIGH);
      delay(50);
      digitalWrite(led2, LOW);
      delay(50);
    }
    for (int i = 255; i >= 0; i --)
    {
      analogWrite(led2, i);
      delay(3);
    }
  }

  //===============================

  if (regim == 9)
  {
    Serial.println("regim 9");

    for (int i = 0; i < 255; i++)
    {
      analogWrite(led1, i);
      analogWrite(led2, i);
      delay(3);
    }
    for (int i = 0; i < 3; i++)
    {
      digitalWrite(led1, HIGH);
      digitalWrite(led2, HIGH);
      delay(50);
      digitalWrite(led1, LOW);
      digitalWrite(led2, LOW);
      delay(50);
    }
    for (int i = 255; i >= 0; i --)
    {
      analogWrite(led1, i);
      analogWrite(led2, i);
      delay(3);
    }
  }

  //====================================

  if (regim == 10)
  {
    Serial.println("regim 10");

    for (int i = 0; i < 255; i++)
    {
      analogWrite(led1, i);
      delay(3);
    }
    for (int i = 0; i < 4; i++)
    {
      digitalWrite(led2, HIGH);
      delay(50);
      digitalWrite(led2, LOW);
      delay(50);
    }
    for (int i = 255; i >= 0; i --)
    {
      analogWrite(led1, i);
      delay(3);
    }
    // Второй диод
    for (int i = 0; i <= 255; i++)
    {
      analogWrite(led2, i);
      delay(3);
    }
    for (int i = 0; i < 4; i++)
    {
      digitalWrite(led1, HIGH);
      delay(50);
      digitalWrite(led1, LOW);
      delay(50);
    }
    for (int i = 255; i >= 0; i --)
    {
      analogWrite(led2, i);
      delay(3);
    }
  }

  //================================

  if (regim == 11)
  {
    Serial.println("regim 11");

    for (int i = 0; i < 255; i++)
    {
      analogWrite(led1, i);
      delay(3);
    }
    for (int i = 255; i >= 0; i --)
    {
      analogWrite(led1, i);
      delay(3);
    }
    // Второй диод
    for (int i = 0; i <= 255; i++)
    {
      analogWrite(led2, i);
      delay(3);
    }
    for (int i = 255; i >= 0; i --)
    {
      analogWrite(led2, i);
      delay(3);
    }
  }
}



[/code]

 

Andy
Andy аватар
Offline
Зарегистрирован: 01.01.2016

Fantomas пишет:
Подскажите примерно по какому пути идти чтобы решить такую задачу?
Избавляться от delay. Основной цикл должен выполняться хотя бы за 100 миллисекунд.

Fantomas
Offline
Зарегистрирован: 20.12.2015

Andy пишет:

Избавляться от delay. Основной цикл должен выполняться хотя бы за 100 миллисекунд.

 

Это по принципу как в Blink без delay?

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

 

vosara
vosara аватар
Offline
Зарегистрирован: 08.02.2014

Вот посмотрите, я Вам на бросал первые 3 режима и 8й для первого светодиода остальное, если подойдет, сделаете по аналогии

int buttonInt = 0; // Прерывание 0 на втором пине)
int led1 = 5; // Первый светодиод
int led2 = 6; // Второй светодиод
int btn = 3; // Кнопка
int tempLed1 = 0;
int tempLed2 = 0;
int regimTemp = 0;
int t1 = 0;//Задержка
byte oper = 1;
unsigned long x; //Переменная для прошедшего периода

volatile byte regim = 0; // Переменная для прерывания


void setup()
{
  pinMode(led1, OUTPUT); // Пятый пин на выход первый светодиод
  pinMode(led2, OUTPUT);// Шестой пин на выход второй светодиод
  attachInterrupt(buttonInt, change, RISING); // прерывание по мпадающему фронту
  Serial.begin(9600); // Для контроля
}

//обработчик прерывания
void change()
{
  regim = regim + 1; // С каждым разом увеличиваем regim на 1
  if (regim > 11) // Если regim больше 11
    regim = 0;// То сбрасываем regim в 0
}

void blinkLed1()
{
digitalWrite(led1, !digitalRead(led1));
}

void blinkLed2()
{
digitalWrite(led2, !digitalRead(led2));
}

void chenLed1(int i)
{
analogWrite(led1, i);
}

void chenLed2(int i)
{
analogWrite(led2, i);
}

void loop()
{
switch (regim) {
    case 0:
      if (millis() - x >= 250)
  	{
    	x = millis() ;
    	Serial.println("regim 0");
	regimTemp = regim;
  	}
      break;

    case 1:
     	if (regim != regimTemp)
	{
	Serial.println("regim 1");
 	regimTemp = regim;
	tempLed1=0;
	tempLed2=6;
	}
	
	if (tempLed1 < 6)
	{
	  if (millis() - x >= 50)
  	  {
    	   x = millis();
	   tempLed1++;
	   blinkLed1();
	   if (tempLed1 >= 6)tempLed2 = 0;
	  }
         }

	if (tempLed2 < 6)
	{
	 if (millis() - x >= 50)
  	  {
    	   x = millis();
	   tempLed2++;
	   blinkLed2();
	   if (tempLed2 >= 6)tempLed1 = 0;
	  }
	}
      break;

    case 2:
      if (regim != regimTemp)
	{
	 Serial.println("regim 2");
 	 regimTemp = regim;
	 tempLed1=0;
	 tempLed2=4;
	}
	
	if (tempLed1 < 4)
	{
	  if (millis() - x >= t1)
  	 {
	 if (!digitalRead(led1)) t1 = 40;
	 else t1 = 60;
    	  x = millis();
	  tempLed1++;
	  blinkLed1();
	  if (tempLed1 >= 4)tempLed2 = 0;
	 }
        }
	
	if (tempLed2 < 4)
	{
	 
	 if (millis() - x >= t1)
  	 {
	 if (!digitalRead(led2))t1 = 40;
	 else t1 = 60;
    	  x = millis();
	  tempLed2++;
	  blinkLed2();
	  if (tempLed2 >= 4)tempLed1 = 0;
	 }
	}
	
      break;

    case 3:
      if (regim != regimTemp)
	{
	 Serial.println("regim 3");
 	 regimTemp = regim;
	}
      break;

    case 4:
      if (regim != regimTemp)
	{
	 Serial.println("regim 4");
 	 regimTemp = regim;
	}
      break;

    case 5:
      if (regim != regimTemp)
	{
	 Serial.println("regim 5");
 	 regimTemp = regim;
	}
      break;
    case 6:
      if (regim != regimTemp)
	{
	 Serial.println("regim 6");
 	 regimTemp = regim;
	}
      break;
    case 7:
      if (regim != regimTemp)
	{
	 Serial.println("regim 7");
 	 regimTemp = regim;
	}
      break;
    case 8:
      if (regim != regimTemp)
	{
	 Serial.println("regim 8");
 	 regimTemp = regim;
	 oper =1;
	 tempLed1=0;
	 tempLed2=255;
	}
	
	if (oper == 1)
	{
	 if (tempLed1 < 255)
	 {
	  if (millis() - x >= 3)
  	  {
	   x = millis();
	   tempLed1++;
	   chenLed1(tempLed1);
	   if (tempLed1 >= 255)
	   {
	    oper = 2;
	    tempLed1 = 0;
	   }
	  }
         }
	}
	
	if (oper == 2)
	{
	if (tempLed1 < 4)
	{
	  if (millis() - x >= 50)
  	 {
	  x = millis();
	  tempLed1++;
	  blinkLed1();
	  if (tempLed1 >= 4)oper = 3, tempLed1=255 ;
	  }
         }
	}

	if (oper == 3)
	{
	 if (tempLed1 > 0)
	 {
	  if (millis() - x >= 3)
  	  {
	   x = millis();
	   tempLed1--;
	   chenLed1(tempLed1);
	   if (tempLed1 == 0)
	   {
	    oper = 4;
	    tempLed1 = 0;
	   }
	  }
         }
	}	  
      break;
    case 9:
      if (regim != regimTemp)
	{
	 Serial.println("regim 9");
 	 regimTemp = regim;
	}
      break;   
    case 10:
      if (regim != regimTemp)
	{
	 Serial.println("regim 10");
 	 regimTemp = regim;
	}
      break;
    case 11:
      if (regim != regimTemp)
	{
	 Serial.println("regim 11");
 	 regimTemp = regim;
	}
      break; 
    
  }

}

 

Fantomas
Offline
Зарегистрирован: 20.12.2015

vosara

Спасибо за совет и за скетч!

Немного странно прада работает, то нормально, а то инвертируються LOW и HIGH на выходах, то есть когда ненада горит, а когда нада тухнет. Но суть с millis я уловил, спасибо, буду пробовать.

Но для начала я нашел более простой способ решения моей проблеммы.

Может кому понадобиться.

В длогих циклах for я поставил if проверку и если переменная сменилась то делается выход из цикла for (while, switch) командой break. Полагаю что и continue тоже подойдет, то есть будет прыгать на следующюу итерацию (правда не проверял). 


    for (int i = 0; i < 255; i++)  // Долгий цикл 2-3 секунды
    {
      if (regim != 8)             //  Если regim не равен 8 то
        break;                    //  Моментально выходим из цикла
      analogWrite(led1, i);
      delay(3);
    }


 

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

Будем пробовать и иные варианты.

Спасибо всем.

dangol
Offline
Зарегистрирован: 12.06.2012

А ведь это ОЧЕНЬ НУЖНЫЙ вопрос!!!

Например, у меня, в loop-е, идет опрос температурных датчиков по шине OneWare Это может длиться 5 и более сек. Библиотека для DS18B20, имеет внутри себя delay(), которые НЕ охота выискивать, а потом модифицировать...( Далее, по нажатию на кнопку, нужно включать backlght на дисплее. В результате, нажимаем кнопку, срабатывает аппаратное прерывание, включение (lcd.backlight), в функции аппаратного прерывания не работает...(. Поэтому, можно ждать и 3 и 5 сек, пока закончится опрос датчиков, и backlight сработает, а это НУ совсем не по пацански...) Вывод, нужно, наплевав на все, по аппаратному прерыванию, переходить в начало loop-а, за последствия отвечаю сам...)  kisoft, предложил блестящую идею, в функции прерывания, подменить адрес возврата не в то место откуда она вызвалась, а на начало Loop-а.

Вот как теперь это реализовать без подробнейшего изучения микропроцессора?

Fantomas
Offline
Зарегистрирован: 20.12.2015

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

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

Logik
Offline
Зарегистрирован: 05.08.2014

Кончайте ересь разводить. Если возникла потребность выйти из прерывания в заданую точку - архитектура дерьмо, нада делать новую. Ибо вы сделаете мегакостыль, который вызовет малопрогнозируемое количество гемороя. Потому так:

1. В принципе это реализуемо, но в прикладных программах не используется, в ОС используется например вытесняющая многозадачность. 

2. Новичек-любитель это не напишет грамотно, надо читать даташит, разбиратся с сохранением в стек при прерывании, заменять в нем нужные значения ручками на ещё более нужные ;) корректировать указатель стека так, чтоб размер стека был таким же как и при приходе в эту точку естественным путем.

3. Профи с таким подходом связыватся не будет, нафиг надо через столб прыгать, если обойти много проще, и яйца целее ;) 

ПС. либу которая 5 секунд держит управление - сразу в топку. Либы под ардуину - полная хрень, за что не возьмись. Увы.

 

dangol
Offline
Зарегистрирован: 12.06.2012

Да, согласен с Logik, будем считать это ересью...)

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

dangol пишет:
А ведь это ОЧЕНЬ НУЖНЫЙ вопрос!!! ... Это может длиться 5 и более сек. ... которые НЕ охота выискивать, а потом модифицировать...

Нужный тут только один вопрос: если не охота выискивать, в том числе православно-верные библиотеки, разбираться в работе устройства, которое Вы программируете (и как понимаю в надежде потом запродать результат своей лени) .. то нафига вы вообще взялись за Ардуино?

Ардуино - ИСКЛЮЧИТЕЛЬНО учебная поделка в целях ОБУЧЕНИЯ. Никакая её часть не может применяться в готовом изделии. Это как из детского конструктора сваять подъемный кран и удивляться, что "дачка не строится, а кран валится".

dangol
Offline
Зарегистрирован: 12.06.2012

Лично я, хочу сделать охлаждающую камеру до 16-18 градусов. Для расстойки теста для хлеба. Для этой цели - ардуино идеальный вариант (а реально, больше не на чем), и действительно, выискивать классные библиотеки, неохота. Алгоритм камеры - не сложен. А по поводу "учебной поделки в целях обучения" - не согласен, все-таки 16 миллионов операций в секунду - спутник можно обсчитывать...)

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

:) dangol, у вас ключевая мысля в этом самом "неохота". Никакая "охлаждающая камера" не требует алгоритма с нарушением цепочки возвратов хода выполнения. Задачи такого рода возникают только на очень сложных асинхронных алгоритмах и имеющих критические секции исполнения .. то есть требующие реальной многозадачности. Но это - далеко не ваш случай. Не надо что-то "выискивать". Все что вам требуется - это нормально прописать алгоритм, что вам явно тоже "неохота", об чем и спич.

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

Dessan07
Offline
Зарегистрирован: 22.11.2018

Так что получается, нет никакого известного способа возвращения из цикла к началу Loop()?

Сам столкнулся с такой необходимостью. Из-за условий эксплуатации и конструкции корпуса возможно временное отключение панели с дисплеем на I2C шине. При проверке выяснилось, что от многократного издевательского воздействия(отключения-подключения провода SCL), если попасть на неопределённый момент вывода информации на экран, программа зависает. Но продолжает свою работу вектор прерываний ISR (WDT_vect). Понять в нём, что программа зависла - легко, но вот попасть бы из него куда-нибудь кроме точки зависания очень бы хотелось.

Araris пишет:

Да простят меня боги программирования и прогрессивная мировая общественность, но может быть оператор goto ? (http://arduino.ru/Reference/Goto) Ну, если уж совсем никак иначе..

Что касается GOTO, он позволяет перемещаться только внутри обработки прерывания, если метку поместить в Loop, а goto в прерывании, компилятор говорит, что метка не объявлена :( 

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

P.S. Похоже дело не в цикле. Попробовал asm("JMP 0"); Реле не отпадают, зато переменные обнуляются, а это кажется поправимо. Но беда в другом: когда код доходит до взаимодействия с устройствами на шине I2C, снова зависает. Зато превосходно продолжает работать код внутри ISR (WDT_vect). Помогает только аппаратный сброс :( Хрень какая-то...

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

Dessan07 - если программа действительно зависла, единственный выход это полный РЕСЕТ. Если вас это не устраивает - проектируйте устройство так, чтобы оно не зависало. Вообще. частое срабатывание ватчдога - признак криворукости программиста.

Dessan07
Offline
Зарегистрирован: 22.11.2018

Ватчдог сам по себе и не срабатывал (если речь о перезагрузке им), он вообще для своевременного опроса датчиков. Програмный перезапуск при помощи asm("JMP 0"); в ватчдоге происходит осознанно, если после долгого насилования шины I2C loop() встаёт колом, и дополнительный код в ватчдоге определяет, что циклы не считаются. Дальнейшие тесты показали, что после програмного перезапуска при обращении к шине I2C происходит повторное зависание, причем пробовал при програмном перезапуске отключать поочерёдно куски кода, обращающиеся и к индикатору, и к дислею, без разницы, только доходит до любого из них, снова колом. Если их при перезапуске полностью отключить, всё работает отлично.

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

Dessan07 - приведите код, вместе посмеемся.

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

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

Dessan07
Offline
Зарегистрирован: 22.11.2018

:) хотел повырезать массивы кода перед отправкой, оставив нужное, но если уж и смеяться, то над всем полностью и не доделанным :)

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

За инфу про регистры и оперативку спасибо. Теперь ясно, а то удивлялся, почему еще не разрешенный после програмного перезапуска ватчдог продолжает работать :) Наверное перед ресетом буду в незанятые пины сохранять самые нужные однобитные переменные :D

//код вырезан при редактировании поста за неиформативностью, к тому же в разговоре после учавствовало только прерывание



ISR (WDT_vect) { 
  static boolean n = 0; 
  n = !n;
  if (n) {
    ds.reset();  
    ds.write(0xCC);
    ds.write(0x44);
  }
  else   {
    ds.reset();
    ds.select(addr1);
    ds.write(0xBE); 
    temp1 =  ds.read() | (ds.read() << 8); 
    
    ds.reset();
    ds.select(addr2);
    ds.write(0xBE); 
    temp2 =  ds.read() | (ds.read() << 8); 
    TMAS = temp1 / 16;
    TVOD = temp2 / 16;
    if (mem_scycle == c_scycle && c_scycle!= 0) {
      err_scycle++;
      Serial.println("bug0  ");
      delay(12);
      if (err_scycle > 1) {
      
        
        digitalWrite(rm1, LOW);
        digitalWrite(rm5, HIGH);
        Serial.println("reset");
        err_scycle = 0;
        
        delay(1500);
        asm("JMP 0"); 
      }
    }
    mem_scycle = c_scycle;
  }
}

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Крофьизглаз. 

Dessan07
Offline
Зарегистрирован: 22.11.2018

DetSimen, Можно пожалуйста топ 3 нюанса, которые сильнее всего режут глаз?

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

ну как я и предполагал :(

В прерывании (строка 553 и далее) вы используете работу с OneWire шиной и с Сериалом. И то и другое использует прерывания и внутри другого прерывания нормально работать не будет. Вообще, внутри вектора прерывания полагается размещать только простой и быстрый код - прочитать регистр, сбросить счетчик, выставить флаг.  Все остальное делается в основной программе.

остальной код не смотрел

Dessan07
Offline
Зарегистрирован: 22.11.2018

Сериал (да и вообще ниже 573 строки) 573573используется только когда программа уже зависла.

Вы меня конечно убедили перенести OneWire в loop, согласен, что так себе затея была...

Но... Оно же на практике всё работает. Температура на экране актуальная. Если не насиловать провод SCL, оно без проблем функционирует. Сейчас попробую вообще убрать ватчдог из кода, ради эксперимента, но почти уверен, что все равно смогу повесить плату.

 

wdrakula
wdrakula аватар
Онлайн
Зарегистрирован: 15.03.2016

Можно тихо, в уголочке, сказать, что для обработки исключительных ситуёвин в ентих Сях предусметрены хитрые функции setjump() и longjump()? Куды их совать - подстажет ...нет, не порнхаб, но Гугль всеблагой! И можно не мастурбировать левой пяткой через среднее ухо.

Dessan07
Offline
Зарегистрирован: 22.11.2018

Отрезал код кусок за куском, пока не оставил это. И оно всё ещё зависает, если дёргать SCL

#include "LCD_1602_RUS.h"          //подключаем библиотеку с кирилицей, так же должна быть библиотека LiquidCrystal_I2C.h с поддержкой bargraph 
LCD_1602_RUS lcd(0x27, 20, 4);      //задаём адрес для данных и размер дисплея (ширина и высота в символах)

void setup() {
Serial.begin(9600);      
lcd.init();                     
lcd.backlight();                
lcd.clear();             
}

void loop() {
  lcd.print("    ");
  Serial.println(millis());
}


 

Dessan07
Offline
Зарегистрирован: 22.11.2018

wdrakula, За setjump() и longjump() спасибо, попробую, судя по описанию то, что изначально и искал :). Правда, как выяснилось, в моём случае оно не поможет, но я рад, что появился ответ на основной вопрос темы, который тут задали еще в 2015 году :)

wdrakula
wdrakula аватар
Онлайн
Зарегистрирован: 15.03.2016

Dessan07 пишет:

Отрезал код кусок за куском, пока не оставил это. И оно всё ещё зависает, если дёргать SCL

тут тебе слишком глубоко копать притется, это в I2C библиотеке ...ну не то, что проблемы, но недочеты.

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

wdrakula
wdrakula аватар
Онлайн
Зарегистрирован: 15.03.2016

Dessan07 пишет:
wdrakula, За setjump() и longjump() спасибо, попробую, судя по описанию то, что изначально и искал :). Правда, как выяснилось, в моём случае оно не поможет, но я рад, что появился ответ на основной вопрос темы, который тут задали еще в 2015 году :)

Там было не обсуждение, а глум, что естественно. Мы - програмисты - злы и циничны. Все, кто глумился, я тебе ставлю 100 к 1, знали про лонг джамп. ;)))) Это на уровне букваря в С.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Dessan07 пишет:

Так что получается, нет никакого известного способа возвращения из цикла к началу Loop()?

Если вернуться к началу цикла, то continue, а если цикл находится внутри функции loop(), и надо вернуться к началу этой функции, то retutn.

Dessan07 пишет:

Что касается GOTO, он позволяет перемещаться только внутри обработки прерывания, если метку поместить в Loop, а goto в прерывании, компилятор говорит, что метка не объявлена :( 

Первое - нужно придумать, кто и как будет чистить стек.

Впрочем, думаю, можно поступить следующим образом:

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

Если надо откуда угодно вернуться к началу loop(), записать в указатель стека запомненное значение и вызвать loop(). По идее еще нужно при каждом входе в loop() разрешать прерывания. Как-то так.

Dessan07
Offline
Зарегистрирован: 22.11.2018

Ой, извиняюсь, там я некорректно написал. Цикл находится где-то в недрах библиотеки, функции которой используются в loop() при работе с I2С дисплеем. Работоспособным оказывается только срабатывающий раз в секунду кусочек кода в вотчдоге, и вот из этого прерывания хотелось бы прекращать заглючившую деятельность и возобновлять работу программы. Так же основная задача для меня побороть не именно это зависание, а вообще любое, какое может случиться в процессе выполнения программы (при этом нет гарантии, что температура в помещении не станет отрицательной, или что в устройство не уронят отвёртку, не будут через года физически отваливаться датчики и т.д., плюс ненадёжный источник питания и вибрации), если при этом прерывание будет в состоянии функционировать. При этом нельзя ронять некоторые контакты реле.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Ну тогда - обычная перезагрузка по "вотчдогу". А если нужно сохранять некоторый набор переменных, записывать его в EEPROM, а в setup() - читать необходимое.

Контакты реле можно зафиксировать 595 регистром.