Comments 31
А можно немного покрасивей абстрагироваться. В LD дописать:
MEMORY
{
CCRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K
FLASH (rx) : ORIGIN = 0x08100000, LENGTH = 1024K
}
LD_FLASH_ORIGIN = ORIGIN(FLASH);
И далее в main:
extern char LD_FLASH_ORIGIN;
#define START_ADDR ((uint32_t)&LD_FLASH_ORIGIN)
int main(void)
{
/* Пока не будет передан новый адрес таблицы векторов,
другие функции не должны вызываться */
__disable_irq();
SCB->VTOR = START_ADDR;
__enable_irq();
/* Остальная программа начинается тут */
while(1)
{
}
}
Загружаясь в оперативную память, не забывайте, что после любого reset-a ваша программа будет стерта и придется ее прожигать заново.
Прожигать в оперативную память?
Загружаясь в оперативную память, не забывайте, что после любого reset-a
ваша программа будет стерта и придется ее прожигать заново.
Применительно к ОЗУ термин "прожигать" звучит несколько зловеще...
Упс... Меня опередили. Но значит не только меня это "торкнуло"
Применительно к ОЗУ термин "прожигать" звучит несколько зловеще...
Обычно программист микроконтроллеров работает в отделах, которые по советских называются: "отдел разработки радио электронной аппаратуры" (РЭА).
Поэтому, как следует из названия, начальник там - бывший схемотехник. Всё начальство программистов микроконтроллеров в российских организациях в пяти случаях из шести - это в прошлом схемотехники, чертежники или вовсе конструкторы механики.
О программировании они знать ничего не хотят из принципа, они из тех кто просто ненавидит программировать еще с института.
Начальник-схемотехник даже программирование называет "программизмом", прошивание называет "прожиганием", а самих программистов-микроконтроллеров "софтописцами".
... прошивание называет "прожиганием" ...
Вообще то "прошивание" еще более древний термин, чем "прожигание"
"Прошивали" ПЗУ, когда оно выполнялось на ферритовых сердечниках, причем "прошивали" в буквальном смысле.
"Прожигать" ПЗУ стали, когда появились микросхемы однократно программируемого ПЗУ с пережигаемыми перемычками, типа советской серии К556.
Правильнее выполнить перенос таблицы векторов прерываний в обработчике прерывания Reset, так как main не первая выполняемая функция.
В моих проектах у меня еще не было ни разу обращения к таблице векторов до функции main, поэтому все работает отлично, но не исключаю, что в других проектах, можно столкнуться с такой проблемой. Хотя я пока не представляю задачи, где таблица векторов может понадобиться еще до main.
Даже после выполненных действий при загрузке открывается страница с ошибкой "Break at address "0x8000f40" with no debug information available, or outside of program code.", а в дизассемблере видно попытки чтения данных с адреса 0x08000000, откуда оно и берет переход по адресу 0x08000f40. Но программа загружается и работает.
Перед тем как загрузить программу по адресу 0x08000F40, попробуйте полностью очистить флешку.
Вы не поняли. У меня загрузчик начинается с 0x08000000, а основная программа с 0x08010000. Так вот когда загружаю основную программу, она зачем то лезет в область загрузчика за адресами, берет оттуда не правильные адреса и пытается их выполнить, естественно при этом выплевывая ошибку. Такое ощущение что недостаточно указать адреса в файле .Id, надо IDE объяснить, что старт программы осуществлять тоже с других адресов.
Если у вас по адресу 0х08000000 уже есть программа она ничего не знает, о приложении которое лежит в более старших адресах и вы сами должны перейти в bootloader на стартовый адрес основной программы. Примерно так будет выглядеть код перехода на основную программу в bootloader.
#define MAIN_PROGRAM_START_ADDRESS (uint32_t)0x0800A000
int main () {
__disable_irq();
NVIC_SetVectorTable(NVIC_VectTab_FLASH, MAIN_PROGRAM_START_ADDRESS);
jumpAddress = *(__IO uint32_t*) (MAIN_PROGRAM_START_ADDRESS + 4);
Jump_To_Application = (pFunction) jumpAddress;
__set_MSP(*(__IO uint32_t*) MAIN_PROGRAM_START_ADDRESS);
Jump_To_Application();
}
Так и происходит, если прошивать МК готовыми бинарниками: сначала запускается бутлодер потом он передает управление основной программе. С этим проблем нет, все работает. Проблема начинается когда я пытаюсь в IDE отладить основную программу. При заливки программы в режиме Debug выходит ошибка, о которой я писал выше. Но программа заливается и стартует, правда со своими приколами. При попытке сделать reset, она опять берет не правильные адреса и крашится.
Ну в целом если вы вызываете Reset вы и должны попадать сначала в загрузчик, попробуйте его запускать в режиме отладки с залитым основным приложением и посмотрите откуда идет обращение.
Так я хочу отладить основную программу, а не бутлодер.И по моей логике, если я запустил отладку основного приложения и указал в .Id что оно у меня начинается с адреса 0x08010000, то после ресета через IDE я должен попасть на дрес 0x08010000, а не на 0x08000000. И при старте отладки я должен попасть на адрес со смещением, а не на дефолтный.
Я не уверен, что так можно сделать за счет этих настроек. В CubeIde не знаю, можно или нет, но в keil можно было в качестве костыля прикрепить *.ini файл c PC = 0x0800A000, и тогда отладка начиналась с нужного адреса.
# Reconfigure vector table offset register to match the application location
set *0xe000ed08 = 0x8010000
# Get the application stack pointer (First entry in the application vector table)
set $sp = *(unsigned int*)0x8010000
# Get the application entry point (Second entry in the application vector table)
set $pc = *(unsigned int*)0x8010004
Наверное, имелось в виду это? Прописать в Startup Scripts
Сдается мне, то что вы указываете а линкере и то, с чего должна начинаться отладка с самим линкером слабо связаны. Ведь в линкере вы указываете то, с какого адреса будет заливаться флэш. А отладка у вас начинается с стандартного адреса начала флэш (0x08000000). В том же кейле, по-моему, есть возможность явно это указать.
Осторожнее с таким кодом. Как было сказано в комментариях к этой статье, __set_MSP() - это зло. Если вдруг переменная Jump_To_Application хранится в стеке, то после set_MSP она превратится в тыкву. И это действительно так, я натыкался на эту проблему. Шаманство с отключением оптимизаций кода помогает, но это некрасиво и ненадёжно. Проверенное решение (GCC) такое:
uint32_t topOfMainStack = *(uint32_t*)MAIN_PROGRAM_START_ADDRESS;
__ASM volatile("msr msp, %0 \n bx %1" :: "r" (topOfMainStack), "r" (JumpAddress));
Компилятор с любыми флагами оптимизациями предварительно загрузит значение в регистр %1, затем загрузит указатель стека, и затем перейдёт по адресу из регистра %1. Ассемблерная вставка не оптимизируется, т.е. в любом случае сначала выполнится команда msr, затем bx.
Например, O0:
...
801ed9c: 68fb ldr r3, [r7, #12]
801ed9e: 693a ldr r2, [r7, #16]
801eda0: f383 8808 msr MSP, r3
801eda4: 4710 bx r2
Или Os+LTO:
...
80004b8: 6823 ldr r3, [r4, #0]
80004ba: f383 8808 msr MSP, r3
80004be: 4728 bx r5
Практически ничего не поменялось, только использованы другие регистры.
Это не программа "лезет" в область загрузчика. Прочитайте как просходит старт мк на базе Arm CortexM. Если хотите, чтобы стартовало с 0x08010000, прошейте это значение в 0x08000000 (точнее не само значение 0x08010000, а то, что лежит по этому адресу. И по-хорошему надо скопировать и следующие 4 байта).
Хотя если и это не помогает, возможно, что это такие кренделя самого КубИда.
Какой МК вы используете?
Прошивка из RAM памяти исполняется быстрее чем из Flash?
Всем привет!
А что делать если загрузчик не может запустить прошитую программу?
Имеется ввиду прошивку настроил как надо:
в *.ld указал смещение адреса ORIGIN = 0x08008000, LENGTH = 224K
в system_stm32f1xx.c тоже указал #define USER_VECT_TAB_ADDRESS и смещение VECT_TAB_OFFSET 0x8000
вначале main() тоже указал
SCB->VTOR = START_ADDR;
Скомпилировал, прошил с адреса 0x08008000.
Но после работы загрузчика переход происходит, но программа не запускается.
Создавал проект загрузчика и тестовой программы в CubeIDE 1.16.
Самое главное что, раньше в uKeil и даже в System Workbench for STM32 все это работало на ура. А вот в CubeIDE не могу понять что не так.
Кстати, если в качестве тестовой программы залить прошивку скомпилированную с проекта System Workbench for STM32 то она запускается. т.е. получается, что где-то в настройках CubeIDE нужно еще что-то сделать, чтобы запуск программы удался.
Кто нибудь уже сталкивался с такой проблемой?
Хотел бы дополнить свой комментарий.
Все выше описанное я делал на чипе STM32F107VCT6 с пакетом обновлений FW_F1 V1.8.5 в CubeIDE 1.16.0, CubeMX 6.12.0 и программа не запускалась.
Я решил попробовать создать загрузчик и программу для другого чипа: STM32F417VGTx, и к моему удивлению все заработало! Загрузчик запускался и передавал управление программе прошитой со смещением.
Также проверил это и для чипа STM32F103C8Tx - тоже все работает как надо.
Сравнил файлы *.ld разных чипов. Ничего не обычного не нашел, что могло бы помешать запуску программы со смещением.
Единственное что я еще не сделал, так не пробовал понизить версию пакета обновлений. Возможно в этом есть смысл.
Как сделаю, отпишусь...
Прошиваем программу не только в начало FLASH памяти STM32