ARDUINO Arduino Nano: old bootloader и новый bootloader

Leon111_09

★★★✩✩✩✩
6 Июн 2021
642
148
55
Таганрог
forum.wisecomp.ru
Можно прошить нормальный загрузчик, без этого. только нужно наличие программатора.я ,например, через стандартный блинк это сделал. понадобится программатор, программа AVRDUDEPROG и все. делаете копию блинка и запускаете его в ардуино. в настройках обязательно укажите Atmega 328p (первая строчка). затем в настройках скетча выбираете "экспорт бинарного файла". когда будет готов этот бинарник закрываете ардуино и переходим к AVRDUDEPROG. подключаете вашу ардуино через программатор к компьютеру. выставляете все как на скрине и прошиваете через программатор. после завершения прошивки отключаете программатор и ардуинку, закрываете дудку и пробуете прошить стандартным блинком уже в Ардуино с выбором стандартно бутлоадера. путь к файлу .hex у вас в дудке по идее должен быть похожим на тот, что указан на скрине. я стрелками отметил важные моменты,на которые стоит обратить внимание.

 

Геннадий П

★★★★★★✩
14 Апр 2021
1,838
592
44
@bort707, Пока не упрется в эти ограничения лучше действовать по правилу "работает - не трожь", потом когда уже освоит - уже можно ковырять загрузчик 🙂
 

bort707

★★★★★★✩
21 Сен 2020
2,899
862
@bort707, В каком смысле не работает ? А как же я его использовал ?
а так вот не работает. Речь идет исключительно о платах Нано со старым бутлоадером. Там в коде загрузчика был баг, который приводит к циклическому ресету после первого срабатывания ватчдога. А ты, если ватчдогом пользуешься - наверно сам забыл, что перешивал загрузчик на оптибут. В оптибуте бага нет.

Проверить, поддерживает загрузчик ватчдог или нет - просто.
Попробовать может каждый.
Заливаешь в Нану вот этот код и ждешь, пока сработает ватчдог. Если после ресета светодиоды на плате начнут быстро-быстро мигать - это он, циклический ребут
C++:
#include <avr/wdt.h>
int led =7;

void setup()
{
   wdt_disable();
   pinMode(led,OUTPUT);
   digitalWrite(led,HIGH);
   delay(16000);          // задержку НЕ УБИРАТЬ! А то потом будет трудно перешить обратно
   wdt_enable (WDTO_1S);
}

void loop()
{
    //wdt_reset();
    digitalWrite(led, ! digitalRead(led));
 
    delay(1000);
}
 
  • Лойс +1
Реакции: Leon111_09

Старик Похабыч

★★★★★★★
14 Авг 2019
4,192
1,281
Москва
Я на оптибут точно не перешивал. И из всех плат перешил только одну на новую версию. Именно нано. Но я не использовал штатную библиотеку. Может в этом все дело, поэтому видимо сам то он работает, но не работает при обращении через wdt_enable и прочие функции.
C++:
void(* resetFunc) (void) = 0;  // фнкция ресета, вызывает прерывание 0

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


ISR(WDT_vect) {
  WDTb++;
  if (WDTb > 2)
  {
    asm("wdr"); // Сбрасываем WDT
    delay(1000);
    resetFunc();
  }
}
 
  • Лойс +1
Реакции: Sergo_ST

bort707

★★★★★★✩
21 Сен 2020
2,899
862
Согласен с @Старик Похабыч, проблема не в загрузчике, а в том как работает либа с регистрами собаки))
ну как не в загрузчике, если с оптибутом на той же либе бага нет?

Добавлю - вот нашел инфу.
Дело не в регистрах ватчдога, а в правильной последовательности после ресета. Вот что написано в документации к либе

Note that for newer devices (ATmega88 and newer, effectively any AVR that has the option to also generate interrupts), the watchdog timer remains active even after a system reset (except a power-on condition), using the fastest prescaler value (approximately 15 ms). It is therefore required to turn off the watchdog early during program startup,

То есть в чипах Атмега88 и старше (в том числе в атмега328) после ресета по ватчдогу сам ватчдог остается включенным, причем с самой маленькой задержкой - 15мс. Таким образом, инициализация камня должна начинаться с выключения собаки, причем делать это надо в первые 15мс - то есть делать это должен бутлоадер, старт юзерского кода происходит позже. Если в буте этого выключения нет - будет циклический ребут.
 
Изменено:

Sergo_ST

★★★★★★✩
15 Мар 2020
821
745
Ну вот так)) В данном случае загрузчик не причина, а следствие проблемы.
Я не копался в стоковой либе от авр, но по всей видимости либа не полностью перенастраивает регистры, а только очищает/устанавливает те биты, которые требуются для выбранного режима, и похоже что бит WDE регистра WDTCSR просто не очищается.
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,192
1,281
Москва
Вообще ядра они такие. Я недавно снес у себя miniCore, как утратившую доверие.

Вот такой код на ней не работал корректно. Красные светодиоды на 6 и 7 пинах горели в пол яркости.
При этом если убрать pwr_inint(); то код работал как надо.
На GyverCore (поклон ;) ) все заработало как надо сразу. И еще на каком то ядре, Эдуард Анисимов может назвать на каком. Надобность в "другом" ядре возникла из за питания 328-ой от 3-3.3в и кварце на 8МГц.
C++:
#define BTN_KEY 2               
#define PWR_ENABLE A4           
#define CHRG_TEST A6
#define PWR_TEST A7

#define LED_GEEN_HI A3
#define LED_GEEN_LO 3
#define LED_ORANGE_HI 4
#define LED_ORANGE_LO 5
#define LED_RED_HI 6
#define LED_RED_LO 7

void setup() {
  led_init();
  pwr_inint();
  delay(1000);

  digitalWrite(LED_ORANGE_HI , 1);
  digitalWrite(LED_RED_HI , 1);
  digitalWrite(LED_RED_LO , 1);
}

void loop() {

}

void led_init()
{
  pinMode(LED_RED_HI , OUTPUT);
  pinMode(LED_RED_LO , OUTPUT);
  pinMode(LED_ORANGE_HI, OUTPUT);
  pinMode(LED_ORANGE_LO, OUTPUT);
  pinMode(LED_GEEN_HI, OUTPUT);
  pinMode(LED_GEEN_LO, OUTPUT);
}

void pwr_inint()
{
  pinMode(BTN_KEY, INPUT);
  pinMode(PWR_ENABLE, OUTPUT);
  pinMode(CHRG_TEST, INPUT);
  pinMode(PWR_TEST, INPUT);
}
 

bort707

★★★★★★✩
21 Сен 2020
2,899
862
@Старик Похабыч, это правильный метод использования твоего кода?
C++:
void(* resetFunc) (void) = 0;  // фнкция ресета, вызывает прерывание 0
volatile byte 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 ); // WD 2 сек
  // WDTCSR = (1 << WDP3) | (1 << WDIE ); // WD 4 сек
  sei(); // Разрешаем прерывания
}


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

void setup() {
delay(16000);
SetWDT();
}

void loop() { }
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,192
1,281
Москва
не совсем.
WDTb=0; в цикле loop, т.е. сбрасывать , что бы не было перезагрузки по вотчдогу.
И может потребоваться #include <avr/sleep.h>
 

Sergo_ST

★★★★★★✩
15 Мар 2020
821
745
@bort707, Это не совсем верно. То что собака продолжает работать после сброса это так, но, если бы это было так как описано выше, мы бы просто висели в вечном ребуте ещё на стадии работы загрузчика. То что случается ребут при настройке собаки виновата либа.
Поправлюсь в предположении проблемы, скорее виноват не очищенный бит WDRF регистра MCUSR или WDIF регистра WDTCSR.
 
Изменено:

bort707

★★★★★★✩
21 Сен 2020
2,899
862
@bort707, Это не совсем верно. То что собака продолжает работать после сброса это так, но, если бы это было так как описано выше, мы бы просто висели в вечном ребуте ещё на стадии работы загрузчика.
что значит "висели бы"? - это именно так и происходит - вечный ребут на стадии загрузчика.
Вы попробуйте залить в Нану со старым бутом мой код - увидите сами.
 

bort707

★★★★★★✩
21 Сен 2020
2,899
862
WDTb=0; в цикле loop, т.е. сбрасывать , что бы не было перезагрузки по вотчдогу.
И может потребоваться #include <avr/sleep.h>
WDTb=0; в цикле loop это понятно.
Но если мы хотим проверить срабатывание ватчдога, как раз WDTb=0; в цикле loop нам не нужен
Вот такой код получился, проверь, все верно?
C++:
#include <avr/sleep.h>
void(* resetFunc) (void) = 0;  // фнкция ресета, вызывает прерывание 0
volatile byte 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 ); // WD 2 сек
  // WDTCSR = (1 << WDP3) | (1 << WDIE ); // WD 4 сек
  sei(); // Разрешаем прерывания
}

ISR(WDT_vect) {
  WDTb++;
  if (WDTb > 2)
  {
    asm("wdr"); // Сбрасываем WDT
    delay(1000);
    resetFunc();
  }
}
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println("start!");
  delay(16000);
  SetWDT();
}

void loop() {
  Serial.println("loop");
  delay(10000);
  // put your main code here, to run repeatedly:

}
так вот, этот код на старом бутлоадере не работает.
Для чистоты эксперимента вскрыл пакетик с новой Наной. С твоим кодом циклический ребут.
Вывод в Монитор портв:
C++:
start!
loop
start!
start!
start!
то есть после первого срабатывания собаки программа перестает доходить до ЛУПа
 

Sergo_ST

★★★★★★✩
15 Мар 2020
821
745
Это означает, что мы даже не вышли бы за приделы загрузчика и не начинали исполнение основной программы. Те к настройке собаки из кода мы бы и не притронулись, парадокс))
Не знаю что там написано у вас, но работает всё нормально как и у @Старик Похабыч так и у меня, при правильной последовательности работы с регистрами проблем не должно быть.
Если интересно разобраться в чем именно проблема, нужно будет разобрать либу авр и сразу будет всё ясно))

Раз уж вы расчихлили новую нанку, давайте я щас по быстрому накидаю код и проверим?))
 

bort707

★★★★★★✩
21 Сен 2020
2,899
862
Это означает, что мы даже не вышли бы за приделы загрузчика и не начинали исполнение основной программы. Те к настройке собаки из кода мы бы и не притронулись, парадокс))
это так и есть
Не знаю что там написано у вас, но работает всё нормально как и у @Старик Похабыч так и у меня, при правильной последовательности работы с регистрами проблем не должно быть.
так не работает. только что проверил. с кодом похабыча циклический ребут.
Давайте свой код - проверю и его.

Ребята, прежде чем спорить - возьмите Нану со старым бутлоадером и попробуйте свой код сами. Или укажите мне на ошибку.
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,192
1,281
Москва
Вообще интересно. Нодо будет проверить. Скорее всего при перезагрузке вочдог продолжает работать. И эти висящие 16 сек не дают скинуть его.
 

Sergo_ST

★★★★★★✩
15 Мар 2020
821
745
Я бы не спорил, если бы не был уверен в обратном)) Лично у меня проверено на проекте дозиметра, там собака в роле основного таймера и никаких проблем ни у кого (в том числе и у меня) не было, что на старом загрузчике, что на новом))

Как я уже выше написал, если у Вас есть желание можем протестировать))
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,192
1,281
Москва
Тогда скорее всего не работает директива отключения вочдога. Значит если написать ее по другому (перед вызовом ресета) то должно заработать.
 

bort707

★★★★★★✩
21 Сен 2020
2,899
862
@Старик Похабыч, ватчдог после ресета сам не отключается, это его штатное поведение, так и даташите написано.
Разница между авр-вской либой и твоим кодом, что в либе после ресета таймаут переписывается на 15мс, а у тебя, похоже, остается 2сек, как настроено. Поэтому в твоем коде выполнение успевает дойти до сброса ватчдога, а в авр-либе - нет
Ща попробую сравнить, какие регистры пишет либа с теми. что у тебя.

надеюсь ты не в обиде, что я на "ты" обращаюсь? :)
 

bort707

★★★★★★✩
21 Сен 2020
2,899
862
Разобрался в разнице кода @Старик Похабыч и avrlibs. Распишу тут, может кому будет интересно.

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

А @Старик Похабыч использует ватчдог иначе - у него он настроен в "Interrupt Mode" - то есть по истечению таймера МК не ресетится, а лишь исполняется вектор прерывания. У Похабыча в этом прерывании анализируется его собственный счетчик WDTb. Если он не сброшен - контроллер не перезапускается, а происходит лишь сброс исполнения программы на нулевой адрес функцией
C++:
void(* resetFunc) (void) = 0;  // фнкция ресета, вызывает прерывание 0
Это так называемый софт-ресет, при этом переинициализации регистров МК не происходит и, соответвенно, никакие настройки ватчдога не сбиваются, поэтому после софт-ресета программа продолжает работать.

В таком подходе есть и минус - поскольку этот ресет "не настоящий" и не чистит регистры - соответвенно в этом случае программа не стартует "с чистого листа" и может тут же зависнуть снова.
 
Изменено:
  • Лойс +1
Реакции: Старик Похабыч