|
|
  |
Ещё раз о бутлоадере, Некоторые тонкости. |
|
|
|
Oct 4 2007, 09:37
|

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

|
Цитата(SasaVitebsk @ Oct 4 2007, 12:17)  Хотелось бы использовать незадействованные вектора бутлоадера. Например +2. Как это сделать. Если использовать pragma location, то наверняка будет linker ругаться. Конечно можно упростить задачу и использовать какой нибудь признак в EEPROM. Или портить CRC к примеру, но что-то мне не нравится такой подход. Есть некая нога, которую приложение всегда настраивает на вывод. Например выход USART или нога, на которой висит светодиод. После сброса все порты гарантировано настроены на ввод. Значит, используюя даже одну точку входа по направлению этой ноги можем судить - мы попали по сбросу или по переходу из приложения. По второму вопросу: в коде пишем Код extern void BootLoader(); В командной строке линкера добавляем -DBootLoader=0x1800 или в .xcl дописываем -DBootLoader=0x1800. Все. в приложении делаем dir_out(MANUAL_PROG); __disable_interrupt(); BootLoader(); На всякий случай в __low_level_init() лодыря сразу же запретить прерывания и переключить вектора на лодырь (если используются).
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Oct 9 2007, 07:28
|

Местный
  
Группа: Свой
Сообщений: 232
Регистрация: 26-02-07
Из: г. Зеленоград
Пользователь №: 25 669

|
Цитата(SasaVitebsk @ Oct 4 2007, 13:17)  Задумка такая.
По старту попадаем в бут. Там считаем CRC и если Ok - выходим на саму приложение. Если не Ok то переходим на сам лодырь.
В приложении по команде переходим непосредственно на лоадырь. То есть мне надо две точки входа. По сбросу и по команде. Хотелось бы использовать незадействованные вектора бутлоадера. Например +2. Как это сделать. Если использовать pragma location, то наверняка будет linker ругаться.
Конечно можно упростить задачу и использовать какой нибудь признак в EEPROM. Или портить CRC к примеру, но что-то мне не нравится такой подход.
А кто как делает? Добавлю и я свои наработки: 1. Использовать ногу процессора - метод надежный, но IMHO это будет "жирновато", ног не всегда хватает+на плате надо вешать на эту ногу резюк, кроме того в разных проектах это могут быть разные ноги. 2. Вариант с EEPROM более предпочтителен, поскольку 1-й старт после прошивки программатором BootLoader-a всегда будет на него. Тогда при отладке приложения работа будет такой: а) - загрузка приложения б) подача BootLoader-у команды передать управление на приложение, т.е. на адрес 0x0000 После отладки - прописать в EEPROM признак: сразу после Reset передавать управление на приложение. Правда здесь есть один подводный камень - зависание приложения или необходимость его коррекции. Здесь можно использовать такой метод: Если в течении, например 1-ой минуты проходит не менее 10-ти горячих рестартов, то в EEPROM устанавливается признак блокировки перехода на приложение.
--------------------
Вяжешь - вой, а поедешь - песни пой. Между "хочу" и "можно" всегда есть дистанция
|
|
|
|
|
Oct 9 2007, 10:45
|

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

|
Цитата(IJAR @ Oct 9 2007, 10:28)  1. Использовать ногу процессора - метод надежный, но IMHO это будет "жирновато", ног не всегда хватает+на плате надо вешать на эту ногу резюк, кроме того в разных проектах это могут быть разные ноги. Используется нога, которая в приложении выполняет какую-либо функцию. Например выход UART или выход на светодиод. Подтяжка желательна, но это может быть штатная подтяжка (например подтяжка CS внешнего устройства или тот же светодиод). В разных проектах пишется #define MANUAL_PROG C,3,L или #define MANUAL_PROG B,0,H
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Oct 9 2007, 11:40
|

Местный
  
Группа: Свой
Сообщений: 232
Регистрация: 26-02-07
Из: г. Зеленоград
Пользователь №: 25 669

|
Цитата(Сергей Борщ @ Oct 4 2007, 13:37)  Есть некая нога, которую приложение всегда настраивает на вывод. Например выход USART или нога, на которой висит светодиод. После сброса все порты гарантировано настроены на ввод. Значит, используюя даже одну точку входа по направлению этой ноги можем судить - мы попали по сбросу или по переходу из приложения. А какой смысл возврата в BootLoader из приложения? Как я понимаю, по Reset всегда на BL а далее разбор: пользоваться ли его услугами или сразу перейти к выполнению приложения. Может я что-то "не догоняю".
--------------------
Вяжешь - вой, а поедешь - песни пой. Между "хочу" и "можно" всегда есть дистанция
|
|
|
|
|
Oct 9 2007, 12:01
|

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

|
Цитата(IJAR @ Oct 9 2007, 14:40)  А какой смысл возврата в BootLoader из приложения? Чтобы обновлять ПО по команде извне по интерфейсу связи. Это удобно как для устройств, находящихся в труднодоступных местах, так и для устройств на столе - для перепрошивки их не нужно вскрывать чтобы замкнуть джампер. Протокол связи поддерживается приложением, оно получает команду "обновление", передает управление в загрузчик, загрузчик формирует ответ "к загрузке готов" в соответствии с протоколом связи. Если же мы стартанули загрузчик принудительно или приложения нет/не сошлась контрольная сумма, то загрузчик сам ожидает команду "обновление" (и это единственная команда из всего протокола, которая в нем реализована).
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Oct 9 2007, 12:33
|

Местный
  
Группа: Свой
Сообщений: 232
Регистрация: 26-02-07
Из: г. Зеленоград
Пользователь №: 25 669

|
Цитата(Сергей Борщ @ Oct 9 2007, 16:01)  Чтобы обновлять ПО по команде извне по интерфейсу связи. Это удобно как для устройств, находящихся в труднодоступных местах, так и для устройств на столе - для перепрошивки их не нужно вскрывать чтобы замкнуть джампер. Протокол связи поддерживается приложением, оно получает команду "обновление", передает управление в загрузчик, загрузчик формирует ответ "к загрузке готов" в соответствии с протоколом связи. Если же мы стартанули загрузчик принудительно или приложения нет/не сошлась контрольная сумма, то загрузчик сам ожидает команду "обновление" (и это единственная команда из всего протокола, которая в нем реализована). Логично! Но есть один недостаток: приложение должно поддерживать протокол (иметь возможно лишний программный код) . А если приложение пишет другой программист, не разработчик BL или вообще в другой конторе (например у потребителя устройства). Ему же надо все описать, что он должен сделать.
--------------------
Вяжешь - вой, а поедешь - песни пой. Между "хочу" и "можно" всегда есть дистанция
|
|
|
|
|
Oct 9 2007, 14:33
|

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

|
Цитата(IJAR @ Oct 9 2007, 15:33)  Но есть один недостаток: приложение должно поддерживать протокол (иметь возможно лишний программный код). Да, есть такое. Но если какой-то интерфейс с внешним миром есть, обычно поверх него есть и какой-то протокол. Добавить в него одну команду обычно несложно. Если интерфейса нет (точнее он не выведен наружу) - ну остается джампер на плате. Цитата(IJAR @ Oct 9 2007, 15:33)  А если приложение пишет другой программист, не разработчик BL или вообще в другой конторе (например у потребителя устройства). Ему же надо все описать, что он должен сделать. Да. Описываю так: 1) запретить прерывания 2)настроить ногу X на вывод. 3) перейти по адресу 0x1800. В общем главное - что мы друг друга поняли.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Oct 9 2007, 23:55
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Сергей, всётаки по коду не уложился в 2к. Со своим WakeUp-ом. Там ещё некоторые приколы есть. У меня должно осуществляться обновление прямо в сети. Соответственно при обновлении требуется сверять серийный номер устройства. Его я не хочу записывать в лоадер, дабы лоадер унифицированный был. Поэтому при перепрошивке вытаскиваю номер перепрошиваю и сохраняю номер.  Короче впрямую в 2к не влез. В связи с тем, что сам протокол Wake, если работать ч/з прерывания, практически не связан с лоадером - переписал его на ASM. Думаю так влезу. Но вот возник вопрос. там есть конструкция в лоадере если помнишь. typedef union { struct { uint8_t m_rxBuffer[BUFFER_SIZE]; // Receive buffer uint8_t m_pageBuffer[PAGE_SIZE]; // Page is assembled here before // getting programmed to flash mem } part1; #define rxBuffer sharedbufs.part1.m_rxBuffer Честно говоря смысл её не всосал. Чтобы к одному и тому же участку памяти под разными именами обращаться, что ли. Но у меня с ней проблемы на ASM. Не могу понять как на ASM указать адрес начала буфера приёмного (rxBuffer)??? Сори.  Разобрался. Просто как-то в голове не укладывается как в ассемблере имена структур фигурируют. Понятно что потом линкер их к адресам приводит, но всё равно несколько диковато.
|
|
|
|
|
Oct 10 2007, 10:58
|

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

|
Цитата(SasaVitebsk @ Oct 10 2007, 02:55)  Сергей, всётаки по коду не уложился в 2к. Со своим WakeUp-ом. Ты пытаешься засунуть в загрузчик еще какие-то команды кроме UpdateFirmware? У меня Wake-подобный протокол, я сделал так: Код static unsigned char busReceiveByte(void) { uint8_t c = UART_getchar(); if(c == CTL) c = UART_getchar() ^ 0x20; return c; } __C_task void main(void) { ....... if(!dir(MANUAL_PROG)) { uint8_t command[7]; command[0] = 0xC0; // Begin Of Frame uint16_t Tmp = SerialNo(); command[1] = Tmp; command[2] = Tmp >> 8; command[3] = 0xFF; uint8_t *ptr = &command[1]; Tmp = 0xFFFF; Tmp = CRC_XMODEM (*ptr++, Tmp); Tmp = CRC_XMODEM (*ptr++, Tmp); Tmp = CRC_XMODEM (*ptr++, Tmp); command[4] = ~Tmp; command[5] = ~(Tmp >> 8); command[6] = 0xC1; // End Of Frame uint8_t idx = 0; do { uint8_t c = busReceiveByte();
if(c != command[idx]) idx = 0; else idx++; } while(idx < sizeof(command)); // wait for EOM } busReplyByte(ERROR_OK); Т.е. я формирую образ всей команды и сравниваю проходящий поток с образцом. Сгенерить образец гораздо проще, чем реализовывать весь конечный автомат приема. Увы, это проходит только для одной команды. Цитата(SasaVitebsk @ Oct 10 2007, 02:55)  Соответственно при обновлении требуется сверять серийный номер устройства. Его я не хочу записывать в лоадер, дабы лоадер унифицированный был. Поэтому при перепрошивке вытаскиваю номер перепрошиваю и сохраняю номер.  У меня в области загрузчика есть функция Код #pragma segment="GET_SERIAL" #pragma location="GET_SERIAL" __root uint16_t SerialNo(void) { return ~0;} При прошивке я указываю AvRealу -c?serno2=1826,serno.txt и он автоматически при программировании подставляет в эту функцию уникальные номера. Номер хранится в загрузчике и не может быть случайно стерт с приложением. Функцию SerialNo() можно вызвать и из приложения, поэтому и приложение становится независимым от серийника. Функция занимает 6 байт, что меньше чем код для чтения 16-битной константы из флеш с помощью LPM Цитата(SasaVitebsk @ Oct 10 2007, 02:55)  Честно говоря смысл её не всосал. Чтобы к одному и тому же участку памяти под разными именами обращаться, что ли. Именно так. Для улучшения читабельности. Т.е. "сейчас мы работаем с этим блоком как с буфером приема" Цитата(SasaVitebsk @ Oct 10 2007, 02:55)  Сори.  Разобрался. Просто как-то в голове не укладывается как в ассемблере имена структур фигурируют. Понятно что потом линкер их к адресам приводит, но всё равно несколько диковато.  Ну, если не на плюсах писать, то имена как и у обычных переменных. А вот к полям из ассемблера, увы, только вручную указывая смещение от начала структуры.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Oct 10 2007, 13:35
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Цитата(Сергей Борщ @ Oct 10 2007, 13:58)  Ты пытаешься засунуть в загрузчик еще какие-то команды кроме UpdateFirmware? У меня Wake-подобный протокол, я сделал так: Пытаюсь Сергей.  Вчера в 3 часа ночи получил результат 2232.  Если бы надо было байт 50 выжать ещё бы попытался, а так буду пробовать в обход. Я видишь ли с прерываниями завязался. Возможно придётся отказаться. Только вектора занимают 112 байт. Можно конечно их под себя подмять. Короче буду думу думать. Жалко выделять 4к если нехватает совсем чуть чуть. Спасибо за рекомендации. Разбираюсь. Пользуюсь. Помагает.
|
|
|
|
|
Oct 10 2007, 15:46
|

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

|
Цитата(SasaVitebsk @ Oct 10 2007, 16:35)  Пытаюсь Сергей. . Интересно, какие? Мне пока в голову приходила всего одна команда, полезная в загрузчике - запрос версии. Приложение отвечает свою версию, загрузчик отвечает 0. Тогда центральный узел понимает, что приложение ёк и его надо перешить обязательно. А по ответу приложения о версии центральный узел может определять - нужно ли апгрейдить. Планирую реализовать. Думаю, что второй массив с образцом команды и поиск по нему как в примере выше не сильно раздует код. А на какие запросы у тебя отвечает загрузчик? Цитата(SasaVitebsk @ Oct 10 2007, 16:35)  Только вектора занимают 112 байт. Можно конечно их под себя подмять. "Не только мона, но и нуна!" А как в .xcl сегменты распределяются? Я отдаю под код все после последнего используемого вектора: Код // Interrupt vectors -Z(CODE)INTVEC=1800-1825 // Code memory -Z(CODE)NEAR_F,HUGE_F,SWITCH,INITTAB,DIFUNCT,CODE=1802-1FFF -Z(CODE)GET_SERIAL,GET_AREA#1802-1FFF Хм. Скопировал сюда и призадумался - зачем указываю 0x1802, ведь 0x1800 всегда занят... Можно писать 1800-1FFF. Цитата(SasaVitebsk @ Oct 10 2007, 16:35)  Жалко выделять 4к если нехватает совсем чуть чуть. "Вам то хорошо..." (с) "Операция "С Новым Годом"". Вот если бы было 2 и не больше, как в m8, это бы еще сильнее стимулировало
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|