Ответ 1
В NULL
Адрес 0 не содержит нулевой указатель. "Нулевой указатель" - это нечто более абстрактное: специальное значение, которое применимые функции должны признать недопустимыми. C говорит, что специальное значение равно 0, и, хотя язык говорит о разыменовании, это "поведение w90 > ", в простом мире микроконтроллеров он обычно имеет очень четко определенный эффект.
Загрузочные загрузчики ATmega
Обычно на reset программный счетчик AVR инициализируется на 0, поэтому микроконтроллер начинает выполнять код по адресу 0.
Однако, если установлен Boot Reset Fuse ( "BOOTRST" ), счетчик программ вместо этого инициализируется адресом блока в верхнем конце памяти (где это зависит от того, как установлены предохранители, см. спецификацию (PDF, 7 МБ) для специфики). Код, который начинается там, может что-то сделать, если вы действительно хотите, чтобы вы могли разместить свою собственную программу там, где вы используете ICSP (загрузчики обычно не могут перезаписывать себя).
Часто, это специальная программа загрузчик — которая может считывать данные из внешнего источника (часто через UART, я 2 C, CAN, и т.д.), чтобы переписать программный код (сохраненный во внутренней или внешней памяти, в зависимости от микро). Обычно загрузчик обычно ищет "специальное событие", которое может буквально быть чем угодно, но для разработки наиболее удобно что-то на шине данных, из которого он вытащит новый код. (Для производства это может быть особый логический уровень на штыре, поскольку его можно проверить почти мгновенно.) Если загрузчик видит специальное событие, он может войти в режим начальной загрузки, где он будет перепрограммировать память программы, иначе он пройдет контроль отключить код пользователя.
Как и в стороне, точка плавкого предохранителя загрузчика и верхнего блока памяти позволяет использовать загрузчик без каких-либо изменений в исходном программном обеспечении (при условии, что он не будет полностью распространяться на адрес загрузчика), Вместо того, чтобы мигать только с оригинальным HEX и желаемыми предохранителями, можно мигать исходным HEX, загрузчиком и модифицированными предохранителями, а также добавлен предварительный загрузчик.
В любом случае, в случае с Arduino, который, как я полагаю, использует протокол из STK500, он пытается связаться через UART, и если он не получит ответа в течение отведенного времени:
uint32_t count = 0;
while(!(UCSRA & _BV(RXC))) { // loops until a byte received
count++;
if (count > MAX_TIME_COUNT) // 4 seconds or whatever
app_start();
}
или если он слишком сильно ошибся, получив неожиданный ответ:
if (++error_count == MAX_ERROR_COUNT)
app_start();
Он передает управление обратно в основную программу, расположенную в 0. В источнике Arduino, показанном выше, это делается путем вызова app_start();
, определенного как void (*app_start)(void) = 0x0000;
.
Поскольку он используется как вызов функции C, прежде чем ПК перейдет на 0, он вытолкнет текущее значение ПК в стек, который также содержит другие переменные, используемые в загрузчике (например, count
и error_count
сверху). Означает ли это кражу оперативной памяти из вашей программы? Ну, после того, как ПК установлен в 0, операции, которые выполняются, явно "нарушают" то, что должна сделать надлежащая функция C (которая в конечном итоге возвращается). Среди других шагов инициализации он сбрасывает указатель стека (эффективно стирает стек вызовов и все локальные переменные), восстанавливая ОЗУ. Глобальные/статические переменные инициализируются до 0, адрес которых может свободно перекрываться с тем, что использовал загрузчик, потому что загрузчик и пользовательские программы были скомпилированы независимо.
Единственными прочными эффектами от загрузчика являются модификации аппаратных (периферийных) регистров, которые хороший загрузчик не покидает в неблагоприятном состоянии (включение периферийных устройств, которые могут потерять энергию при попытке спать). Как правило, хорошая практика также полностью инициализирует периферийные устройства, которые вы будете использовать, поэтому даже если загрузчик сделал что-то странное, вы установите его так, как хотите.
Загрузители ATtiny
На ATtinys, как вы уже упоминали, нет роскоши для плагинов или памяти для загрузчика, поэтому ваш код всегда будет начинаться с адреса 0. Возможно, вы можете поместить ваш загрузчик на несколько более высоких страниц памяти и указать свой Reset в нем, тогда всякий раз, когда вы получаете новый шестнадцатеричный файл для флеш-памяти, возьмите команду, которая по адресу 0: 1, замените ее адресом загрузчика, а затем сохраните замененный адрес в другом месте, чтобы вызвать нормальное выполнение. (Если это RJMP
( "относительный скачок" ), значение, очевидно, необходимо будет пересчитать)