|
|
  |
STM32 bootloader, написание собственного бута |
|
|
|
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(); Спасибо за пояснения)) Но с ремапом у меня нивкакую запускаться не хочет)) Продолжу с понедельника. П.с. и все же есть возможность записи во флеш ассемблерными вставками?(Как запасной вариант, если ремап не запустится)
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|