Программная перезагрузка Arduino

Иногда возникает ситуация, когда необходимо перезагрузить Arduino, чтобы программа стартовала с самого начала, и сделать это изнутри кода.

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

Дисклеймер

Хочу сразу оговориться о двух вещах:

1. Программый сброс не сбросит в ноль все регистры микроконтроллера, как это происходит при аппаратной перезагрузке. Программный сброс приведет к тому, что вся периферия, которая настраивается ардуиной, настроится заново (таймеры, АЦП, прерывания), все ваши глобальные переменные вернутся в начальное состояние, заново выполнится всё, что вы поместили в setup().

2. Для вас как пользователя не имеет значения, как именно вы заставили счетчик программ прыгнуть на адрес 0. Здесь все способы хороши, я приведу два, которые равноправны с функциональной точки зрения, а отличаются только с эстетической.

Поехали!

 

Способ 1. Ассемблерная вставка

Не возбраняется при программировании на Arduino напрямую вставить в код ассемблерные инструкции. Это делается так:

1
asm volatile("инструкция1\n инструкция2\n инструкиця3\n");

 То есть инструкции по очереди складываются в текст и отдаются функции asm volatile(), причем они разделяются символом переноса строки.

Давайте вставим туда инструкцию прямого перехода на адрес. Наш скетч будет выглядеть так:

1
2
3
4
5
void setup(){}
  
void loop(){
	asm volatile("jmp 0x00");
}

Соберем наш код, и посмотрим дамп функции loop():

1
2
3
00000092 <loop>:
  92:	0c 94 00 00 	jmp	0	; 0x0 <__vectors>
  96:	08 95       	ret

Видно, что программа делает аккуратно то, что мы ей сказали - прыгает на 0.

 

Способ 2. Указатель на функцию

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

1
2
3
4
5
6
void setup(){}
 
void loop(){
	void (* reboot)(void) = 0;
	reboot();
}

Итого в loop() мы написали две строчки: объявление указателя на функцию reboot() с адресом 0 и вызов функции reboot().

Давайте соберем код и посмотрим на дамп функции loop():

1
2
3
4
00000000 <loop>:
   0:	e0 e0       	ldi	r30, 0x00	; 0
   2:	f0 e0       	ldi	r31, 0x00	; 0
   4:	09 94       	ijmp

Вот это уже интересно!

Инструкция  ldi (load immediate) - загружает указанное значение в регистр. Два ее операнда - это регистр и значение. Мы видим, что в программе в регистры r30 и r31 загружаются нули.

Инструкция ijmp (indirect jump) - это косвенный переход на (Z). Z в архитектуре AVR - это название пары регистров, которые служат для косвенной адресации, кроме него для этих целей еще есть X и Y. Два регистра, входящие в состав Z называются r30 и r31. Пазл сошелся. 

 

Если было лень читать

Краткое резюме.

Программно перезагрузить Arduino можно, вставив в произвольное место такой код:

1
asm volatile("jmp 0x00");

или такой код:

1
2
void (* reboot)(void) = 0;
reboot();

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

При программной перезагрузке периферия ардуины типа АЦП, таймеров и прерываний настроится заново, вашим глобальным переменным присвоятся начальные значения, заново выполнится setup().

 

Метки: программирование, Arduino, hack, код, хардкор, asm

Комментарии   

+2 #2 Илья Дубков 20.04.2017 10:12
Цитирую IVAN222:
Как мне сделать чтобы ардуинка перегружалась сама через 15 минут.


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

Вставь вот эту строчку в loop():
if (millis() > 900000) asm volatile("jmp 0x00");

готово.
Цитировать
0 #1 IVAN222 19.04.2017 21:49
Как мне сделать чтобы ардуинка перегружалась сама через 15 минут.
Цитировать

Добавить комментарий