В вопросе прикладного программирования
Я работаю над проектом на STM32L152RCT6, где мне нужно создать механизм для самостоятельного обновления кода из вновь созданного файла (HEX-файл). Для этого я реализовал такой механизм, как загрузчик, где он проверяет наличие новой прошивки, если там он должен перепроверить, и если он найден действительным, он должен храниться в "Местоположении приложения".
Я делаю следующие шаги.
- Адрес загрузчика = 0x08000000
- Адрес приложения = 0x08008000
- Где-то в указанном месте он должен проверить наличие нового файла через программу Boot Loader.
- Если он признан действительным, он должен быть скопирован на весь HEX (согласно руководству).
- Чем запуск кода приложения через переход по этому месту.
Теперь проблема исходит от шага 5, все вышеперечисленные шаги, которые я сделал, даже хранение данных были выполнены правильно (проверьте в утилите STM32), но когда я перехожу к коду приложения, оно не будет работать.
Есть ли необходимость в перекрестной проверке или что-то, чего мне не хватает?
3 ответа
В отличие от других контроллеров ARM, которые при сбросе напрямую переходят к адресу 0, серия Cortex-M берет начальный адрес из таблицы векторов. Если программа загружается напрямую (без загрузчика), таблица векторов находится в начале двоичного файла (загружается или отображается на адрес 0). Первая запись со смещением 0 является начальным значением указателя стека, вторая запись по адресу 4 называется вектором сброса, она содержит адрес первой команды, которая должна быть выполнена.
Программы, загруженные с помощью загрузчика, обычно сохраняют это расположение и помещают векторную таблицу в начало двоичного файла, 0x08008000
в твоем случае. Тогда вектор сброса будет 0x08008004
, Но это ваше приложение, вы должны проверить, куда вы положили таблицу векторов. Подсказка: посмотрите на .map
файл, сгенерированный компоновщиком, чтобы быть уверенным. Если это действительно в 0x08008000
, затем вы можете перенести управление в вектор сброса приложения так:
void (*app)(void); // declare a pointer to a function
app = *(void (**)(void))0x08008004; // see below
app(); // invoke the function through the pointer
Сложное приведение во второй строке преобразует физический адрес в указатель на указатель на функцию, принимает указанное значение, которое теперь является указателем на функцию, и присваивает его app
,
Затем вы должны управлять переключением на векторную таблицу приложения. Вы можете сделать это либо в загрузчике, либо в приложении, или разделить шаги между ними.
- Отключите все прерывания и остановите SysTick. Обратите внимание, что SysTick не является прерыванием, не вызывайте
NVIC_DisableIRQ()
в теме. Я бы сделал этот шаг в загрузчике, чтобы он отвечал за отключение того, что он включил. - Присвойте новый адрес таблицы векторов
SCB->VTOR
, Остерегайтесь, что шаблонSystemInit()
функция вsystem_stm32l1xx.c
безусловно измененияSCB->VTOR
вернуться к началу вспышки, т.е.0x08000000
, вы должны отредактировать его, чтобы использовать правильное смещение.
Вы также можете загрузить значение указателя стека из таблицы векторов, но это сложно сделать правильно, и в этом нет необходимости, приложение может просто продолжать использовать стек, который был настроен в загрузчике. Просто проверьте, чтобы убедиться, что это разумно.
Вы изменили приложение в соответствии с новой позицией фальши?
Например, Векторная таблица должна быть правильно настроена через
SCB->VTOR = ...
Когда ваш загрузчик запускает приложение, оно должно настроить все обратно на состояние сброса, поскольку приложение может перевести значения сброса по умолчанию. Особенно вам нужно:
- Возврат значений всех аппаратных регистров к значениям сброса
- Выключите все периферийные часы (не забывайте о SysTick)
- Отключить все разрешенные прерывания
- Возврат всех часовых доменов к значениям сброса.
- Установить адрес таблицы векторов
- Загрузите указатель стека с начала таблицы векторов APP.
- Позвоните в точку входа APP (начало таблицы vertor + 4).
Ваше приложение должно быть скомпилировано и связано с использованием пользовательского сценария компоновщика, где начальная точка FLASH равна 0x8008000.
например:
FLASH (rx) : ORIGIN = 0x8000000 + 32K, LENGTH = 512K - 32K
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;
где значение FLASH_BASE должно быть равно адресу значения IROM в KEIL
пример:
#define FLASH_BASE 0x08004000