в этом простом скетче указатель стека не меняется?
Я экспериментирую с переключением контекста, и моя первая идея — поиграть с SP и посмотреть, как он изменится. Однако Sp не делает то, что я ожидаю. Скетч ниже. Я использую Uno R3. Я ожидаю увидеть, что SP сначала должен распечатать какое-то значение, а затем оно должно быть на 4 целых числа дальше (4x2 байта?) при печати во второй раз. Но этого не происходит, и оба раза я вижу одно и то же значение SP. В чем может быть проблема?
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
Serial.print("X = ");
Serial.println(SP,HEX);
}
void loop() {
// put your main code here, to run repeatedly:
Serial.print("BEFORE: ");
Serial.println(SP,HEX);
int y = 15;
int z = y;
int g = z + y;
int dsf = z-y;
Serial.println(dsf+g); //this should make these variables DO something...
Serial.print(" After = ");
Serial.println(SP,HEX);
delay(1000);
}
2 ответа
Лучший ответ:
Невозможно таким образом угадать компилятор. Вам нужно понять несколько вещей:
- Компилятор оптимизирует любые переменные, которые просто присваиваются другим переменным в ходе вычислений и больше не используются.
- ЦП AVR имеет 32 регистра. Более чем достаточно, чтобы не нуждаться использовать ОЗУ для хранения значений для такого простого скетча.
Сделать переменные изменчивыми
, безусловно, поможет, поскольку это заставляет компилятор помещать их в память. Однако вам действительно следует изучить ассемблерный код, который создает компилятор, чтобы точно знать, что он делает.
Другой способ оптимизации компилятора — встроенная компиляция небольших или одноразовых функций. То, что вы ожидали, будет заключено в последовательность вызова и возврата, может и не быть.
Вставка __attribute__ ((noinline))
перед определением функции может подавить эту оптимизацию:
__attribute__ ((noinline)) void foo(int bar)
{
// do something
}
подавляет встраивание в функцию foo(). Встраивание не раз мешало моим исследованиям, поэтому я создал этот более простой для запоминания макрос. (Ключевое слово — это просто __NOINLINE
; предложение #else используется для того, чтобы «кто-то» случайно не попытался использовать декларатор «__attribute...» с другим компилятором, который этого не делает. не узнаю).
// Don't compile this function inline
// '__NOINLINE void foo(void){ ... }'
#ifdef __GNUC__
#define __NOINLINE __attribute__ ((noinline))
#else
#define NOINLINE error 'NOINLINE' not defined for this compiler
#endif
Даже небольшие многофункциональные функции могут быть встроены., @Nick Gammon
Я заметил, что gcc более склонен к встраиванию, поскольку Arduino IDE начала предоставлять ему опцию компилятора -flto
. До этого момента он не встраивал никакие нетривиальные нестатические функции., @Edgar Bonet
- Программирование микроконтроллера Attiny85 без arduino
- Заменить предохранители Arduino Uno (может ли Arduino Uno заменить свои собственные предохранители?)
- Arduino EEPROM сохраняет старые данные после прошивки новой программой
- Как добавить два сборочных массива в arduino
- о том, как разделить один внешний последовательный EEprom с помощью двух микроконтроллеров?
- BASCOM-AVR stk500_2_ReceiveMessage(): время ожидания: -1
- Заставить TCNT оставаться ниже OCRxA на ATmega328P
- Что такое метод распределения памяти в Arduino
Почему вы предполагаете, что стек изменится? Ядро AVR имеет 32 регистра. Много места для хранения небольших временных переменных, подобных этим. Добавьте к этому тот факт, что компилятор оптимизирует множество вещей, которые ему не нужны, и вы получите почти ничего., @Majenko
Верный пункт. Однако в аналогичном скетче, который на самом деле вызывал функцию, единственной ролью которой была распечатка SP, SP по-прежнему не менялся. Если я понимаю вызовы функций (а возможно, и нет), то, по крайней мере, адрес возврата должен находиться в стеке в этот момент, не так ли?, @Michael Stachowsky
Вам следует изучить скомпилированный ассемблерный код, чтобы понять, что он на самом деле делает., @Majenko
Вам также следует прочитать это: https://gcc.gnu.org/wiki/avr-gcc - особенно разделы соглашения о вызовах и макета кадра., @Majenko
Спасибо, сделаю. Если вы суммируете комментарии в качестве ответа, я приму, @Michael Stachowsky
Вы можете использовать setjmp/longjmp для переключения контекста. В качестве примера см. https://github.com/mikaelpatel/Arduino-Scheduler/blob/master/src/Scheduler.cpp#L124., @Mikael Patel
@MikaelPatel: я установил/перепрыгнул кучу в предыдущем коде. Однако я хочу сделать это самостоятельно, чтобы посмотреть, как это делается. В противном случае я бы просто скачал FreeRTOS или что-то в этом роде., @Michael Stachowsky
@MichaelStachowsky Вот это дух! Вот ссылка, которая может дать некоторое представление. https://github.com/vancegroup-mirrors/avr-libc/blob/master/avr-libc/libc/stdlib/setjmp.S Написание этого на C/C++ создаст дополнительную проблему :), @Mikael Patel