|
|
  |
LPC11xx не стартует код из своего бутлоадера |
|
|
|
Sep 24 2012, 11:18
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Цитата(Almaz1988 @ Sep 24 2012, 11:46)  Работает только если в "рабочей программе" не используются прерывания. Если прерывания используются, то "Рабоча программа" нивкакую не стартует. Какие есть догадки, в чем ошибка? Попробуйте по-другому. Например так: Код typedef void (*ISRPtr) (void); uint32_t *ram_vector_table, *flash_vector_table; ram_vector_table=(uint32_t *)0x10000000; flash_vector_table=(uint32_t *)0x1000; for (uint_fast32_t i=0; i<48; i++) *ram_vector_table++=*flash_vector_table++; LPC_SYSCON->SYSMEMREMAP=1; __set_MSP(*(uint32_t *)0x1000); ISRPtr application_reset_handler=(ISRPtr)(*(uint32_t *)0x1004); application_reset_handler();
|
|
|
|
|
Sep 24 2012, 11:41
|

Профессионал
    
Группа: Модераторы
Сообщений: 1 951
Регистрация: 27-08-04
Из: Санкт-Петербург
Пользователь №: 555

|
Т.к. у Cortex-M0 нет VTOR и переместить таблицу прерываний нельзя, а все таки понадобился вторичный бутлоадер, я сделал так: в бутлоадере прерывания не используются! И все указатели из таблицы прерываний (кроме ресета) указывают на такую функцию: Код void handler(void) { asm ( "mrs r0,IPSR\n" "movs r1,#0x1\n" "lsls r1,#10\n" "add r0, r1\n" "lsls r0,#2\n" "ldr r0,[r0]\n" "mov r15, r0"); while(1); } А старт приложения такой последовательностью: Код asm ("movs r1,#0x1\n" "lsls r1,#12\n" "ldm r1, {r0,r1}\n" "mov r13, r0\n" "mov r15, r1"); while(1); Все работает! Приложение ничем не отличается от обычного, только начинается с адреса 0x1000 по которому лежит обычная таблица прерываний.
|
|
|
|
|
Sep 24 2012, 12:45
|

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

|
QUOTE (Almaz1988 @ Sep 24 2012, 11:46)  Вот код бутлоадера: Для вставки кода в сообщения есть кнопочка  на форме ввода. Используйте ее, пожалуйста. Здесь так принято. QUOTE (Almaz1988 @ Sep 24 2012, 11:46)  uint32_t const * pSrc = (uint32_t const *)0x00001000; //копируем первые 200 байт флеша, начиная с адреса 0х1000 в ОЗУ uint32_t * pDst = (uint32_t *)0x10000000; #define VECTORS_COUNT 128 for(uint_fast8_t i = 0; i < VECTORS_COUNT; ++i) *pDst++ = *pSrc++; А теперь смотрите внимательно: 1) Вы в комментариях пишете, что копируете 200 байт, а на самом деле копируете 128*sizeof(uint32_t), т.е. 512 байт. 2) Та часть ОЗУ, куда вы пишете, у вас зарезервирована? Т.е. не получается ли так, что вы копируя разрушаете данные или стек загрузчика?
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Sep 24 2012, 15:09
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Цитата(Almaz1988 @ Sep 24 2012, 11:46)  Работает только если в "рабочей программе" не используются прерывания. Если прерывания используются, то "Рабоча программа" нивкакую не стартует. Какие есть догадки, в чем ошибка? Такая догадка: в рабочей программе происходит очистка памяти, в которой находятся адреса обработчиков прерываний. Проверьте на входе в main рабочей программы что у вас по адресам ОЗУ 0x10000000-0x100000C0? Заполнена эта область нулями или там адреса обработчиков? Эту область нужно как-то выделить в скрипте линкера или указать в скрипте область ОЗУ рабочей программы не 0x10000000-0x100001FF, а от 0x100000C0 до 0x100001FFF. Цитата(Almaz1988 @ Sep 24 2012, 11:46)  нивкакую не стартует. Какие есть догадки, в чем ошибка? Не стартует - это что значит? А что она делает? В HardFault не попадает? P.S. Выложили бы проекты целиком - программа это не только текст.
|
|
|
|
|
Sep 28 2012, 06:22
|
Частый гость
 
Группа: Участник
Сообщений: 100
Регистрация: 19-09-12
Пользователь №: 73 602

|
Наметился небольшой прогресс)) В "загрузчике" копирую из флеша ячейки 0х0000-0х0200 в ОЗУ по адресам 0х1000 0000 - 0х1000 0200, делаю ремап и затем прыгаю по адресу Reset_handler(0х1000 0004). "Загрузчик" успешно перезапускается. Если же я копирую в ОЗУ из адресов флеша 0х2000-0х2200. То после ремапа и прыжка "Рабочая программа", которая залита по адресу 0х2000 не запускается. В том что у меня залита корректная рабочая программа я могу убедиться если не делая ремапа прыгаю: Код void (*fptr)(void); fptr = (void (*)(void))0x000002675; fptr(); Тогда рабочая программа запускается. Прикладываю проекты "Загрузчика" и "Рабочей программы". Еще один вопрос: Используемый МК - lpc11c24 с "железным" протоколом CAN. В рабочей программе он используется. В мануале на МК написано: Цитата On-chip RAM from address 0x1000 0050 to 0x1000 00B8 is used by the CAN API. This address range should not be used by the application. For applications using the on-chip CAN API, the linker control file should be modified appropriately to prevent usage of this area for application’s variable storage. Выходит, что ремап векторов в ОЗУ будет приводить к затиранию CAN функций? Значит ремап не решение. Какие есть другие способы решения проблемы, если и загрузчик и рабочая программа активно будут пользоваться прерываниями?
|
|
|
|
|
Sep 28 2012, 07:15
|

Профессионал
    
Группа: Модераторы
Сообщений: 1 951
Регистрация: 27-08-04
Из: Санкт-Петербург
Пользователь №: 555

|
Цитата(Almaz1988 @ Sep 28 2012, 10:22)  Еще один вопрос: Используемый МК - lpc11c24 с "железным" протоколом CAN. В рабочей программе он используется. В мануале на МК написано:
Выходит, что ремап векторов в ОЗУ будет приводить к затиранию CAN функций?
Значит ремап не решение. Какие есть другие способы решения проблемы, если и загрузчик и рабочая программа активно будут пользоваться прерываниями? Если вы используете функции CAN, которые в ПЗУ лежат, то они могут перетереть ваши вектора - т.к. используют эту область памяти под свои данные (сами функции стереть нельзя они в ПЗУ) В любом случае хранить вектора прерываний в ОЗУ для данного контроллера не очень хорошее решение! Я уже привел метод - он прекрасно работает! http://electronix.ru/forum/index.php?s=&am...t&p=1095595Можно использовать метод еще тупее - в бутлоадере создать обработчик на каждое прерывание (для M0 их не так много) который просто берет указатель из таблицы прерываний приложения и переходит по нему. (собственно говоря приведенный исходник это и делает, но что бы не плодить функции он узнает номер из регистра). При этом приложения с бутлоадером и без бутлоадера отличаются только icf файлом! (началом флеша) и никаких ограничений на использования памяти нет.
|
|
|
|
|
Sep 28 2012, 07:56
|

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

|
QUOTE (Almaz1988 @ Sep 28 2012, 09:22)  и затем прыгаю по адресу Reset_handler(0х1000 0004). А вот неверно - вы прыгаете на вектор сброса. А надо прыгать на адрес, которых хранится в этом векторе: CODE void (**fptr)(void); fptr = (void (**)(void))0x10000004; (*fptr)();
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Sep 28 2012, 08:10
|
Частый гость
 
Группа: Участник
Сообщений: 100
Регистрация: 19-09-12
Пользователь №: 73 602

|
Цитата(KRS @ Sep 28 2012, 11:15)  Если вы используете функции CAN, которые в ПЗУ лежат, то они могут перетереть ваши вектора - т.к. используют эту область памяти под свои данные (сами функции стереть нельзя они в ПЗУ) В любом случае хранить вектора прерываний в ОЗУ для данного контроллера не очень хорошее решение! Я уже привел метод - он прекрасно работает! http://electronix.ru/forum/index.php?s=&am...t&p=1095595Можно использовать метод еще тупее - в бутлоадере создать обработчик на каждое прерывание (для M0 их не так много) который просто берет указатель из таблицы прерываний приложения и переходит по нему. (собственно говоря приведенный исходник это и делает, но что бы не плодить функции он узнает номер из регистра). При этом приложения с бутлоадером и без бутлоадера отличаются только icf файлом! (началом флеша) и никаких ограничений на использования памяти нет. Ваш пример подойдет в случае, если в загрузчике не используются прерывания. В моем же случае, я планирую это делать. Я думаю написать обработчики для каждого прерывания с двумя вариантами выбора: Код __ASM void CAN_IRQapplication(void) { ldr r0, =0x2074 ldr r0, [r0] mov pc, r0 } __ASM void CAN_IRQbootloader(void) { ldr r0, =0x0074 ldr r0, [r0] mov pc, r0 } void CAN_IRQ(void) { if ( main < 2000 ) { CAN_IRQapplication(); } else { CAN_IRQbootloader(); } } Такое решение приведено в примере NXP secondary bootloader (без варианта выбора). Сейчас пытаюсь вкрутить их стартап-файл (LPCXpresso) в свой Keil-овский проект. Сработает?
|
|
|
|
|
Sep 28 2012, 09:06
|
Частый гость
 
Группа: Участник
Сообщений: 100
Регистрация: 19-09-12
Пользователь №: 73 602

|
Цитата(Сергей Борщ @ Sep 28 2012, 12:44)  Нет. для этой программы main будет константой, она понятия не имеет, что в другой программе есть свой main и никаким образом не может получить его адрес. Можно взводить и анализировать какой-то флаг. Так нам и не нужен main другой программы. Запускается загрузчик. Начинает работать. При прерываниях прыгает в свою табл векторов ( т.к. адрес его функции main < 0x2000). Закончил работу, передал управление рабочей программе. Та начинает работу и при прерываниях прыгает по своим адресам ( т.к. адрес его функции main > 0x2000). что в keil'e, что в LPCXpresso startup-файлы полны загадочных extern и импорт, например: в Keil: Цитата Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT __main LDR R0, =__main BX R0 ENDP в LPCXpresso: Цитата extern unsigned long _etext; extern unsigned long _data; extern unsigned long _edata; extern unsigned long _bss; extern unsigned long _ebss;
void ResetISR(void) { unsigned char *pulSrc, *pulDest; pulSrc = (unsigned char *)&_etext; for(pulDest = (unsigned char *)&_data; pulDest < (unsigned char *)&_edata; ) { *pulDest++ = *pulSrc++; } ......... Откуда в Кейловском варианте берется __main, а в LPC-шном _etext, _data, _edata....? Их тупо проэкстернили, но они нигде не инициализируются
|
|
|
|
|
Sep 28 2012, 09:43
|
Частый гость
 
Группа: Участник
Сообщений: 100
Регистрация: 19-09-12
Пользователь №: 73 602

|
Цитата(Сергей Борщ @ Sep 28 2012, 13:26)  Прыгает в каком месте? В тех функциях, которые принадлежат загрузчику и знают только о существовании своего main. Либо приложению надо переписать эти функции поверх функций загрузчика, тогда зачем в них ветвление? В каждый проект(загрузчик и рабочая программа) добавляет один и тот же файл (Handlers.h), в котором написаны обработчики прерываний которые отправляют по адресам 0х0000 - 0х00С0, если они вызваны из загрузчика и по адресам 0х2000 - 0х20С), если они вызваны из рабочей программы. По идее нужно просто переделать стартап файл, чтобы из него при всех прерываниях прыгать в мои самописные обработчики прерываний. Но он меня пока вводит в тупик - своими экстернами и импортами нигде не объявленных переменных
|
|
|
|
|
Sep 28 2012, 10:45
|

Профессионал
    
Группа: Модераторы
Сообщений: 1 951
Регистрация: 27-08-04
Из: Санкт-Петербург
Пользователь №: 555

|
Цитата(Almaz1988 @ Sep 28 2012, 12:10)  Я думаю написать обработчики для каждого прерывания с двумя вариантами выбора: там функции на асме не нужны! Проще сразу из С вызвать нужную функцию из таблиц указателей! тут весь вопрос в условии - как определить что бутлоадер еще работает! А можно и один код для всех обработчиков оставить - только базовый адрес таблицы вычислять в зависимости от условия Код void handler(void) { asm ( // вычислить адрес таблицы в R1
"mrs r0,IPSR\n" "add r0, r1\n" "lsls r0,#2\n" "ldr r0,[r0]\n" "mov r15, r0"); while(1); }
|
|
|
|
|
Oct 1 2012, 11:34
|
Частый гость
 
Группа: Участник
Сообщений: 100
Регистрация: 19-09-12
Пользователь №: 73 602

|
Продолжаю штурм)) и в загрузчике и в рабочей программе будут использоваться прерывания: SysTick, SVC, PendSV, CAN. Пишу обработчики для этих прерываний. Если в обработчике для каждого прерывания делаю прыжок, например в SysTick_Handler: Код __ASM void SysTick_Handler(void) { ldr r0, =0x203C ldr r0, [r0] mov pc, r0 } То Загрузчик передает управление Рабочей программе, в которой все прерывания обрабатываются корректно. Но, стоит написать вот так (через функцию посредник): Код __ASM void SysTick_Handler_of_application(void) { ldr r0, =0x203C ldr r0, [r0] mov pc, r0 }
void SysTick_Handler(void) { SysTick_Handler_of_application( ); } И три из четырех обработчика прерывания работать уже не хотят! Обрабатываются только CAN-прерывания! Функции посредники нужны мне для того, чтобы производить проверку, из какой программы вызвано прерывание: Код void SysTick_Handler(void) { if ( *(uint32_t *)0x100001F0 == 0x67 ) //сли обработчик прерывания вызван из рабочей программы, то прыгаем в таблицу векторов рабочей программы SysTick_Handler_of_application( ); else SysTick_Handler_of_bootloader();//иначе обработчик прерывания вызван из загрузчика }
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|