Сброс Arduino Uno в коде

Можно ли сбросить Arduino (т. Е. Перезагрузить его) из кода (т. Е. Из самого скетча)? Я знаю, что это возможно с помощью специальной схемы, но есть ли шанс сделать это просто с помощью кода?

Ниже приведен мой код и комментарий //reset, где я хочу принудительно сбросить.

#include <TrueRandom.h>

int i;
int randSeed;
long randNumber;

void setup(){
  Serial.begin(9600);
  Serial.println("20 pseudo Zufallszahlen:");
  for (i=1;i<=20;i++) Serial.print(random(10));
  Serial.println();
  Serial.println();
  //randomSeed(TrueRandom.random());
  randSeed = analogRead (A0);
  randomSeed(randSeed);
  Serial.print("Der 'seed' Wert: ");
  Serial.println(randSeed);
  Serial.println();
  Serial.println("20 Zufallszahlen mit analogem 'seed' Wert:");
  for (i=1;i<=20;i++) Serial.print(random(10));
  Serial.println();
  Serial.println("---------------------------");
  Serial.println();
  delay(500);
  //reset  
}

void loop() {
}

Я хочу сбросить микроконтроллер в конце функции настройки, чтобы показать эффект случайных чисел с начальным числом и без него.

, 👍22


2 ответа


Лучший ответ:

25

Для этого есть три способа. (последнее - мое любимое)

1) Соедините неиспользуемый ввод-вывод с выводом СБРОСА. Оставьте его в качестве входных данных для обычного бегите, Так как он внешне подтянут высоко. И при желании сбросить установите его как НИЗКИЙ и выходной. (хлоп его перезагрузка).

setup() {
  ...
  pinMode(PINtoRESET, INPUT);    // Просто для ясности, так как по умолчанию используется ВВОД. На самом деле в этом не было необходимости.
  digitalWrite(PINtoRESET, LOW); // Запускает его, но фактически не устанавливает вывод. 
  ...                            // Отключает подтягивание на 10 Км, но кого это волнует.

затем, когда захочется...

...
  pinMode(PINtoRESET, OUTPUT);   // выключен свет. Предполагая, что это правильно.
  while(1);                      // никогда не попадает сюда.

2) Перейдите к началу кода.

void(* resetFunc) (void) = 0;  // объявление функции сброса по адресу 0
...
resetFunc(); //сброс вызова

Но будьте осторожны, это не приведет к истинному сбросу, так как НЕ все регистры ПО УМОЛЧАНИЮ. Скорее они и ИО остаются как есть. Где что-то из загрузчика, а затем куча будет инициализирована. И это не так!

3) Используйте сторожевого пса. Библиотека SoftReset упрощает это. Хотя это нетрудно реализовать напрямую. Показано ниже..

#include <avr/wdt.h>
...
setup() {
  ...
  MCUSR = 0;  // снимите все флаги предыдущих сбросов.
  ...

затем, когда захочется...

...
wdt_enable(WDTO_15MS); // включите WatchDog и не трогайте его.
for(;;) { 
  // ничего не делайте и ждите окончательного...
} 
...
,

Из всего, что я прочитал, первый вариант не рекомендуется. Варианты 2 и 3 подходят., @sachleen

@sachleen: Пожалуйста, расскажите об этом подробнее (вариант 1)., @JRobert

Вариант 1. или вариант может быть достаточно чистым способом сделать это, пока рассчитаны переходные условия включения питания., @Russell McMahon

@RussellMcMahon - [Как спроектирован переходный процесс включения питания conditions](http://arduinoprosto.ru/q/13416/how-does-one-design-for-the-power-on-transient-conditions)?, @Greenonline

Следует отметить, что вариант 2 не запускает загрузчик., @Edgar Bonet

Разве вызов "resetFunc ()" не приведет к сбросу стека? Поэтому повторное его вызов приведет к переполнению стека., @Kevin Smyth

Вариант 1 не рекомендуется разработчиком микросхем Atmel. Они не рекомендуют выполнять сброс с помощью выводов чипа., @Javier

Вариант 3 работает только с загрузчиками, у которых включен WDT. В противном случае он переводит плату в цикл сброса., @zgoda

Вариант 2 выполняет тот же код, который выполняется при сбросе оборудования или включении питания: чип всегда начинается с адреса 0., @SDsolar

Вариант 2 следует отредактировать, чтобы добавить тот факт, что не каждый чип начинается с адреса "0". Вместо этого следует использовать указатель на вызываемую функцию "void(* resetFunc) (void) = &setup;" (в этом случае настройка должна быть вызвана для повторного запуска или даже main, если это имеет смысл) У меня только что был случай со stm32, когда он начинается позже в памяти., @Dardan Iljazi

Вариант 2-это присвоение нулевого указателя указателю функции, который фактически ни на что не указывает при вызове функции. Вызов этого является незаконным, вызывает исключение с нулевым указателем и без обработки исключений приводит к перезагрузке. Это грязный трюк, и рекомендовать его ненадежно. Для всех верующих указатель является указателем на ЭНЕРГОНЕЗАВИСИМУЮ область памяти, а не на программную память (флэш-память). @Dardanboy, вызов setup() не является сбросом, и да, вся память начинается с местоположения 0, однако может быть возможно, что доступна не вся меньшая память, @Codebeat

@Codebeat Нет, это действительный адрес памяти во флэш-памяти программы, с которого начинается встроенная программа., @Dmitry Grigoryev

@DmitryGrigoryev Нет, это допустимое смещение, на которое вы можете перейти, а не действительный адрес. Не путайте эти два понятия., @Codebeat

Я смеюсь над комментарием "отбой" — спасибо. Жаль, что у меня нет запасного контакта ввода-вывода., @Eric Nelson


4

Если у вас есть оригинальный загрузчик Arduino, который вы хотите выполнить как часть сброса, вы можете выполнить сброс SW, перейдя к адресу сброса загрузчика (0x7800 на платах ATmega328P).

void reset() { asm volatile ("jmp 0x7800"); }

Подход сторожевого сброса не будет работать из-за ошибки в загрузчике. Вот примечание от ATmega328P Datasheet страница 45:

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

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

,

Да, я наблюдал вечный цикл тайм-аутов, отвечая на вопрос: "Интересно, сбрасывает ли загрузчик WDT", почему бы просто не сделать "прыжок 0x0" ?, @Eric Nelson

Переход @EricNelson на 0x0 не приведет к запуску загрузчика, что является одним из двух основных случаев использования для сброса программного обеспечения. Второй вариант использования, сброс системы в известное состояние, невозможен без загрузчика с поддержкой WD или подключения контакта сброса к GPIO., @Dmitry Grigoryev

@DmitryGrigoriev По какой-то причине Visual Studio считает, что это неверный синтаксис. Он подчеркивает точку с запятой красного цвета, говоря, что ожидается '('. Однако код скомпилируется. Я предполагаю, что это какое-то несоответствие между Visual Studio и Visual Micro (плагин arduino для VS). Тем не менее, Arduino также сбрасывается, если я не указываю ключевое слово Летучее (и таким образом Intellisense не жалуется). Есть ли у этого какие-либо побочные эффекты?, @Bas

@Bas летучий предотвращает чрезмерную оптимизацию вашего кода компилятором. Технически компилятору разрешено полностью исключить функцию сброса, поскольку она не имеет побочных эффектов, видимых компилятору C. Конечно, современные компиляторы, как правило, достаточно умны, чтобы этого не делать (например, в GCC нет необходимости использовать voluty, который знает, что оптимизация jmp запрещена), но, строго говоря, сброс не гарантированно произойдет без voluty на случайный компилятор., @Dmitry Grigoryev

Спасибо @DmitryGrigoriev! Оказывается, Intellisense, считающая неверный синтаксис, на самом деле была старой версией Intellisense в сочетании с VMicro. Обновление VMicro решило проблему. В любом случае, теперь это работает и с ключевым словом Volatible., @Bas