почему не удается подвесить прерывание WDT ?

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

Попался на глаза вот такой код:

void(* resetFunc) (void) = 0;  // фнкция ресета, вызывает прерывание 0
volatile uint8_t WDTb = 0;

void  SetWDT() 
 {
 cli(); // Запрещаем прерывания на время изменения WDE и WDP
 asm("wdr"); // Сбрасываем WDT
 WDTCSR |= (1 << WDCE) | (1 << WDE); // Разрешаем изменение значения предделителя WDT:
 WDTCSR = (1 << WDP2) | (1 << WDP1) | (1 << WDP0) | (1 << WDIE ); // Interrupt mode, WD 2 сек
 sei(); // Разрешаем прерывания
}

ISR(WDT_vect) {

  WDTb++;
  if (WDTb > 2)
   {
   asm("wdr"); // Сбрасываем WDT
   delay(1000);
   resetFunc();
   }
}

void setup() {
 SetWDT();
}

void loop() {
  WDTb = 0;
}

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

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

Код работает, проверил лично. Вопросы про другое - про обработчик прерывания WDT. Первая для меня непонятка - в нем стоит delay() . Но ведь все знают. что делей в прерывании использовать нельзя. он же сам использует прерывание? - как так?

И даже более того - есть поиенять делей на бесконечный цикл - программа все равно работает и софт-ресет происходит без малейшей задержки! Почему так?

ISR(WDT_vect) {

  WDTb++;
  if (WDTb > 2)
   {
   asm("wdr"); // Сбрасываем WDT
   while(1) {};
   resetFunc();
   }
}

Можно бы подумать, что компилятор выкидывает while как пустой цикл- но нет, я пробовал вставлять в цикл разные операторы, разницы нет.

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

Может WDTb переполняется ?

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

Komandir пишет:

Может WDTb переполняется ?

не, прерывание настроено раз в 2 секунды... это ж скока надо до переполнения... Тут сброс происходит быстро, визуально такое впечатление, что даже delay(1000) не отрабатывает.

Upper
Offline
Зарегистрирован: 23.06.2020
void delay(unsigned long ms)
{
	uint32_t start = micros();

	while (ms > 0) {
		yield();
		while ( ms > 0 && (micros() - start) >= 1000) {
			ms--;
			start += 1000;
		}
	}
}

Странно, что данный вопрос задан в песочнице.

ТС не знает ответа или предлагает решить эту задачу новичкам?

Если сам не знает ответа - то по delay(); достаточно заглянуть в первоисточники, чтобы понять почему не зависания (в данном конкретном случае).

sadman41
Offline
Зарегистрирован: 19.10.2016

Какие операторы? Мигалку может жахнуть, чтобы видно было.

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

А вообще вход в прерывание происходит ?

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

sadman41 пишет:
Какие операторы? Мигалку может жахнуть, чтобы видно было.

мигалку и ставил. Не успевает мигнуть ни разу, сразу софтресет...  Такое впечатление, что улетает в ресет со сточки с wdr

Кстати мысль! А может команду wdr в прерывании ватчдога использовать нельзя??

Добавка позже - просмотрел даташит атмеги328 и атмеловский апноут про WDT - запрета на wdr в прерывании не нашел

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

Upper пишет:

ТС не знает ответа или предлагает решить эту задачу новичкам?

не знает.

про delay спасибо, ща гляну, не подсказывайте дальше :)

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

Upper пишет:

Странно, что данный вопрос задан в песочнице.

ну а где его еще задавать? разве что в "Отвлеченных"...

Мне почему-то кажется, что ответ по факту будет очень простым.

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

В loop WDTb постоянно обнуляется - как она может стать > 2 ???

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

Komandir пишет:

В loop WDTb постоянно обнуляется - как она может стать > 2 ???

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

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

Уточнения

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

Во-вторых - вопрос абсолютно реальный, не викторина и не глум. То что код c while(1) ресетится- проверено на стандартной нано с контроллером атмега328

rkit
Offline
Зарегистрирован: 23.11.2016

b707 пишет:

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

Нет, как раз это и надо обсуждать, потому что это какой-то бред и полное непонимание процесса вечного ребута.

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

Цитата:

The Enhanced Watchdog Timer has three modes of operation. When operating in WDT System Reset Mode, a WDT timeout causes a system reset. If WDT Interrupt Mode and global interrupts are enabled, a WDT timeout sets the WDT Interrupt Flag and executes the WDT Interrupt handler, instead of resetting the system. If both WDT System Reset Mode and WDT Interrupt Mode are enabled, the first WDT timeout is handled as if only WDT Interrupt Mode was enabled. Then WDT Interrupt Mode is disabled automatically and the WDT is back in only WDT System Reset mode.

AVR132: Using the Enhanced Watchdog Timer   стр 2

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

rkit пишет:

Нет, как раз это и надо обсуждать, потому что это какой-то бред и полное непонимание процесса вечного ребута.

согласен про бред и непонимание, но это не ко мне. Это к авторам идеи

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

Сейчас вопрос исключительно о том, почему while(1) не завешивает интеррапт-хендлер

Код представлен ТОЛЬКО ДЛЯ ИЛЛЮСТРАЦИИ

Green
Offline
Зарегистрирован: 01.10.2015

b707 пишет:

Сейчас вопрос исключительно о том, почему while(1) не завешивает интеррапт-хендлер

Для того и wdt, что бы НИЧТО не завешивало.)

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

Green пишет:

Для того и wdt, что бы НИЧТО не завешивало.)

тоном обиженного новичка: " - Я понял, вы тут сами ответ не знаете..." :))

Ладно, обсуждение не было бесплодным, появились кое-какие идеи, вечером еще покопаю.

Rumata
Rumata аватар
Offline
Зарегистрирован: 29.03.2019

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

Green
Offline
Зарегистрирован: 01.10.2015

Дмитрий, ну ты же сам цитату из ДШ приводил. Что непонятно?
Стоит на while(1); Перегружает по сбросу. Или я не понял вопроса? 

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

Green пишет:

Дмитрий, ну ты же сам цитату из ДШ приводил. Что непонятно?
Стоит на while(1); Перегружает по сбросу. Или я не понял вопроса? 

по какому сбросу7

Upper
Offline
Зарегистрирован: 23.06.2020
Программа стоит на while

ISR(WDT_vect) {
00000053  PUSH R1		Push register on stack 
00000054  PUSH R0		Push register on stack 
00000055  IN R0,0x3F		In from I/O location 
00000056  PUSH R0		Push register on stack 
00000057  CLR R1		Clear Register 
00000058  PUSH R24		Push register on stack 
	WDTb++;
00000059  LDS R24,0x0100		Load direct from data space 
0000005B  SUBI R24,0xFF		Subtract immediate 
0000005C  STS 0x0100,R24		Store direct to data space 
	if (WDTb > 2)
0000005E  LDS R24,0x0100		Load direct from data space 
00000060  CPI R24,0x03		Compare with immediate 
00000061  BRCS PC+0x03		Branch if carry set 
		asm("wdr"); // ���������� WDT
00000062  WDR 		Watchdog reset 
00000063  RJMP PC-0x0000		Relative jump 
}
00000064  POP R24		Pop register from stack 
00000065  POP R0		Pop register from stack 
00000066  OUT 0x3F,R0		Out to I/O location 
00000067  POP R0		Pop register from stack 
00000068  POP R1		Pop register from stack 
00000069  RETI 		Interrupt return 

 

Green
Offline
Зарегистрирован: 01.10.2015

wdt же перегружает.

 

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

Upper, все-таки wdr?

	asm("wdr"); /
00000062  WDR 		Watchdog reset 
00000063  RJMP PC-0x0000		Relative jump 

почему об этом в даташите ни слова?

Или я опять нифига не понял.... откуда берется джамп на нулевой адрес?

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

Green пишет:

wdt же перегружает.

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

Green
Offline
Зарегистрирован: 01.10.2015

Да я о том. Или я всё пропустил?)

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

Green пишет:

Да я о том. Или я всё пропустил?)

в #13 перечислены три режима ватчдога. То, о чем вы с Дедом говорите - это третий режим, "Interrupt and Reset mode". А в этом коде использован второй - только Interrupt

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

В #20 не вижу while(1) ...

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

Komandir пишет:

В #20 не вижу while(1) ...

я так понял, все что ниже wdr - при компиляции выкидывается и в код вообще не попадает. Ждем обьяснений от Upper

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

RJMP 0000 же попал

Upper
Offline
Зарегистрирован: 23.06.2020
Это и есть while(1);
 
RJMP PC-0x0000        Relative jump 
Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

Тогда resetFunc(); он точно отбросил - понимает с..а что туда не попасть ...

Green
Offline
Зарегистрирован: 01.10.2015

Судя по листингу Upper, оптимизатор выкинул while(1);

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

Upper пишет:

Это и есть while(1);
 
RJMP PC-0x0000        Relative jump 

а, ну да, он же Relative

Тогда где абсолютный переход на 0 ResetFunc() ?

 

Upper
Offline
Зарегистрирован: 23.06.2020

А вот ResetFunc() оптимизатор выкинул.

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

Upper пишет:

А вот ResetFunc() оптимизатор выкинул.

если компилятор выкинул его - почему программа не виснет а все-таки улетает на нулевой адрес? - вернулись к исходному вопросу....

Upper
Offline
Зарегистрирован: 23.06.2020

Прошил, проверил - виснет

void(* resetFunc) (void) = 0;  // фнкция ресета, вызывает прерывание 0
volatile uint8_t WDTb = 0;

void  SetWDT()
{
  cli(); // Запрещаем прерывания на время изменения WDE и WDP
  asm("wdr"); // Сбрасываем WDT
  WDTCSR |= (1 << WDCE) | (1 << WDE); // Разрешаем изменение значения предделителя WDT:
  WDTCSR = (1 << WDP2) | (1 << WDP1) | (1 << WDP0) | (1 << WDIE ); // Interrupt mode, WD 2 сек
  sei(); // Разрешаем прерывания
}

ISR(WDT_vect) {

  WDTb++;
  if (WDTb > 2)
  {
    asm("wdr"); // Сбрасываем WDT
    digitalWrite(13,1);
    while(1) {
        asm("nop");
        };
    resetFunc();
  }
}

void setup() {
  pinMode(13,OUTPUT);
  digitalWrite(13,1);
  delay(1000);
  digitalWrite(13,0);
  delay(4000);
  SetWDT();
}

void loop() {
  digitalWrite(13,0);
}

 

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

setup вот так перепишите - попробуйте, сколько строчек "start" напишет?

void setup() {
 Serial.begin(9600);
 Serial.println("start");
  pinMode(13,OUTPUT);
  digitalWrite(13,1);
  delay(1000);
  digitalWrite(13,0);
  delay(4000);
  SetWDT();
}

 

Upper
Offline
Зарегистрирован: 23.06.2020

Start печатается один раз.

Светодиод после первого Вкл Выкл  включается и больше и не моргает, значит ни в loop ни в setup он больше не заходит

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

Upper пишет:

Start печатается один раз.

Светодиод после первого Вкл Выкл  включается и больше и не моргает, значит ни в loop ни в setup он больше не заходит

спасибо за тест, вечером попробую на своей. У меня на подобном скетче start печатала дважды - то есть первый раз ресет все-таки срабатывал, а потом программа висла, не доходя до вторичной настройки setWDT() в сетапе

sadman41
Offline
Зарегистрирован: 19.10.2016

Мошт отключить канпилятору оптимизацию, чтобы условия сравнения были одинаковые?

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

Повторил тест Upper - все работает ровно так же, как у него. Программа виснет на бесконечном цикле, как и должна

 

После этого полез в свой код.... и сразу нашел ошибку.

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

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

 

Блин, я ж говорил, что эта тема - для "Песочницы" и  причиной будет какая-то глупая ошибка. Зато я прошутидовал кучу материалов про WDT и теперь выучил все его режимы :)

Всем спасибо, а Upper - отдельно!

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

Upper пишет:

Программа стоит на while

ISR(WDT_vect) {
00000053  PUSH R1		Push register on stack 
00000054  PUSH R0		Push register on stack 
....
00000067  POP R0		Pop register from stack 
00000068  POP R1		Pop register from stack 
00000069  RETI 		Interrupt return 

 

Upper, еще хотел спросить - а это из какой программы такой листинг?

Green
Offline
Зарегистрирован: 01.10.2015

Кстати, странно что в libc нет настройки режима wdt. Только enable/disable и reset.

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

b707 ИМХО это похоже на дизассемблер Atmel Studio

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

У мня Visual Micro так же умеет.

Upper
Offline
Зарегистрирован: 23.06.2020

Komandir пишет:

b707 ИМХО это похоже на дизассемблер Atmel Studio

Да это дизассемблер при отладке в Atmel Studio 7.

Формат отличается от создаваемого им же (Atmel Studio 7) листинга lss

Если просто надо посмотреть на генерируемый код, то в lss проще найти нужный код