|
STM32 bootloader, написание собственного бута |
|
|
|
Jan 3 2012, 13:31
|

Участник

Группа: Участник
Сообщений: 32
Регистрация: 3-02-11
Из: Украина, Киев
Пользователь №: 62 695

|
Всем привет. Появилась задача создать бутлоадер, который будет удаленно перепрошивать контроллер. Контроллер работает в связке с gsm-модулем. Раньше опыта создания бутов не было, поэтому вопросов появилось просто масса. Во-первых, для себя я вижу 2 концепции бутлоадера, каждый со своими плюсами и минусами. 1. Основная программа качает прошивку, пишет её в определенное место флеша(например, с 16-й страницы флеша), проверяет, все ли правильно записалось, устанавливает в энергонезависимом регистре флаг, что нужно войти в бут, перезагружается, происходит вход в бутлоадер, который очищает основную программу(например, со 2й страницы флеша) и перезаписывает новую прошивку на это место и переходит на выполнение основной программы. Преимущества(+)/недостатки(-): +простота бутлоадера, в том числе не нужно инициализировать юарт и модуль из бутлоадера. +не нужно долго висеть в буте +перезагружаться в бут можно только после того, как прошивка успешно закачана -если не верно закачалась прошивка либо же не рабочая прошивка - только вручную перепрошивать -нужен МК с бОльшим обьемом Flash 2. Основная программа перегружает МК в бут, который удаляет старую прошивку, качает новую и сразу записывает её вместо старой. Преимущества(+)/недостатки(-): +бут может сам скачать новую прошивку +контроллер с меньшим обьемом флеша -сложность бута - нужно будет иниициализировать юарт, включать и инициализировать gsm модуль. -долго находиться в бутлоадере Какой вариант лучше? И, может, есть более совершенные решения? Теперь вопросы по реализации: 1. Какие подводные камни могут быть в написании бута? 2. Читал на форуме про то, что нужно перезаписывать таблицу векторов(или вектора прерываний?)? Можно об этом подробнее? Как это делается? Где об этом можно подробнее почитать, а то никакой вразумительной инфы не нашел. 3. Бут и основная программа пишуться ведь как 2 разных проекта? Отадельным вопросом - как заставить программу перейти на выполнение с определенного адреса? Где найти об этом информацию? Пробывал как в примере от ST: CODE #include "common.h" #define ApplicationAddress 0x08000073
extern pFunction Jump_To_Application; extern uint32_t JumpAddress;
/* Jump to user application */ JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4); Jump_To_Application = (pFunction) JumpAddress; /* Initialize user application's Stack Pointer */ __set_MSP(*(__IO uint32_t*) ApplicationAddress); Jump_To_Application(); Не получилось. На ф-ции Jump_To_Application(); уходит в hardfault Особенно не понятно, где тело этой функции, нигде в присоединенных файлах эта функция не описана. (файлы примера прикрепил к сообщению) Был бы очень благодарен, если бы кто-то подсказал, где об этом можно прочитать и где можно найти рабочие и понятные примеры. И чтобы не плодить сообщения, напишу про еще одну проблему с отладочной платой: на STM32VLDiscovery целевой контроллер работает, прошивается, но невероятно греется. Даже на 2 секунды нельзя на нем задержать палец. Очень горячий. Закороток визуально нет, да и паяльником я не притрагивался к плате , не знаю, с чего все и началось. Тестера под рукой тоже нет. Не работает светодиод LD4. Правда я не знаю, это следствие или причина нагрева, сейчас далеко от цивилизации, протестить и перепаять ничего не могу. (схема : http://www.st.com/internet/com/TECHNICAL_R...CD00267113.pdf)
Прикрепленные файлы
an2557.rar ( 1.64 мегабайт )
Кол-во скачиваний: 79
|
|
|
|
|
 |
Ответов
(1 - 99)
|
Jan 3 2012, 16:12
|
■ ■ ■ ■
    
Группа: Свой
Сообщений: 1 100
Регистрация: 9-08-06
Пользователь №: 19 443

|
Я бы делал вариант 2, т.к. в первом варианте во вновь загруженной прошивке может неожиданно вылезти глюк при загрузке новой пошивки и это "конец".  Да и памяти больше нужно. Хотя конечно глюк может вылезти везде. Переход на приложение: Код void JumpToApplication(Int32U addr) { typedef void (*pFunction)(void); pFunction Jump_To_Application; Int32U JumpAddress; if(addr>=0x08005000) { JumpAddress = *(Int32U*) (addr + 4); Jump_To_Application = (pFunction) JumpAddress; /* Initialize user application's Stack Pointer */ __MSR_MSP(*(vu32*) addr); Jump_To_Application(); } } Цитата Читал на форуме про то, что нужно перезаписывать таблицу векторов(или вектора прерываний?)? NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x08005000); Цитата Бут и основная программа пишуться ведь как 2 разных проекта? Да.
--------------------
Делай что должен и будь что будет.
|
|
|
|
|
Jan 3 2012, 20:52
|
Местный
  
Группа: Свой
Сообщений: 311
Регистрация: 12-01-11
Из: Калининград (Koenigsberg)
Пользователь №: 62 182

|
Цитата(ierofant @ Jan 3 2012, 16:31)  Пробывал как в примере от ST: Не получилось. На ф-ции Jump_To_Application(); уходит в hardfault Внимательно проверьте правильно ли и по правильным ли адресам записывается приложение. Я с этим долго бился на LPC17 из-за банальной ошибке при записи данных. skripach, Ваш пример ничем не отличается от приведённого ТС. Указания нового положения таблицы векторов нет. Или это фича STM? Я правда пока с ними не работал, но предстоит в ближайшее время. Вы ставите указатель на функцию на адрес ResetISR в приложении и обновляете верхушку стека, но не указываете новое расположение таблицы векторов прерываний. Ведь без этого прерывание в приложении приведёт к попутке перехода на вектор в загрузчике.
--------------------
typedef enum { no, yes, maybe } bool; | блог тут
|
|
|
|
|
Jan 3 2012, 21:30
|
■ ■ ■ ■
    
Группа: Свой
Сообщений: 1 100
Регистрация: 9-08-06
Пользователь №: 19 443

|
Цитата skripach, Ваш пример ничем не отличается от приведённого ТС. Возможно, автор вопрошал "как перейти на выполнение с определенного адреса?" я привел функцию из своего загрузчика под stm32. Цитата Указания нового положения таблицы векторов нет. Если правильно помню это сделано в приложении, перед прыжком были выключены все прерывания.
--------------------
Делай что должен и будь что будет.
|
|
|
|
|
Jan 3 2012, 21:33
|

Участник

Группа: Участник
Сообщений: 32
Регистрация: 3-02-11
Из: Украина, Киев
Пользователь №: 62 695

|
Спасибо, что откликнулись. skripachПытаюсь в простейшей программке протестировать переход выполнения кода по определенному адресу. Пробывал с вашим примером, опять не получилось. Вот мой код(для иара, поэтому немного отличается от вашего): CODE #include "stm32f10x.h" #include "core_cm3.h" #define ApplicationAddress 0x08000081
void JumpToApplication(uint32_t addr) { typedef void (*pFunction)(void); pFunction Jump_To_Application; uint32_t JumpAddress; //if(addr>=0x08005000) //{ JumpAddress = *(uint32_t*) (addr + 4); Jump_To_Application = (pFunction) JumpAddress; /* Initialize user application's Stack Pointer */ __set_MSP(*(uint32_t*) addr); //__MSR_MSP(*(vu32*) addr); Jump_To_Application(); //} }
void init_mk() { RCC->APB2ENR |= (RCC_APB2ENR_IOPAEN|RCC_APB2ENR_IOPCEN); GPIOC->CRH |= (GPIO_CRH_MODE9 | GPIO_CRH_MODE8); //C.8, C.9 OUTPUT GPIOC->CRH &= ~(GPIO_CRH_CNF9 | GPIO_CRH_CNF8); GPIOA->CRL&=~GPIO_CRL_MODE0;//A.0 INPUT }
void test() { GPIOC->BSRR = GPIO_BSRR_BS9; }
int main() { init_mk();
JumpToApplication(ApplicationAddress); GPIOC->BSRR = GPIO_BSRR_BS8;
test();
while(1) { } }
__MSR_MSP - не было, заменил на функцию подобного содержания из своего хидера. Вот такая функция CODE void __set_MSP(uint32_t topOfMainStack) { __ASM("msr msp, r0"); __ASM("bx lr"); } Проверка была для конкретно вашего примера, насколько я понял, поэтому она закоменчена. #define ApplicationAddress 0x08000081 - это начальный адрес функции test(). (беру его из map-а) Т.е. по предположению - код должен выполниться в обход строки : GPIOC->BSRR = GPIO_BSRR_BS8; Верно? Отладчик после выполнения Jump_To_Application(); матерится на то, что указатель стека находится вне его пределах, причем откуда такое значение - черт знает: The stack pointer for stack 'CSTACK' (currently 0x00F44F28) is outside the stack range (0x20000000 to 0x20000400) Cosmojam, я пока пытаюсь осуществить просто переход выполнения программы по определенному адресу, таблицу векторов даже не трогаю.  Выше отписался, что у меня не так. Кстати, я правильно понимаю алгоритм работы бутлоадера? После того, как он закончил все свои необходимые действия, нужно: 1. перенести таблицу векторов по адресу в памяти, где начинается основная программа.(т.е. если у меня основная программа записана, начиная с 2й страницы флеша (1 страница - 1кб), то нужно будет сделать так: NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x08000800)  2. Перейти к выполнению кода на тот же адрес (0x08000800) Верно ли я все понял? P.S. Кстати, skripach, спасибо за приведенный код.
Сообщение отредактировал ierofant - Jan 3 2012, 21:38
|
|
|
|
|
Jan 4 2012, 00:49
|
■ ■ ■ ■
    
Группа: Свой
Сообщений: 1 100
Регистрация: 9-08-06
Пользователь №: 19 443

|
Цитата Т.е. по предположению - код должен выполниться в обход строки : GPIOC->BSRR = GPIO_BSRR_BS8; Верно? Разумеется нет. Функция JumpToApplication это не совсем "перейти на выполнение с определенного адреса", это переход на приложение расположенное по адресу [addr]. Если попытаетесь разобраться с содержимым JumpToApplication то многое станет понятно в том числе почему "матерится на то, что указатель стека...". Также советую посмотреть в дизассамблер. Цитата Кстати, я правильно понимаю алгоритм работы бутлоадера? После того, как он закончил все свои необходимые действия, нужно: 1. перенести таблицу векторов по адресу в памяти, где начинается основная программа.(т.е. если у меня основная программа записана, начиная с 2й страницы флеша (1 страница - 1кб), то нужно будет сделать так: NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x08000800) Близко. Таблицу векторов нужно перенести туда где она находится в приложении. Начало приложения и адрес таблицы векторов могут не совпадать, см настройки линкера. Цитата 2. Перейти к выполнению кода на тот же адрес (0x08000800) Нет. Нерейти на начало приложения, в нашем случае адрес начала приложения расположен по адресу (адрес таблицы векторов+4). см. таблицу векторов.
--------------------
Делай что должен и будь что будет.
|
|
|
|
|
Jan 4 2012, 07:47
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Как у вас все сложно! Какова структура "обычной" программы под STM32? В начале идет блок векторов, он состоит из: -начальное значение стека -адрес обработчика исключения Reset -адреса остальных обработчиков ядра -адреса обработчиков периферии Далее за ним идет код. Ну так и работать надо с этой структурой, а не с какими-то магическими числами и приведением указателей: CODE struct application { struct vectors { typedef void( *handler )( void ); uint32_t MSP_init; handler Reset_vector; handler Core_handler[14]; static const uint_fast8_t MCU_VECTORS = #if defined(STM32F10X_LD_VL) || defined(STM32F10X_MD_VL) 56 #elif defined(STM32F10X_HD_VL) 61 #elif defined(STM32F10X_CL) 68 #elif defined(STM32F10X_LD) || defined(STM32F10X_MD) ||defined(STM32F10X_HD) ||defined(STM32F10X_XL) 60 #endif ; handler MCU_handler[MCU_VECTORS]; } Vectors; uint32_t Size; // application size, 4-byte words };
extern const application Application;
....... if (!CRC->DR) // Application Section OK { // set vectors table to application vectors SCB->VTOR = (uintptr_t)&Application.Vectors; asm volatile ( " MSR MSP, %0\n" // store App stack init value to MSP : : "r" (Application.Vectors.MSP_init) ); Application.Vectors.Reset_vector(); } От линкера требуется лишь предоставить символ Application с адресом начала той области, в которую загружается приложение. Не знаю как в последнем ИАРе, в старом это можно было сделать, вписав в линкерный скрипт или командную строку линкера -DApplication=0x08001000 У меня при сборке приложения линкер сразу за таблицей векторов вписывает размер образа, чтобы загрузчик мог просчитать контрольную сумму загруженного приложения (и только его, не учитывая свободную память).
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jan 8 2012, 00:38
|

Участник

Группа: Участник
Сообщений: 32
Регистрация: 3-02-11
Из: Украина, Киев
Пользователь №: 62 695

|
Спасибо всем за помощь. В целом получилось и перейти на выполнение приложения и перенести таблицу векторов. Вот только если я пытаюсь перенести таблицу в программе бутлоадера - не выходит (не работают прерывания), если же в основной программе - все нормально. Переношу так(т.к. не использую библиотеку для периферии, написал сам) : CODE #define vector_table_offset 0xC00 //смещение #define offset (uint32_t)(vector_table_offset & 0x1FFFFF80) //выравнивание
SCB->VTOR = offset; На приложение перехожу с помощью функции JumpToApplication, которую подсказал skripach. Сергей Борщ, пытался разобрать вашу программу. Не понятна эта строчка: CODE SCB->VTOR = (uintptr_t)&Application.Vectors; Возвращается адрес Application.Vectors, после чего явно преобразуется в тип указателя. Тут понятно. А что именно содержит Application.Vectors? И как оно туда попало? Связано это, видимо, с QUOTE От линкера требуется лишь предоставить символ Application с адресом начала той области, в которую загружается приложение. И на сколько я понимаю, линкер предоставляет структуре адрес начала приложения, потом уже в структуре в соотвествии этому начальному адресу всем векторам присваиваются их адреса? Написана ваша программа, однозначно красивее и четче, единственное, хочется её полностью понять. А может еще подскажете, где в iar 6.21 линкерный скрипт?
|
|
|
|
|
Sep 19 2012, 10:09
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Цитата(Almaz1988 @ Sep 19 2012, 12:41)  Столкнулся с казалось бы элементарной проблемой - не могу прошить даже самую простую программу (моргание светодиодом) по адресу, отличному от нуля. Прошиваться она может и прошивается, но работать не будет. Цитата(Almaz1988 @ Sep 19 2012, 12:41)  Пишу в Keil. Адрес выставляю следующим образом: правый клик по проекту-->Options for target-->Linker-->R/O base = 0x00000100. Попробовал прошить из LPCXpresso, - получил ошибку "vectors still have erased values". Спасибо за ответы Этого недостаточно. Нужна вторая программа (собственно бутлоадер), которая должна быть расположена по адресу 0, и которая запустит основное приложение. Зашить её нужно до зашивки-отладки основной программы. Ещё момент: смещение 0x100 может и допустимо, но нежелательно - flash стирается по 4кБ за раз и получается нельзя обновить основную программу, не стирая бут. Стереть его можно, если он будет работать из ОЗУ, но это чревато. Ставьте смещение кратное 0x1000. Для простого бута 4к достаточно, если нужно больше - увеличивайте. P.S. ещё про boot на lpc11xx
|
|
|
|
|
Sep 19 2012, 10:55
|
Частый гость
 
Группа: Участник
Сообщений: 100
Регистрация: 19-09-12
Пользователь №: 73 602

|
Спасибо Артем, ваш ответ очень помог)
По адресу 0х0000 2000 залил "Рабочая программа", которая моргает 4 раза.
По адресу 0х0000 0000 тоже залил "Бутлоадер", которая моргает 4 раза, а потом прыгает по адресу 0х2169 (функция main() "Рабочей программы" по MAP-файлу).
Все сработало как и ожидалось - МК моргнул 8 раз. Т.е. МК стартовал с "Бутлоадера" затем передал управление "Рабочей программе".
Но вот на следующем этапе на место "Рабочей программы" я залил программу по сложнее ( которую собственно говоря и пишу). В итоге МК моргнул 4 раза, а "Рабочая программа" не запустилась. Подозрения падают на то, что она работает по прерываниям, о чем, собственно, ваша ссылка.
Не могли вы "на пальцах" объяснить, почему эти прерывания требуют особого отношения и для чего нужен ремап векторов?
|
|
|
|
|
Sep 19 2012, 11:56
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Цитата(Almaz1988 @ Sep 19 2012, 13:55)  Не могли вы "на пальцах" объяснить, почему эти прерывания требуют особого отношения и для чего нужен ремап векторов? Ремап нужен чтобы и бут и рабочая программа могли использовать прерывания. К тому же вектора расположены по адресам от 4 и получается что приложение хранит адреса своих обработчиков в области бута, что как-то странно (если без ремапа). Если буту прерывания не нужно, то можете посмотреть пример NXP (secondary bootloader). там пример где все обработчики делают jump (или call - не помню точно) в область рабочей программы. Цитата(Almaz1988 @ Sep 19 2012, 13:55)  По адресу 0х0000 0000 тоже залил "Бутлоадер", которая моргает 4 раза, а потом прыгает по адресу 0х2169 (функция main() "Рабочей программы" по MAP-файлу). P.S. Бут должен "прыгать" не на main, а на Reset_Handler.
|
|
|
|
|
Sep 20 2012, 04:24
|
Частый гость
 
Группа: Участник
Сообщений: 100
Регистрация: 19-09-12
Пользователь №: 73 602

|
Цитата(Сергей Борщ @ Sep 20 2012, 00:15)  Если мы говорим о Cortex-M3 (M4), то нужно записать в регистр VTOR значение 0x00002000, ведь именно по этому адресу начинается таблица векторов вашего приложения (ячека с начальным значением стека также входит в эту таблицу). Cortex-M0
|
|
|
|
|
Sep 20 2012, 06:20
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
QUOTE (Almaz1988 @ Sep 20 2012, 07:24)  Cortex-M0 Тогда нужно: 1) Располагать (линковать) загрузчик с адреса 0x08000000, чтобы он мог продолжать работать после ремапа. 2) Располагать (линковать) приложение с адреса 0x0800хххх, чтобы онo могло работать после ремапа. 2) Копировать вектора приложения в начало ОЗУ. У приложения должно быть "откушено" начало ОЗУ под это дело в скрипте линкера. 3) Из векторов приложения брать адрес начала стека и прописывать его в MSP 4) Делать ремап (SYSCFG, биты MEM_MODE) 5) передавать управление на адрес, взятый из вектора reset_handler
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Sep 20 2012, 07:18
|
Частый гость
 
Группа: Участник
Сообщений: 100
Регистрация: 19-09-12
Пользователь №: 73 602

|
Цитата(_Артём_ @ Sep 19 2012, 15:56)  Если буту прерывания не нужно, то можете посмотреть пример NXP (secondary bootloader) В этом примере предлагают использовать ассемблерные вставки, добавляю их в main() Бутлоадера: __asm volatile("ldr r0, =0x103C"); __asm volatile("ldr r0, [r0]"); __asm volatile("mov pc, r0"); Компиллирую проект, выдает ошибку: aplication\main.c(38): error: #1113: Inline assembler not permitted when generating Thumb code Добавляю в настройках проекта --arm. Появляется ошибка: main.c: Error: C3006E: specified processor or architecture does not support ARM instructions Цитата(Сергей Борщ @ Sep 20 2012, 10:20)  Тогда нужно: 1) Располагать (линковать) загрузчик с адреса 0x08000000, чтобы он мог продолжать работать после ремапа. 2) Располагать (линковать) приложение с адреса 0x0800хххх, чтобы онo могло работать после ремапа. 2) Копировать вектора приложения в начало ОЗУ. У приложения должно быть "откушено" начало ОЗУ под это дело в скрипте линкера. 3) Из векторов приложения брать адрес начала стека и прописывать его в MSP 4) Делать ремап (SYSCFG, биты MEM_MODE) 5) передавать управление на адрес, взятый из вектора reset_handler 1,2) Flash-память микроконтроллера lpc11c24 - 0x0000 0000 - 0x0000 8000 RAM-память - 0х1000 0000 - 0х1000 2000 Адреса 0x08000000 у меня нет. Если загрузчик распологаю не по адресу 0х0000 0000, то у меня МК не стартует. 2) копировать с помощью ассемблерных вставок из NXP примера "secondary bootloader"? Постом ниже я написал о затруднениях, с которыми столкнулся при их использовании. 3,4) также выподняется ассемблерными вставками?
|
|
|
|
|
Sep 20 2012, 09:23
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
QUOTE (Almaz1988 @ Sep 20 2012, 10:18)  Компиллирую проект, выдает ошибку: С кейлом не работаю, не подскажу. Пусть другие участники помогутю QUOTE (Almaz1988 @ Sep 20 2012, 10:18)  1,2) Flash-память микроконтроллера lpc11c24 А название ветки - STM32 bootloader. Невнимательно читал ваше первое сообщение и даю советы по STM32F0xx. Для LPC11 ремапятся первые 512 байт. Соответственно смотрите по ссылке от _Артем_а - там я приводил кусок запуска приложения для LPC11 QUOTE (Almaz1988 @ Sep 20 2012, 10:18)  2) копировать с помощью ассемблерных вставок из NXP примера "secondary bootloader"? Постом ниже я написал о затруднениях, с которыми столкнулся при их использовании.
3,4) также выподняется ассемблерными вставками? 2 - можно и ассемблером, но зачем? Можно сделать и на С/С++ при помощи цикла и указателей либо библиотечной функцией memcpy() 3) да, ассемблерная вставка либо функция из CMSIS, которая тоже на ассемблерной вставке строится. 4) Обычная сишная запись в регистр. Для LPC это будет регистр SYSMEMREMAP
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Sep 20 2012, 11:21
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
QUOTE (Almaz1988 @ Sep 20 2012, 14:01)  2) а если размещать вектор прерываний не в RAM, а во flash по адресу 0х0000 2000 Разместить вектора вы можете где угодно, но вот процессор читает их с адреса 0x00000000. И никаких способов считывать их из других адресов у Cortex-M0 не предусмотрено (у M3 для этого есть регистр VTOR). Поэтому разработчики процессоров идут на хитрость - делают отражение (remap) на эти адреса других регионов памяти, например RAM. После ремапа вы кладете какие-то данные в начало RAM, а процессор их "видит" не только по "родным" адресам в RAM, но и в начале флеша. И таких мест, которые могут быть отражены на начало адресного пространства всего два - начало ОЗУ и начало ПЗУ со встроенным загрузчиком (ISP). Эти места прибиты гвоздями к своим адресам разработчиками процессора. QUOTE (Almaz1988 @ Sep 20 2012, 14:01)  3) Не помните названия функции? Нет, я не использую CMSIS (только заголовочный файл с описанием адресов регистров). Поищите поиском по файлам, ключевое слово "MSP" вы уже знаете. Или обратитесь в техподдержку Кейла -они должны быстро отвечать на вопросы покупателей своего продукта.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Sep 21 2012, 07:11
|
Участник

Группа: Участник
Сообщений: 21
Регистрация: 14-05-09
Из: Тула
Пользователь №: 49 063

|
Если Вы в загрузчике не используете прерывания - сделайте как в примере от NXP (secondary bootloader) В прерываниях загрузчика сделайте редирект на прерывания приложения. Как-то так можно: (для загрузчика размером 4кб(0x1000) )
#define BOOTLOADER_SIZE 0x1000
#define redirect(address) unsigned long pc = *(unsigned long*)(address) + (BOOTLOADER_SIZE); void (*redirect_handler)(void) = (void(*)(void))pc;\ redirect_handler();
.... void CT16B0_IRQHandler(void) { redirect(0x1080); } void CT16B1_IRQHandler(void) { redirect(0x1084); } void CT32B0_IRQHandler(void) { redirect(0x1088); } .....
Сообщение отредактировал Alex19 - Sep 21 2012, 07:42
|
|
|
|
|
Sep 21 2012, 07:51
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
QUOTE (Almaz1988 @ Sep 21 2012, 09:37)  После того как мы их скомпилировали, открыть HEX-файлы обоих проектов. В ручную заменить строки в НЕХ-файле "Загрузчика", соответствующие таблице векторов прерываний на аналогичные строки из НЕХ-файла "Рабочей программы" не трогая только первые две ячейки: main stack pointer и reset vector. Давайте думать дальше. Вы доработали рабочую программу и ее обработчики прерываний оказались по другим адресам. Вам снова надо брать обе прошивики, снова копировать строки HEX-файлов, программировать уже обновленный загрузчик. Тогда какой в нем смысл, если его надо править и заливать программатором перед каждым обновлением приложения? В чем проблема? Вам жалко 256 байт ОЗУ (а реально меньше, ибо не вся таблица используется)?
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Sep 21 2012, 10:00
|
Частый гость
 
Группа: Участник
Сообщений: 100
Регистрация: 19-09-12
Пользователь №: 73 602

|
Цитата(Сергей Борщ @ Sep 21 2012, 11:51)  Давайте думать дальше. Вы доработали рабочую программу и ее обработчики прерываний оказались по другим адресам. Вам снова надо брать обе прошивики, снова копировать строки HEX-файлов, программировать уже обновленный загрузчик. Тогда какой в нем смысл, если его надо править и заливать программатором перед каждым обновлением приложения? В чем проблема? Вам жалко 256 байт ОЗУ (а реально меньше, ибо не вся таблица используется)? Пытаюсь сделать с помощью Ремапа, не могу понять в чем дело. Решил идти от простого к сложному. 1)Самое простое в ручную из одного HEX-файла скопировать данные по адресам 0х09-0хС0 (по этим адресам располагается таблица векторов. Не трогаю лишь два первых вектора 0х00 - 0х08). Получилось "Рабочая программа" запустилась. 2) Перешел к более сложному - копирую файлы 0х00 - 0х08 (MSP и Reset handler) программно с помощью чего осуществляю прыжок в "Рабочую программу" и она запускается, если не использует прерываний: #include "LPC11xx.h" #include "core_cm0.h" #include "system_LPC11xx.h" __ASM void __jump_( ) { ldr r0, =0x1000 ldr r0, [r0] mov sp, r0 ldr r0, =0x1004 ldr r0, [r0] mov pc, r0 } nt main(void) { SystemInit(); LPC_SYSCON->SYSAHBCLKCTRL |= (1<<6); __jump_( ); while(1); } 3)Перехожу к более сложному - программно копирую всю таблицу прерываний - 0х00 - 0хС0. Текст программы: #include "LPC11xx.h" #include "core_cm0.h" #include "system_LPC11xx.h" __ASM void __jump_( ) { ldr r0, =0x1000 ldr r0, [r0] mov sp, r0 ldr r0, =0x1004 ldr r0, [r0] mov pc, r0 ldr r0, =0x1008 ldr r0, [r0] ldr r1, =0x0008 mov [r1], r0 ldr r0, =0x1008 ldr r0, [r0] ldr r1, =0x0008 mov [r1], r0 ...................... //здесь такие же наборы команд для других адресов ...................... ldr r0, =0x10BC ldr r0, [r0] ldr r1, =0x00BC mov [r1], r0 } int main(void) { SystemInit(); LPC_SYSCON->SYSAHBCLKCTRL |= (1<<6); __jump_( ); while(1); } И тут сталкиваюсь с затруднением - компилятор на строке "mov [r1], r0" выдает ошибку: error: A1647E: Bad register name symbol, expected Integer register В чем дело? Эта команда же допускает копирование из РОН в память
|
|
|
|
|
Sep 21 2012, 10:12
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
QUOTE (Almaz1988 @ Sep 21 2012, 13:00)  Эта команда же допускает копирование из РОН в память Если вы читаете из памяти командной ldr, то писать в нее логично командой str. Но, мама дорогая! Почему на ассемблере, да еще и тупым copy-paste? Это, кажется, называется "индусский код"? CODE uint32_t const * pSrc = (uint32_t const *)0x1000; uint32_t * pDst = (uint32_t const *)0x0000; #define VECTORS_COUNT 64 // подставьте сколько нужно, включая указатель стека и reset handler for(uint_fast8_t i = 0; i < VECTORS_COUNT; ++i) *pDst++ = *pSrc++;
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Sep 21 2012, 12:30
|
Частый гость
 
Группа: Участник
Сообщений: 100
Регистрация: 19-09-12
Пользователь №: 73 602

|
Цитата(Сергей Борщ @ Sep 21 2012, 14:12)  Если вы читаете из памяти командной ldr, то писать в нее логично командой str. Но, мама дорогая! Почему на ассемблере, да еще и тупым copy-paste? Это, кажется, называется "индусский код"? Код uint32_t const * pSrc = (uint32_t const *)0x1000; uint32_t * pDst = (uint32_t const *)0x0000; #define VECTORS_COUNT 64 // подставьте сколько нужно, включая указатель стека и reset handler for(uint_fast8_t i = 0; i < VECTORS_COUNT; ++i) *pDst++ = *pSrc++; Индусский ли пиндосский ли...)) Ваш кусок кода не перепрыгивает в "Рабочую программу". Видимо на STM32 есть возможность напрямую писать во флеш (в lpc11xx это возможно только посредством специальных IAP команд) Написанная мною тоже не пашет: ldr r0, =0x1004 ; загружаем в r0 константу 0х1004 ldr r0, [r0] ; загружаем в r0 содержимое по адресу 0х1004 ldr r1, =0x0004 ; загружаем в r0 константу 0х0004 str r0, [r1] ; загружаем значение r0 в адрес r1
|
|
|
|
|
Sep 21 2012, 12:45
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
QUOTE (Almaz1988 @ Sep 21 2012, 15:30)  Видимо на STM32 есть возможность напрямую писать во флеш Вы и себя запутали и я просто перенес на С ваш код с некоторой оптимизацией. Не нужно писать во флеш. И не нуждно копировать в адрес 0. Надо копировать в начало ОЗУ. И потом ремапом подставлять эту область ОЗУ на нулевые адреса: CODE uint32_t const * pSrc = (uint32_t const *)0x00001000; // начало приложения uint32_t * pDst = (uint32_t *)0x10000000; // начало ОЗУ #define VECTORS_COUNT 64 // подставьте сколько нужно, включая указатель стека и reset handler for(uint_fast8_t i = 0; i < VECTORS_COUNT; ++i) *pDst++ = *pSrc++; // далее надо загрузить указатель стека. не_знаю_как_это_сделать_в_кейле(*(uint32_t const *)0x00001000); // LPC_SYSCON->SYSMEMREMAP = 1; // remap to ram void (*Application)(); Application = *(void (**)())0x10000004; Application();
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Sep 21 2012, 13:14
|
Частый гость
 
Группа: Участник
Сообщений: 100
Регистрация: 19-09-12
Пользователь №: 73 602

|
Цитата(Сергей Борщ @ Sep 21 2012, 16:45)  Вы и себя запутали и я просто перенес на С ваш код с некоторой оптимизацией. Не нужно писать во флеш. И не нуждно копировать в адрес 0. Надо копировать в начало ОЗУ. И потом ремапом подставлять эту область ОЗУ на нулевые адреса: Код uint32_t const * pSrc = (uint32_t const *)0x00001000; // начало приложения uint32_t * pDst = (uint32_t *)0x10000000; // начало ОЗУ #define VECTORS_COUNT 64 // подставьте сколько нужно, включая указатель стека и reset handler for(uint_fast8_t i = 0; i < VECTORS_COUNT; ++i) *pDst++ = *pSrc++; // далее надо загрузить указатель стека. не_знаю_как_это_сделать_в_кейле(*(uint32_t const *)0x00001000); // LPC_SYSCON->SYSMEMREMAP = 1; // remap to ram void (*Application)(); Application = *(void (**)())0x10000004; Application(); Спасибо за пояснения)) Но с ремапом у меня нивкакую запускаться не хочет)) Продолжу с понедельника. П.с. и все же есть возможность записи во флеш ассемблерными вставками?(Как запасной вариант, если ремап не запустится)
|
|
|
|
|
Sep 21 2012, 16:10
|

Гуру
     
Группа: Свой
Сообщений: 2 957
Регистрация: 19-09-06
Из: Москва
Пользователь №: 20 514

|
Цитата(Almaz1988 @ Sep 21 2012, 18:57)  А IAP-команды переводятся же в ассемблерный код при компилляции? Это ведь не отдельный язык программирования. нет, конечно в разные регистры заносятся команда, номер начального сектора, конечного сектора, адрес блока данных вызывается IAP, адрес которого заранее известен из еще одного регистра читается результат операции какая разница, каким языком это дело описать? а вот требования к выполнению IAP (как то - отключить PLL, например, или вызов IAP не из области FLASH) нужно выполнять обязательно
|
|
|
|
|
Sep 22 2012, 12:13
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Цитата(toweroff @ Sep 21 2012, 19:10)  а вот требования к выполнению IAP (как то - отключить PLL, например В UM10398 Chapter 26: LPC111x/LPC11Cxx Flash programming firmware PLL не упоминается (или я не нашёл). Ссылку приведёте? Цитата(toweroff @ Sep 21 2012, 19:10)  вызов IAP не из области FLASH В примере от NXP функции IAP вызываются из flash. Наверняка потому, что из bootloader не использует прерываний. В случае испоользования прерываний таблицу векторов и обработчики нужно поместить в RAM или запрещать прерывания на время стирания/записи flash.
|
|
|
|
|
Mar 2 2013, 07:01
|
Группа: Новичок
Сообщений: 3
Регистрация: 10-01-11
Пользователь №: 62 117

|
Здравствуйте! Просьба откликнуться кто в теме. Пишу загрузчик для своего приложения (IAR 6.40, STM32F103VET). Как можно в процессе линковки вставить размер образа по конкретному адресу? Это точно можно сделать, тому пример пост Сергея Борща :
'У меня при сборке приложения линкер сразу за таблицей векторов вписывает размер образа, чтобы загрузчик мог просчитать контрольную сумму загруженного приложения (и только его, не учитывая свободную память).'
|
|
|
|
|
Mar 3 2013, 06:27
|
Группа: Новичок
Сообщений: 3
Регистрация: 10-01-11
Пользователь №: 62 117

|
Спасибо за ответы. Пробовал следовать советам официальной техподдержки IAR http://supp.iar.com/Support/?note=62709&from=note+65473а именно пункта Alternative solution using checksum-start and checksum-end markers Всё отлично, есть константы с адресами начала и конца проекта, считается CRC32, только в Нех файл значение CRC не заносится категорически, вместо него там нули. А в дизассемблере нормальное значение. Пока не победил это.
|
|
|
|
|
Dec 11 2014, 08:55
|
Участник

Группа: Участник
Сообщений: 20
Регистрация: 28-10-11
Пользователь №: 68 026

|
Хочу сделать загрузку прошивки через веб, но неполучается, не запускается основная программа. Делаю на основе проекта с STM32CubeF4, LwIP_IAP. Манеул к этому проекту http://www.st.com/st-web-ui/static/active/.../DM00103145.pdfСреда Keil uVision, мк STM32f4 ------------------------------------------ В бутлоадере выставил начальный адрес с которого будет стартовать основная программа #define USER_FLASH_FIRST_PAGE_ADDRESS 0x08020000 В основной программе изменил начальный адресс 0x08020000 В основной программе изменил смещение для таблици векторов #define VECT_TAB_OFFSET 0x20000 ------------------------------------------ После загрузки прошивок, доходит до Jump_To_Application(); и все, дальше тишина. Переменная JumpAddress = 0x080201A5 if (((*(__IO uint32_t*)USER_FLASH_FIRST_PAGE_ADDRESS) & 0x2FFE0000 ) == 0x20000000) { JumpAddress = *(__IO uint32_t*) (USER_FLASH_FIRST_PAGE_ADDRESS + 4); Jump_To_Application = (pFunction) JumpAddress; __set_MSP(*(__IO uint32_t*) USER_FLASH_FIRST_PAGE_ADDRESS); Jump_To_Application(); } Основная программа начинается с адреса 0x08020000 смотрел через STM32 ST-LINK Utility На скринах без смещения с со смещением, данные совпадаю, отличие только адресами. В чем может быть причина? Спасибо.
Эскизы прикрепленных изображений
|
|
|
|
|
Dec 12 2014, 11:12
|
Участник

Группа: Участник
Сообщений: 20
Регистрация: 28-10-11
Пользователь №: 68 026

|
Цитата(Kabdim @ Dec 12 2014, 14:46)  Выглядит более менее, за исключением того что неясно как вы меняете таблицу прерываний с бутлоадера на вашу программу. А где менять менять? Я с таблицей ничего не делал. Я думал что таблица прерываний по умолчанию в основной программе будет после адреса 0x08020000 Цитата(Kabdim @ Dec 12 2014, 14:46)  Вы блинк пробовали прошить? Я свою прошивку заливаю.
|
|
|
|
|
Dec 12 2014, 13:19
|
Знающий
   
Группа: Свой
Сообщений: 558
Регистрация: 26-11-14
Из: Зеленоград
Пользователь №: 83 842

|
Цитата(vovanxp @ Dec 12 2014, 14:12)  А где менять менять? Я с таблицей ничего не делал. Я думал что таблица прерываний по умолчанию в основной программе будет после адреса 0x08020000
Я свою прошивку заливаю. SCB->VTOR - на этом форуме очень много тем посвященных бутлоадерам. Залейте простейшую мигалку что бы понять правильно ли вы слинковались и заливаете. Заодно если она заработает, значит дело действительно в таблице прерываний. Еще стоит отключать прерывания во время смены таблицы и перехода т.к. они могут испортить процесс. Вдогонку, если у вас thumb, то младший бит адреса по которому переходите должен быть 1.
|
|
|
|
|
Dec 12 2014, 14:56
|
Участник

Группа: Участник
Сообщений: 20
Регистрация: 28-10-11
Пользователь №: 68 026

|
Цитата(Kabdim @ Dec 12 2014, 16:19)  SCB->VTOR - на этом форуме очень много тем посвященных бутлоадерам. Залейте простейшую мигалку что бы понять правильно ли вы слинковались и заливаете. Заодно если она заработает, значит дело действительно в таблице прерываний. Еще стоит отключать прерывания во время смены таблицы и перехода т.к. они могут испортить процесс.
Вдогонку, если у вас thumb, то младший бит адреса по которому переходите должен быть 1. В моей программе(не в загрузчике) я поменял дефайн //#define VECT_TAB_OFFSET 0x00 #define VECT_TAB_OFFSET 0x20000 В SystemInit() есть такой кусок кода #ifdef VECT_TAB_SRAM SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */ #else SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */ #endif
Сообщение отредактировал vovanxp - Dec 12 2014, 14:56
|
|
|
|
|
Dec 12 2014, 17:25
|
Знающий
   
Группа: Свой
Сообщений: 558
Регистрация: 26-11-14
Из: Зеленоград
Пользователь №: 83 842

|
Цитата(vovanxp @ Dec 12 2014, 17:56)  В моей программе(не в загрузчике) я поменял дефайн Видимо нужно отладится в асемблере. К примеру мой код для кортекса м0 Код void JumpToMainProgram() { __disable_irq(); memcpy(MEMORY_OFFSET, MAIN_PROG_FLASH_OFFSET, INT_MEM_TO_MAP_SIZE); __set_MSP(*MAIN_PROG_FLASH_OFFSET); LPC_SYSCON->SYSMEMREMAP = 1; __enable_irq(); ISRPtr application_reset_handler = *((ISRPtr*) (MAIN_PROG_FLASH_OFFSET + 1)); goto *application_reset_handler;
} MAIN_PROG_FLASH_OFFSET - указатель на u32
Сообщение отредактировал Kabdim - Dec 12 2014, 17:23
|
|
|
|
|
Dec 17 2014, 07:59
|
Участник

Группа: Участник
Сообщений: 20
Регистрация: 28-10-11
Пользователь №: 68 026

|
Только что удалось прошить через веб, причина было совсем капец, инициализацию IWDG закоментировал, а в обработчике прерывания таймера оставил HAL_IWDG_Refresh(&Hiwdg); При обычном режиме все работало, но если основная программа начиналась с 0x8020000 программа не работала.
Одним словом причина была совсем в ином.
|
|
|
|
|
Feb 20 2015, 07:53
|

Универсальный солдатик
     
Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362

|
Цитата(AHTOXA @ Feb 20 2015, 08:46)  А как туда будет передаваться управление? Это будет автономный проект, загрузчик. Без прерываний, работа только по последовательному порту. После сброса попадаем в него. Если есть признак, что нужно обновить основную программу (или просто ждать сообщения секунды 4), принимаем, программируем, сбрасываемся. Если признака нет уходим на основную программу. Стартовый адрес (тот, что в прошивке по 0x00000000), сохраняем где-то в недрах..., и используем его для перехода. А вместо него при прошивке пишем адрес загрузчика. И не надо перемещать основную программу, удобно для отладки. Вижу вероятность получить неработающий прибор, когда сектор 0 уже стер, а записать не успел.
|
|
|
|
|
Feb 20 2015, 10:36
|

Универсальный солдатик
     
Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362

|
Цитата(AHTOXA @ Feb 20 2015, 12:17)  Я вот про это и спрашивал. Как мы попадаем в него? Насколько я знаю, после сброса можно попасть в три места: в начало флеша, в ОЗУ и в заводской загрузчик. Как обычно. В начале flash записан адрес (загрузчика, в данном случае), по которому переходим... ага, эта часть не отображается на нулевые адреса... и из нее, очевидно, нельзя выполнять программы. Но ведь в System Memory скакнуть, вроде, можно? Ладно, идею похерим. Займу нулевой сектор под загрузчик. Только разобраться надо, как основную программу создавать. Задать ее расположение с адреса 0x0800_4000, что ли? Я вот чего не пойму. Программный счетчик когда по программе бегает, он какие значения перебирает? Вижу в отладчике 0x0800XXXX. Тогда при чем тут нулевые адреса вообще? Или в этом случае (работа из flash) адреса 0x00000000 и 0x08000000 - это одно и то же? И можно задать и так, и так? Скажем, вручную в отладчике? То есть, старшие биты адреса при старте принудительно выставляются в 0x0800 и погнали...
|
|
|
|
|
Feb 20 2015, 12:20
|

Знающий
   
Группа: Участник
Сообщений: 974
Регистрация: 4-04-08
Из: далека
Пользователь №: 36 467

|
Цитата(ViKo @ Feb 20 2015, 05:36)  Я вот чего не пойму. Программный счетчик когда по программе бегает, он какие значения перебирает? Вижу в отладчике 0x0800XXXX. Тогда при чем тут нулевые адреса вообще? Или в этом случае (работа из flash) адреса 0x00000000 и 0x08000000 - это одно и то же? И можно задать и так, и так? Скажем, вручную в отладчике? То есть, старшие биты адреса при старте принудительно выставляются в 0x0800 и погнали... Правильные пацаны просто читают мануаль в таком случае. Угу, одно и тоже. А вот "задать вручную в отладке" это уже методы юных кулхацкеров. Неизвестно как оно имплементировано, и гарантии нет что везде одинаково и так и будет. И не факт что поможет. http://www.keil.com/forum/20219/stm32-and-interrupts/
--------------------
Верить нельзя никому, даже себе. Мне - можно.
|
|
|
|
|
Feb 20 2015, 13:01
|

Универсальный солдатик
     
Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362

|
Цитата(A. Fig Lee @ Feb 20 2015, 15:20)  Правильные пацаны просто читают мануаль в таком случае. Угу, одно и тоже. А вот "задать вручную в отладке" это уже методы юных кулхацкеров. Неизвестно как оно имплементировано, и гарантии нет что везде одинаково и так и будет. И не факт что поможет. Чему поможет? Мне ничего не надо. А мануалов я начитался тонны. Просто когда не сталкиваешься, не задумываешься. А кое-кто и столкнувшись, не думает. Просто посылает в мануал.  Зачем тогда компилировать в адреса 0x08000000? Кидай сразу в 0, и что из RAM, что из Flash будет работать. Зачем мне Keil демонстрирует адреса 0x08000000? Глянул в отладчике Memory View. Что по 0x00000000, что по 0x08000000 - одинаковое показывает. Первое слово - стек, второе - стартовый адрес - в обоих вариантах 0x0800EAC5. Куда-то в конец скачет, видимо, загрузка переменных.
|
|
|
|
|
Feb 20 2015, 14:36
|

Знающий
   
Группа: Участник
Сообщений: 974
Регистрация: 4-04-08
Из: далека
Пользователь №: 36 467

|
Цитата(ViKo @ Feb 20 2015, 09:33)  Он уже включен железно так, как задано. Может, вам мануал почитать?  Да, не надо нашим людям помогать. Тебя же потом и по кумполу. Зря влез. До свидания.
--------------------
Верить нельзя никому, даже себе. Мне - можно.
|
|
|
|
|
Feb 23 2015, 19:34
|
Профессионал
    
Группа: Свой
Сообщений: 1 719
Регистрация: 13-09-05
Из: Novosibirsk
Пользователь №: 8 528

|
Цитата(ViKo @ Feb 22 2015, 19:35)  Есть у кого-нибудь подтверженное практикой мнение, или и так всем ясно (что нельзя)? Cortex™-M3 Technical Reference Manual от ARM утверждает что исполнение кода запрещено только из старших 1/2 Гигабайта адресного пространства (от 0xE0000000 и до упора). Так что при желании можно поисполнять даже содержимое регистров периферийных устройств.Т.е. архитектура ARM не запрещает, но конкретно у STM32F1xx не получится, выборка кода запрещена для адресов 0x40000000- 0x5FFFFFFF и от 0xA0000000 до конца памяти. Под внешнюю память отведен диапазон адресов 0x60000000 - 0x9FFFFFFF, если контроллер внешней памяти настроить соответствующим образом, то можно и из внешней памяти код исполнять.
--------------------
Russia est omnis divisa in partes octo.
|
|
|
|
|
Feb 24 2015, 10:58
|

Универсальный солдатик
     
Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362

|
Судя по рисунку, STM32F20X может выполнять программу из внешней памяти. А также и из внутренней OTP Flash, наверное. А еще под картинкой написано: S2: S-bus This bus connects the system bus of the Cortex®-M3 core to a BusMatrix. This bus is used to access data located in a peripheral or in SRAM. Instructions may also be fetch on this bus (less efficient than ICode). The targets of this bus are the 112 KB & 16 KB internal SRAMs, the AHB1 peripherals including the APB peripherals, the AHB2 peripherals and the external memories through the FSMC. То есть, можно команды выбирать откуда хочешь!?
Эскизы прикрепленных изображений
|
|
|
|
|
Feb 25 2015, 13:41
|
Профессионал
    
Группа: Свой
Сообщений: 1 719
Регистрация: 13-09-05
Из: Novosibirsk
Пользователь №: 8 528

|
Цитата(ViKo @ Feb 25 2015, 20:01)  Но основной проект не работает. Предполагаю, в загрузчике дело. Что-то не так. NVIC_SetVectorTable() в загрузчике не особо нужен. Это надо в самой запущенной программе сделать, потому что по умолчанию в SystemInit() регистр SCB->VTOR устанавливается на начало флеша.
--------------------
Russia est omnis divisa in partes octo.
|
|
|
|
|
Feb 25 2015, 17:29
|
Профессионал
    
Группа: Свой
Сообщений: 1 719
Регистрация: 13-09-05
Из: Novosibirsk
Пользователь №: 8 528

|
Цитата(ViKo @ Feb 25 2015, 21:00)  Дохожу в загрузчике до JumpAppl() и улетаю незнамо куда, в 0xBFFFXXXX. Надо смотреть куда линкер таблицу векторов засунул, похоже она осталась на прежнем месте. Возможно что сегмент кода и сегмент под таблицу векторов настраиваются отдельно.
--------------------
Russia est omnis divisa in partes octo.
|
|
|
|
|
Feb 25 2015, 18:29
|

Универсальный солдатик
     
Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362

|
Цитата(SSerge @ Feb 25 2015, 20:29)  Надо смотреть куда линкер таблицу векторов засунул, похоже она осталась на прежнем месте. Я ее (их обеих) вижу в окнах Memory в отладчике. Все, как надо: адрес стека, адрес по сбросу... Цитата(Сергей Борщ @ Feb 25 2015, 20:58)  Вот прямо в дизассемблере так и написано JumpAppl()? У меня там обычно что-то вроде BL R0 и чуть выше можно увидеть, откуда в R0 вдруг оказался неправильный адрес перехода. У меня тоже, естественно. BL R1. И адрес в нем правильный, из таблицы, что находится по адресу 0x08004004. А дальше - то, что описал. Здесь вот какая штука - в основной программе использую Keil RTX RTOS. Может, она и меняет что-то, например, VTOR. Надо посмотреть в несдвинутом проекте.
|
|
|
|
|
Mar 2 2015, 19:43
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(ViKo @ Feb 24 2015, 16:58)  Судя по рисунку, STM32F20X может выполнять программу из внешней памяти. А также и из внутренней OTP Flash, наверное. ... То есть, можно команды выбирать откуда хочешь!?  Должен выполнять, но возможно нужно настроить MPU предварительно. По-крайней мере для LPC1788 в UM указано: Default memory space permissions for the Cortex-M3 do not allow program execution from the address range that includes the dynamic memory chip selects. These permissions can be changed by programming the MPUВ реальности если запустить код в SDRAM, то при шагании JTAG видно, что несколько команд (с десяток) код выполняется нормально, но на некоторой команде вдруг улетает в Memory Management Fault (MMFSR.IACCVIOL = 1). Я только что прописал конфигурирование MPU и после этого всё заработало - теперь программа нормально выполняется из SDRAM (LPC1788).
|
|
|
|
|
Mar 9 2015, 10:10
|

Универсальный солдатик
     
Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362

|
Хочу спросить... Что-то не могу после прошивки новой программы перескочить в основную. Если после сброса, и таймер отсчитал 2 с - переходит нормально. За эти 2 с я должен нажать кнопку на панели, тогда дается таймер в 16 с для приема кода прошивки. Так вот, если кода не пришло, тоже не могу перейти в основную программу. Как будто, что-то переинициализировать нужно.
Добавлю информации. После записи во флэш проверяю, что есть новый адрес для стека (т.е., что прошивка записана, и есть куда идти). Этот момент сигнализирую вспышкой светодиода. Дальше перехожу в основную программу. Сразу после программирования диод не пыхает, т.е., не обнаруживается зашитая прошивка. После сброса - нормально проходит, после 2 с ожидания в загрузчике. Если нажму кнопку в это время, после 16 с светодиод тоже пыхает, но перехода нет. Чудо. Запросы от USART стираю. Больше никакими прерываниями не пользуюсь. P.S. Я, собственно, прерываниями от USART не пользуюсь. Проверяю состояние. Постирал pending биты запросов. Еще забыл про таймер. От него тоже стер. Всё то же.
|
|
|
|
|
Mar 19 2015, 13:24
|
Участник

Группа: Участник
Сообщений: 28
Регистрация: 5-12-06
Пользователь №: 23 160

|
Есть проблема со входом в бутлоадер STM32L051 (cortex m0+) из тела программы, микроконтроллер сбрасывается. Тот же код только с другими адресами прекрасно работает на STM32L151 и STM32L401 (cortex m3/m4). Подскажите в чем может быть проблема? CODE
void (*SysMemBootJump)(void);
SysMemBootJump = (void (*)(void)) (*((unsigned int*) 0x1FF00004));
__disable_irq(); SysTick->CTRL = 0; SysTick->LOAD = 0; SysTick->VAL = 0;
__set_MSP(0x20001000); SysMemBootJump();
Сообщение отредактировал veteq - Mar 19 2015, 13:25
|
|
|
|
|
Mar 23 2015, 11:29
|

Универсальный солдатик
     
Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362

|
Цитата(ViKo @ Mar 13 2015, 16:47)  Теперь задача - зашифровать-расшифровать прошивку. Попробовал XTEA. Пока только закодировал.  Не знаю, сколько времени будет декодироваться в микроконтроллере. Есть одно замечание. Поскольку кодируются блоки из двух слов, то и перемешивание битов идет в пределах этих двух слов. Дальше все начинается сначала. То есть, одинаковые последовательности слов в начале двоичного файла прошивки (пустые обработчики прерываний) после кодирования тоже будут выглядеть одинаково. Что наталкивает взломщика на принцип кодирования. Сделал и декодирование, все по https://ru.wikipedia.org/wiki/XTEA#cite_note-report-1Декодируется быстро, практически, мгновенно. Фактически, имеется 4 32-битовых ключа, и количество итераций тоже можно выбрать на свой вкус.
|
|
|
|
|
Mar 25 2015, 10:00
|

Универсальный солдатик
     
Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362

|
Цитата(Сергей Борщ @ Mar 25 2015, 12:52)  Пойдет, но я бы не экономил. Потом когда-нибудь будете портировать на более продвинутый кортекс у которого это место в таблице занято и будете долго искать причину неработы. Это раз. Второе - будет довольно сложно заставить линкер класть размер в уже занятое векторами место. А сразу за векторами никто ему мешать не будет. Полистал книжку по Cortex-M4, там тоже дыра. Можно и после векторов прерываний разместить, но и там сдвиг возможен в новых микроконтроллерах, даже с большей вероятностью. Линкером класть не умею (вернее, вычислять размер не умею). Буду своей программой записывать, туда же CRC, туда же и серийный номер. STM32 ST-Link Utility умеет перезаписывать флэш (ей серийный номер и обновляю). Э-э, CRC туда поместить нельзя. Иначе при расчете нужно проскакивать ее. Ее лучше в конец приписать, чтобы полный расчет дал 0.
|
|
|
|
|
Mar 26 2015, 12:36
|

Универсальный солдатик
     
Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362

|
Цитата(AHTOXA @ Mar 26 2015, 08:30)  Напишите перед использованием объявление: extern uint16_t Serial; Так не пробовал. У меня же строка. А extern char *Serial; компилируется, но не работает. Потому что не указатель. А extern char[] Serial не компилируется. Как-то надо создать указатели на строки в ассемблерном файле. Неужели вручную? Сообразил: TxBuffer_write((char *)&Serial);
|
|
|
|
|
Mar 26 2015, 13:09
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(ViKo @ Mar 26 2015, 14:36)  А extern char *Serial; компилируется, но не работает. Потому что не указатель. А extern char[] Serial не компилируется. " А кто из телепатов не догадался, что там у меня в исходнике и как ругается компилятор - я не виноват". Цитата(ViKo @ Mar 26 2015, 14:36)  Сообразил: TxBuffer_write((char *)&Serial); Какое-то масло масляное. Как теперь объявлен Serial?
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Mar 26 2015, 13:14
|

Универсальный солдатик
     
Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362

|
Цитата(Сергей Борщ @ Mar 26 2015, 16:09)  " А кто из телепатов не догадался, что там у меня в исходнике и как ругается компилятор - я не виноват". Какое-то масло масляное. Как теперь объявлен Serial? Я, вроде, всё описал выше. В startup.s: Код EXPORT Serial ALIGN 4 Serial DCB "0002",0 В Main.h: Код extern const char Serial; Использую: Код TxBuffer_write((char *)&Serial);
|
|
|
|
|
Mar 26 2015, 14:11
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(ViKo @ Mar 26 2015, 15:14)  Я, вроде, всё описал выше. Про main.h не было. И текста ругани компилятора не было. Вот так должно работать: Код extern const char Serial[];
TxBuffer_write(Serial); Цитата(ViKo @ Mar 26 2015, 15:14)  Код extern const char Serial; А, тогда понятно, почему потребовалось брать адрес и грубой силой приводить его к другому типу.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
  |
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0
|
|
|