реклама на сайте
подробности

 
 
8 страниц V   1 2 3 > »   
Reply to this topicStart new topic
> STM32 bootloader, написание собственного бута
ierofant
сообщение Jan 3 2012, 13:31
Сообщение #1


Участник
*

Группа: Участник
Сообщений: 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
 
Go to the top of the page
 
+Quote Post
skripach
сообщение Jan 3 2012, 16:12
Сообщение #2


■ ■ ■ ■
*****

Группа: Свой
Сообщений: 1 100
Регистрация: 9-08-06
Пользователь №: 19 443



Я бы делал вариант 2, т.к. в первом варианте во вновь загруженной прошивке может неожиданно вылезти глюк при загрузке новой пошивки и это "конец". sm.gif Да и памяти больше нужно. Хотя конечно глюк может вылезти везде.
Переход на приложение:
Код
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 разных проекта?

Да.


--------------------
Делай что должен и будь что будет.
Go to the top of the page
 
+Quote Post
Cosmojam
сообщение Jan 3 2012, 20:52
Сообщение #3


Местный
***

Группа: Свой
Сообщений: 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; | блог тут
Go to the top of the page
 
+Quote Post
skripach
сообщение Jan 3 2012, 21:30
Сообщение #4


■ ■ ■ ■
*****

Группа: Свой
Сообщений: 1 100
Регистрация: 9-08-06
Пользователь №: 19 443



Цитата
skripach, Ваш пример ничем не отличается от приведённого ТС.

Возможно, автор вопрошал "как перейти на выполнение с определенного адреса?" я привел функцию из своего загрузчика под stm32.
Цитата
Указания нового положения таблицы векторов нет.

Если правильно помню это сделано в приложении, перед прыжком были выключены все прерывания.


--------------------
Делай что должен и будь что будет.
Go to the top of the page
 
+Quote Post
ierofant
сообщение Jan 3 2012, 21:33
Сообщение #5


Участник
*

Группа: Участник
Сообщений: 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, я пока пытаюсь осуществить просто переход выполнения программы по определенному адресу, таблицу векторов даже не трогаю.sm.gif Выше отписался, что у меня не так.


Кстати, я правильно понимаю алгоритм работы бутлоадера?
После того, как он закончил все свои необходимые действия, нужно:
1. перенести таблицу векторов по адресу в памяти, где начинается основная программа.(т.е. если у меня основная программа записана, начиная с 2й страницы флеша (1 страница - 1кб), то нужно будет сделать так: NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x08000800)wink.gif
2. Перейти к выполнению кода на тот же адрес (0x08000800)

Верно ли я все понял?

P.S. Кстати, skripach, спасибо за приведенный код.

Сообщение отредактировал ierofant - Jan 3 2012, 21:38
Go to the top of the page
 
+Quote Post
skripach
сообщение Jan 4 2012, 00:49
Сообщение #6


■ ■ ■ ■
*****

Группа: Свой
Сообщений: 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). см. таблицу векторов.


--------------------
Делай что должен и будь что будет.
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Jan 4 2012, 07:47
Сообщение #7


Гуру
******

Группа: Модераторы
Сообщений: 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)
Go to the top of the page
 
+Quote Post
ierofant
сообщение Jan 8 2012, 00:38
Сообщение #8


Участник
*

Группа: Участник
Сообщений: 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 линкерный скрипт?
Go to the top of the page
 
+Quote Post
Almaz1988
сообщение Sep 19 2012, 09:41
Сообщение #9


Частый гость
**

Группа: Участник
Сообщений: 100
Регистрация: 19-09-12
Пользователь №: 73 602



Так же пишу бутлоадер, но для lpc11c24.
Столкнулся с казалось бы элементарной проблемой - не могу прошить даже самую простую программу (моргание светодиодом) по адресу, отличному от нуля.
Т.е. если стартовый адрес во флеш-памяти ставлю 0х00000000, то программа стартует.
Стоит его поменять на, к примеру, 0х00000100 и программа не запускается.
Пишу в Keil. Адрес выставляю следующим образом:
правый клик по проекту-->Options for target-->Linker-->R/O base = 0x00000100.
Попробовал прошить из LPCXpresso, - получил ошибку "vectors still have erased values".
Спасибо за ответы
Go to the top of the page
 
+Quote Post
_Артём_
сообщение Sep 19 2012, 10:09
Сообщение #10


Гуру
******

Группа: Свой
Сообщений: 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
Go to the top of the page
 
+Quote Post
Almaz1988
сообщение Sep 19 2012, 10:55
Сообщение #11


Частый гость
**

Группа: Участник
Сообщений: 100
Регистрация: 19-09-12
Пользователь №: 73 602



Спасибо Артем, ваш ответ очень помог)

По адресу 0х0000 2000 залил "Рабочая программа", которая моргает 4 раза.

По адресу 0х0000 0000 тоже залил "Бутлоадер", которая моргает 4 раза, а потом прыгает по адресу 0х2169 (функция main() "Рабочей программы" по MAP-файлу).

Все сработало как и ожидалось - МК моргнул 8 раз. Т.е. МК стартовал с "Бутлоадера" затем передал управление "Рабочей программе".

Но вот на следующем этапе на место "Рабочей программы" я залил программу по сложнее ( которую собственно говоря и пишу). В итоге МК моргнул 4 раза, а "Рабочая программа" не запустилась. Подозрения падают на то, что она работает по прерываниям, о чем, собственно, ваша ссылка.

Не могли вы "на пальцах" объяснить, почему эти прерывания требуют особого отношения и для чего нужен ремап векторов?
Go to the top of the page
 
+Quote Post
_Артём_
сообщение Sep 19 2012, 11:56
Сообщение #12


Гуру
******

Группа: Свой
Сообщений: 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.
Go to the top of the page
 
+Quote Post
Almaz1988
сообщение Sep 19 2012, 12:36
Сообщение #13


Частый гость
**

Группа: Участник
Сообщений: 100
Регистрация: 19-09-12
Пользователь №: 73 602



Исправил на reset handler.

По поводу ремапа, как я понял, если "Бутлоадер" залит по адресу 0х0000 0000, а "Рабочая программа" залита по адресу 0х0000 2000 то:

"Бутлоадер", перед тем как передать управление "Рабочей программе" должен таблицу векторов, размещенную по адресам 0х0000 0000 - 0х0000 00С0 скопировать в адреса 0х0000 0000 - 0х0000 20С0, иначе "Рабочая программа" не сможет запуститься?
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Sep 19 2012, 20:15
Сообщение #14


Гуру
******

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



QUOTE (Almaz1988 @ Sep 19 2012, 15:36) *
"Бутлоадер", перед тем как передать управление "Рабочей программе" должен таблицу векторов, размещенную по адресам 0х0000 0000 - 0х0000 00С0 скопировать в адреса 0х0000 0000 - 0х0000 20С0, иначе "Рабочая программа" не сможет запуститься?
Если мы говорим о Cortex-M3 (M4), то нужно записать в регистр VTOR значение 0x00002000, ведь именно по этому адресу начинается таблица векторов вашего приложения (ячека с начальным значением стека также входит в эту таблицу).


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
Almaz1988
сообщение Sep 20 2012, 04:24
Сообщение #15


Частый гость
**

Группа: Участник
Сообщений: 100
Регистрация: 19-09-12
Пользователь №: 73 602



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

Cortex-M0
Go to the top of the page
 
+Quote Post

8 страниц V   1 2 3 > » 
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 21st July 2025 - 13:45
Рейтинг@Mail.ru


Страница сгенерированна за 0.01526 секунд с 7
ELECTRONIX ©2004-2016