|
Bootloader для MSP430, как написать бутлоадер для МСП430 |
|
|
|
Oct 15 2007, 11:12
|
Частый гость
 
Группа: Участник
Сообщений: 91
Регистрация: 24-08-06
Из: Москва
Пользователь №: 19 809

|
Цитата(VAI @ Oct 15 2007, 14:36)  Это то понятно, там целое шаманство производить надо и со схемкой, и с программкой, чтоб программить с командной строки, а у меня задача другая стоит: на объекте используется аппарат, который висит на собственной шине, чтоб не ездить за обновлением прошивки, пользователь скачивает из интернета hex, и с помошью нашей же программы закачивает удаленно прошивку по адресу этого устройства, далее я понимаю так, что эту прошивку надо собирать в отдельном файле, проверить контрольную сумму и запустить программный бутлоадер, чтоб он эту прошивку залил, но возможна ситуация, когда на шине пропадает питание, здесь на помошь приходит опять таки бутолоадер, который проверяет CRC прошивки и выдает код ошибки пользователю, то етсь таким образом устройство продолжает работу дальше.. Проблема здесь в том, что как это сделать правильно, у меня попытка очистить флеш по адресу 0x1100 и залить туда код с оперативки (традиционным способом) не увенчалось успехом, после перезагрузки проц вообще перестал работать, приходится перепрошивать чеез JTAG.. Прошивку из шины я пытаюсь сохранять во внешней EEPROM, ибо 64 Кб - маловато для этих целей.. Забыл про проц MSP430f149. Спасибо.
|
|
|
|
|
Oct 15 2007, 12:50
|
Частый гость
 
Группа: Участник
Сообщений: 91
Регистрация: 24-08-06
Из: Москва
Пользователь №: 19 809

|
Цитата(rezident @ Oct 15 2007, 16:11)  asket, вы сначала разберитесь как внутрипрограммно программировать Flash MSP430 в принципе. Когда разберетесь с принципом (на INFO тренируйтесь), то тогда и приступайте к написанию своего BootLoader. Только предполагаю, что вы наверняка наступите на грабли НЕперемещаемых векторов прерываний. Поищите здесь тему про то как в MSP430 организуются перемещаемые вектора. Оно точно тут было год-полтора назад. Благодарю за совет!) Буду разбираться, правда, время поджимает, судя по ответам похоже этим либо занимались либо редко, либо никто.. А вот по поводу перемешаемых векторов я как то не подумал, спасибо, что обратили внимание на этот факт...)
|
|
|
|
|
Oct 15 2007, 13:21
|

Профессионал
    
Группа: Модераторы
Сообщений: 1 120
Регистрация: 17-06-04
Пользователь №: 37

|
Нашел на http://tech.groups.yahoo.com/group/msp430/Коментарий: "Code for an ISR jump table in RAM" может поможет.
--------------------
Если зайца бить, его можно и спички научить зажигать Сколько дурака не бей - умнее не будет. Зато опытнее
|
|
|
|
|
Oct 15 2007, 13:56
|
Частый гость
 
Группа: Участник
Сообщений: 91
Регистрация: 24-08-06
Из: Москва
Пользователь №: 19 809

|
Цитата(VAI @ Oct 15 2007, 17:21)  Нашел на http://tech.groups.yahoo.com/group/msp430/Коментарий: "Code for an ISR jump table in RAM" может поможет. Огромное Вам спасибо за помощь! И еще вопрос: где бы найти описание формата пакета с прошивками например Intel Standart или Intel Extended? Там конечно разбираться то нечего, только вот хотелось бы понять по какому алгоритму считают CRC и что за байт кроется между адресом и данными? Вот первая строчка откомпилированного кода по стандарту hex-Extended: 101100003140000A3C401A023E407E05B012B4E76E, я понял так что 0x10 - длина данных, 0x1100 - адрес 0x00 - ? данные - сама прошивка 16 байт 0x6E - CRC
|
|
|
|
|
Oct 22 2007, 06:19
|
Знающий
   
Группа: Свой
Сообщений: 526
Регистрация: 24-08-07
Из: Беларусь, Минск
Пользователь №: 30 045

|
Цитата(asket @ Oct 19 2007, 11:10)  Окей, спасибо огромное! Кое что стало проясняться.. Еще один вопрос, извиняюсь, чисто ламерский: каким способом программно определить размер той или иной функции? Допустим знаем адрес раположения этой функции, и нужно будет определить размер этой функции, пробывал использовать sizeof(), ругается что операнд не может быть функцией, каким способом можно определить? Если программа написана на ассемблере, то размер можно узнать: поставить метки в начале и конце функции, а где необходимо -- вычислить разность меток, это и будет "размер" или точнее "размер кода" функции. Пример: Код mov #STOP,R7 sub #START,R7 START: ; просто любая функция push R4 push R5 mov R5,R4 pop R5 pop R4 STOP: ret В R7 -- результат
Сообщение отредактировал gotty - Oct 22 2007, 06:20
|
|
|
|
|
Oct 23 2007, 14:16
|
Знающий
   
Группа: Свой
Сообщений: 526
Регистрация: 24-08-07
Из: Беларусь, Минск
Пользователь №: 30 045

|
Цитата(asket @ Oct 23 2007, 16:27)  Еще такой вопрос по поводу сегментации флеш-памяти, компилер IAR по умолчанию ставит сегмент CODE и размещает туда все функции, если они явно пользователем не указаны в каком сегменте их расположить, но мне не понравилось тот факт, что IAR без ведома пихает стандартные функции, и если его стереть, то фактически программа без них не будет нормально работать. Как указать компилятору в какой сегмент нужно раположить эти функции? Можно изменить сегмент CODE (начальный и конечный адреса), таким образом, что бы он отвечал Вашим требованиям. Это можно проделать при помощи параметра линкера: Код -Z(CODE)MYCODE=4000-5000
Сообщение отредактировал gotty - Oct 23 2007, 14:28
|
|
|
|
|
Oct 23 2007, 15:48
|
Частый гость
 
Группа: Участник
Сообщений: 91
Регистрация: 24-08-06
Из: Москва
Пользователь №: 19 809

|
Цитата(gotty @ Oct 23 2007, 18:16)  Можно изменить сегмент CODE (начальный и конечный адреса), таким образом, что бы он отвечал Вашим требованиям. Это можно проделать при помощи параметра линкера: Код -Z(CODE)MYCODE=4000-5000 Это мне понятно, дело здесь в другом, допустим у нас несколько сегментов: -Z(CODE)CODE=1100-1200 -Z(CODE)MYCODE=1200-1500 По умолчанию компилятор IAR помещает стандартные и служебные функции в сегмент CODE, то есть это означает, что если программист забыл указать сегмент, то компилятор автоматом поместит в сегмент --Z(CODE)CODE, то есть мне не хотелось сильно зависеть от данного сегмента, ибо большинство функции находятся в сегменте MYCODE, придется тогда над каждой функцией писать #pragma location="MYCODE". В старой версии IAR мне понравилось тем, что распределение по сегментам определяется один раз, достаточно наверху с помошью прагмы указать сегмент и тогда все функции данного модуля окажутся в этом сегменте, а новой версии этого почему-то не нашел, приходится объявлять сегмент каждой функции, что не есть хорошо..
|
|
|
|
|
Oct 29 2007, 20:53
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(CAHTA_1939 @ Oct 30 2007, 01:36)  я тут в msp430x1xx_user_guide прочитал что флеш память можно программировать если программа находится в ОЗУ... Совсем не обязательно. Программа программирования Flash может работать прямо из самой Flash. Только естественно, не из того же сектора, который в данный момент программируется. В MSP430 реализован автомат (контроллер Flash-памяти), который на время стирания/записи Flash сам тактирует ядро и подменяет текущую команду, выбираемую из Flash, командой "пустого" перехода - JMP PC. Если вы читали User's Guide, то вопрос странный. Если не читали, то стоит почитать. http://www.gaw.ru/html.cgi/txt/doc/micros/msp430/arh/5.htmЦитата(CAHTA_1939 @ Oct 30 2007, 01:36)  а кук туда ее записать? сорри если туплю... я в этом новичек и учусь... Дык банальным копированием  - скопировали код в ОЗУ и передали ему управление. Только этот кусок кода должен быть перемещаемым, т.е. работать с относительными локальными метками переходов. Ну и не быть слишком большим конечно же, помещаться в имеющийся на кристалле объем ОЗУ.
|
|
|
|
|
Nov 1 2007, 13:39
|
Участник

Группа: Участник
Сообщений: 36
Регистрация: 4-03-05
Из: Киев
Пользователь №: 3 078

|
Цитата(asket @ Oct 23 2007, 17:48)  Это мне понятно, дело здесь в другом, допустим у нас несколько сегментов:
-Z(CODE)CODE=1100-1200 -Z(CODE)MYCODE=1200-1500
По умолчанию компилятор IAR помещает стандартные и служебные функции в сегмент CODE, то есть это означает, что если программист забыл указать сегмент, то компилятор автоматом поместит в сегмент --Z(CODE)CODE, то есть мне не хотелось сильно зависеть от данного сегмента, ибо большинство функции находятся в сегменте MYCODE, придется тогда над каждой функцией писать #pragma location="MYCODE". В старой версии IAR мне понравилось тем, что распределение по сегментам определяется один раз, достаточно наверху с помошью прагмы указать сегмент и тогда все функции данного модуля окажутся в этом сегменте, а новой версии этого почему-то не нашел, приходится объявлять сегмент каждой функции, что не есть хорошо.. для Си void func_name ( void ) @ "MYCODE" { _NOP(); } недочитал %), ответ не по вопросу ...
Сообщение отредактировал NoName - Nov 1 2007, 14:12
|
|
|
|
|
Oct 5 2011, 07:31
|

Знающий
   
Группа: Свой
Сообщений: 604
Регистрация: 5-05-06
Из: Нижегородская обл.
Пользователь №: 16 819

|
Здравствуйте. Собрался писать загрузчик. Т.к. в MSP430F2410 выводы своего загрузчика находятся на P1.1 и P2.2, которые заняты. Почитав форум узнал: - что лучше делать 2 проекта (один загрузчик, другой рабочий проект), получается 2 hex файла? Первый – загрузчик - прошивается через JTAG? А после, через интерфейс загрузчика? - что-то делается с таблицами векторов. Так называемый remapping. Но так и не понял как это делается. Смею предположить, что переписывается всего лишь xcl файл. С программированием флеш памяти (INFO) знаком. Написал функцию прошивальщика с общением по UART.(Прикрепил) Правда ещё не проверял в действии. Вопросы: с чего продолжить? Как оформить таблицу векторов? Как оформить функцию прошивальщик.
--------------------
Кризис - это не отсутствие денег, а отсутствие идей! Учитесь и никаких кризисов не будет.
|
|
|
|
|
Oct 5 2011, 17:26
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(KARLSON @ Oct 5 2011, 12:31)  - что лучше делать 2 проекта (один загрузчик, другой рабочий проект), получается 2 hex файла? Да. Цитата(KARLSON @ Oct 5 2011, 12:31)  Первый – загрузчик - прошивается через JTAG? А после, через интерфейс загрузчика? Не обязательно. Можно обе прошивки hex-файла объединить. Ведь пересекаться по расположению в памяти они не должны. Цитата(KARLSON @ Oct 5 2011, 12:31)  - что-то делается с таблицами векторов. Так называемый remapping. Но так и не понял как это делается. Смею предположить, что переписывается всего лишь xcl файл. Не совсем так. В xcl-файле проекта пользовательского исходника нужно объявить сегмент, который компилятор не должен использовать для размещения кода. В исходнике же бутлоадера - наоборот, компилятор должен использовать только этот выделенный сегмент. На месте векторов прерываний должны быть адреса переходов. А сами обработчики прерываний располагаются где-то в другом месте (в неперезаписываемом сегменте Flash или в ОЗУ). Хотя по-моему в бутлоадере использовать прерывания это моветон, да и незачем, если только у вас нет жесткого требования обновления firmware прямо во время работы программы без прерывания ее функционирования. Хотя в этом случае образец новой firmware предварительно еще где-то сохранить нужно, проверив его на валидность. Работать в бутлоадере с UART можно и без прерываний, по опросу флагов готовности.
|
|
|
|
|
Oct 10 2011, 06:57
|

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

|
QUOTE (KARLSON @ Oct 10 2011, 08:20)  А при включении контроллера запускаться должна рабочая программа? А если ее нет или она записалась не до конца? Как раз наоборот - при включении питания запускается загрузчик, проверяет целостность рабочей программы, и если она признана живой - запускает ее. Если нет - ожидает поступления по UART (или другому интерфейсу перепрошивки) команды перепрошивки. QUOTE (KARLSON @ Oct 10 2011, 08:20)  И по команде по UART вызываться функция бутлоадера, вернее переход на адрес во флешь? Это да. Если приложение запущено, то оно запускает загрузчик по команде. Но не переходом на его main(), а переходом на начало его стартап-кода, который штатно подготовит ОЗУ для запуска main() загрузчика.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Oct 10 2011, 11:34
|

Знающий
   
Группа: Свой
Сообщений: 604
Регистрация: 5-05-06
Из: Нижегородская обл.
Пользователь №: 16 819

|
И так. Создаю пример. Вопрос: правильно ли прописаны xcl файлы? Как вызвать в загрузчике приложение и наоборот))Msp430F2410 описание памяти: // Interrupt vectors: 32 // Peripheral units: 0-01FF // Information memory (FLASH): 1000-10FF // Read/write memory (RAM): 1100-20FF // Read-only memory (FLASH): 2100-FFFF Bootloader Код #include <io430.h> #include "Titl.h" #include "function.cpp" #include "UART0.cpp"
void main( void ) { WDTCTL = WDTPW + WDTHOLD; Init_DCO_16MHz(); Init_USCI_A0(); Init_FLASH(); P3OUT = 0; P3DIR = BIT3; if (Check_Flash()) {// проверка прошла удачно // переход в приложение } else {// неудачная проверка // ждём команду по UART перепрошивки _EINT(); while(1) { for(unsigned int i=0; i<65000; i++); for(unsigned int i=0; i<65000; i++); P3OUT ^= BIT3; } } } xcl файл Bootloader Код // ------------------------------------- // Code // -Z(CODE)CSTART,ISR_CODE,CODE_ID=EC00-FFBF -P(CODE)CODE=EC00-FFBF
// ------------------------------------- // Interrupt vectors // -Z(CODE)INTVEC=FFC0-FFFF -Z(CODE)RESET=FFFE-FFFF Приложение Код #include <io430.h> #include "Titl.h" #include "function.cpp" #include "UART0.cpp"
void main( void ) { WDTCTL = WDTPW + WDTHOLD; Init_DCO_16MHz(); Init_USCI_A0(); P3OUT = 0; P3DIR = BIT3; _EINT(); while(1) {// приложение for(unsigned int i=0; i<65000; i++); for(unsigned int i=0; i<65000; i++); P3OUT ^= BIT3; } }
#pragma vector = USCIAB0RX_VECTOR __interrupt void USCIA0_RX(void) {//1 if(UCA0STAT&UCADDR) {//2 Clovo_Data_RX0=0; //--------------------------------------------------- switch(UCA0RXBUF) { case 0: // команда для прошивке // переход на прошивку UCA0CTL1 &=~ UCDORM; //Переключиться на прием неадресных символов break; default: break; } }//2 }// 1 xcl файл Приложения Код // ------------------------------------- // Code //
-Z(CODE)CSTART,ISR_CODE,CODE_ID=2200-EBFF -P(CODE)CODE=2200-EBFF
// ------------------------------------- // Interrupt vectors //
-Z(CODE)INTVEC=21C0-21FF -Z(CODE)RESET=21FE-21FF
--------------------
Кризис - это не отсутствие денег, а отсутствие идей! Учитесь и никаких кризисов не будет.
|
|
|
|
|
Oct 10 2011, 19:44
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(KARLSON @ Oct 10 2011, 16:34)  И так. Создаю пример. Bootloader на плюсах?  Весьма оригинально! Цитата(KARLSON @ Oct 10 2011, 16:34)  Вопрос: правильно ли прописаны xcl файлы? Как вызвать в загрузчике приложение и наоборот)) main для приложения должена компилироваться как функция по заранее определенному адресу. Тогда не составляет сложности вызвать эту функцию по указателю. О том, как разместить функцию по определенному адресу, написано в разделах Placing code and data и Pragma directives документа EW430_CompilerReference.pdf. Обязательно следует учитывать, что еще до вызова функции main в скомпилированной программе сначала выполняется установка/инициализация указателя стека, затем вызывается функция low_level_init, а потом выполняется функция очистки и начальной инициализации глобальных и статических переменных (?cstart_init_zero). Так что если вы будете вызывать пользовательскую программу по адресу main, то вышеописанные функции вам придется реализовывать "вручную". Ну либо ищите как можно узнать или зарезервировать адрес метки __program_start (?cstart_begin в случае С), которая располагается по адресу перехода от вектора прерывания RESET и с которой начинается выполнение программы. Обычно эта метка совпадает с началом сегмента CODE. Все это справедливо лишь для "чистого" Си. C++ для программирования MSP430 я ни разу не использовал.
|
|
|
|
|
Oct 11 2011, 06:12
|

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

|
QUOTE (rezident @ Oct 10 2011, 22:44)  Bootloader на плюсах?  Весьма оригинально! Ой, да бросьте. Гораздо удобнее чем на обычных Сях. Накладных расходов нет, если пользоваться головой. QUOTE (rezident @ Oct 10 2011, 22:44)  main для приложения должена компилироваться как функция по заранее определенному адресу. Тогда не составляет сложности вызвать эту функцию по указателю. Зачем? Достаточно перейти по тому адресу, который находится в векторе Reset приложения. И все необходимые действия (cstartup) будут выполнены, и адрес этого вектора известен. KARLSON: Включать .cpp в другие файлы - путь в корне неправильный. Вам необходимо почитать про заголовочные (header) файлы и раздельную компиляцию. CODE -Z(CODE)INTVEC=FFC0-FFFF -Z(CODE)RESET=FFFE-FFFF Бить область векторов на две (выделать вектор сброса в отдельную область) не обязательно. Мне кажется вторая строка лишняя - вектор сброса компилятор тоже кладет в INTVEC. Во втором файле то же самое. Но идею вы ухватили верно. И подумайте, как быть с прерываниями - у приложения будут свои обработчики и их надо вызывать, а "настоящие" вектора находятся в области загрузчика. Я на GCC делал так: CODE __attribute__ ((section(".app_vectors"))) struct { flash_t::address_t App_image_size; void *Vectors[INT_VECTORS_COUNT - 1]; void (*ResetVector)(); } Application;
#define TRAMP(name, vector) \ extern "C" __attribute__((__naked__)) void Tramp_##name() \ { \ asm volatile("BR %0\n\t" ::"m"(InterruptVectors[vector])); \ }
TRAMP(DAC12, 0); TRAMP(DMA, 1); TRAMP(USCIAB1TX_VECTOR, 2); TRAMP(USCIAB1RX_VECTOR, 3); ..... typedef void (*vector_t)();
__attribute__ ((section(".vectors"))) extern vector_t const VectorTable[INT_VECTORS_COUNT] = { Tramp_DAC12, Tramp_DMA, Tramp_USCIAB1TX_VECTOR, Tramp_USCIAB1RX_VECTOR, Вы можете сделать что-то подобное в отдельном асм-файле. Если загрузчику тоже нужны обработчики прерываний, то надо копировать вектора нужной части программы в ОЗУ и адрес для этого перехода брать из ОЗУ: CODE if (!crc) // Application Section crc ok { DRIVER(MANUAL_BOOT, OUTPUT); // set MANUAL_PROG as output uint_fast8_t i = INT_VECTORS_COUNT - 1; // do not copy reset vector while(i--) { InterruptVectors[i] = Application.Vectors[i]; } Application.ResetVector(); } Не забудьте запретить прерывания перед переходом из приложения в загрузчик.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Oct 11 2011, 17:32
|

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

|
QUOTE (rezident @ Oct 11 2011, 18:08)  В векторе сброса располагается адрес старта бутлоадера, а не пользовательского приложения. Или я что-то не понимаю?  В векторе Reset приложения. Приложение ведь имеет свой полный комплект векторов, в том числе и вектор Reset c адресом перехода как раз в нужную точку старта. У меня так
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Oct 11 2011, 22:32
|

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

|
QUOTE (rezident @ Oct 11 2011, 20:59)  Дык ведь тогда вектора RESET бутлоадера и приложения будут наложены друг на друга. Ну почему же? Посмотрите сообщение №24 - две программы, две таблицы векторов по разным адресам. А... кажется понял. У нас несколько разная трактовка понятия "вектор". Я под вектором подразумеваю содержимое, т.е. сам адрес "куда переходить", а не саму ячейку, т.е. "адрес, из которого процессор берет адрес перехода". Поэтому и таблица "моих" векторов у приложения может быть своя, совершенно независимая. Да, адреса из таблицы приложения берутся не аппаратно, а посредством функций-помошников ("трамплинов"), указатели на которые сидят на "железных", "ваших"  векторах. Во всяком случае такой подход позволяет ценой одного дополнительного косвенного перехода и двух байт ОЗУ на каждый вектор иметь совершенно независимые обработчики у приложения и загрузчика. Причем в целях экономии ОЗУ таким образом можно охватить не всю таблицу, а лишь физически реализованные в процессоре прерывания.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Oct 12 2011, 06:14
|

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

|
QUOTE (rezident @ Oct 12 2011, 02:16)  Нет, Сергей, как сделать "перемещаемые" вектора прерываний я лично понимаю. Для пользовательского приложения "аппаратные" вектора прерываний как таковые вообще не используются. Пишутся лишь функции-обработчики прерываний, адреса которых "раскладываются" в таблицу переходов, расположенную в ОЗУ. Верно. И раскладывает их туда загрузчик перед тем, как запустить приложение. И берет он их из таблицы, которая расположена в фиксированном месте приложения. И таблицей этой является содержимое сегмента INTVEC приложения. QUOTE (rezident @ Oct 12 2011, 02:16)  У ТС же возник вопрос по совмещению двух проектов/прошивок в одном кристалле и вызове одного из другого. Как я понял, ты предлагаешь замещать вектор сброса тем, который генерируется при компиляции именно бутлоадера. А то содержимое вектора RESET, которое компилируется в пользовательском приложении из прошивки выкинуть, использовав его лишь как адрес для старта приложения. Так? Зачем же выбрасывать и замещать? Пусть он лежит в таблице векторов приложения. Тогда загрузчик точно будет знать, откуда его брать. Вот смотрите, я в сообщении №26 давал пример. Вот структура таблицы векторов приложения, т.е. полное содержимое сегмента INTVEC приложения: CODE __attribute__ ((section(".app_vectors"))) struct { void *Vectors[INT_VECTORS_COUNT - 1]; void (*ResetVector)(); } Application; Тут и все вектора прерываний и вектор сброса. Вот загрузчик копирует эту таблицу в ОЗУ, в то место, откуда берут адреса функции-трамплины: CODE uint_fast8_t i = INT_VECTORS_COUNT - 1; // do not copy reset vector while(i--) { InterruptVectors[i] = Application.Vectors[i]; } А вот из этой же таблицы берется адрес, на который надо перейти для старта приложения: CODE Application.ResetVector(); А "железный" вектор RESET указывает на старт загрузчика. Тогда и приложение знает, как запустить загрузчик - адрес его точки входа всегда лежит в ячейках по адресу 0xFFFE.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Oct 12 2011, 13:07
|

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

|
QUOTE (KARLSON @ Oct 12 2011, 15:42)  А что делает запись ((void(*)() )0x2200)();? Вызов функции вида void func(void), расположенной по адресу 2200. QUOTE (KARLSON @ Oct 12 2011, 15:42)  Если после программировании в бутлоадере с помощью этой записи обратиться к инструкции по адресу 0x2200 ( от куда и начинается приложение) старт приложения с инициализацией произойдёт? Если по адресу 0x2200 расположена первая инструкция стартапа. Если же там располагается указатель на адрес точки входа (как было бы, если бы там располагался) вектор сброса, то запись должна была бы быть несколько иной: ((void(**)() )0x2200)().
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Oct 13 2011, 06:35
|

Знающий
   
Группа: Свой
Сообщений: 604
Регистрация: 5-05-06
Из: Нижегородская обл.
Пользователь №: 16 819

|
Захотел я сделать прошивку с переходами между приложениями пока без использовании прерываний: приложение 1 Код #include "io430.h"
void main( void ) { // Stop watchdog timer to prevent time out reset WDTCTL = WDTPW + WDTHOLD; P1DIR = BIT0; P1OUT = BIT0; char j=0; unsigned int k = 40000, z=2; while(1) { for(unsigned int i=0; i<k; i++); for(unsigned int i=0; i<z; i++); P1OUT ^= BIT0; ++j; if(j == 30) { ((void(*)() )0x2200)(); } } } xcl файл приложения 1 Код // ----------------------------------------------- // Read/write memory //
-Z(DATA)DATA16_I,DATA16_Z,DATA16_N,DATA16_HEAP+_DATA16_HEAP_SIZE=1100-20FF -Z(DATA)CODE_I -Z(DATA)CSTACK+_STACK_SIZE#
// ------------------------------------- // Constant data //
-Z(CONST)DATA16_C,DATA16_ID,DIFUNCT,CHECKSUM=2100-FFBF
// ------------------------------------- // Code // -Z(CODE)CSTART,ISR_CODE,CODE_ID=EC00-FFBF -P(CODE)CODE=EC00-FFBF
// ------------------------------------- // Interrupt vectors //
-Z(CODE)INTVEC=FFC0-FFFF -Z(CODE)RESET=FFFE-FFFF прошивка приложения 1 Код @EC00 31 40 00 21 B0 12 0C EC B0 12 4E EC 0A 12 0B 12 08 12 B2 40 80 5A 20 01 D2 43 22 00 D2 43 21 00 4A 43 3B 40 40 9C 28 43 0B 3C 1F 53 0F 98 FD 2B D2 E3 21 00 5A 53 7A 90 1E 00 02 20 B0 12 00 22 0F 43 01 3C 1F 53 0F 9B FD 2B 0F 43 EF 3F 30 40 52 EC 30 40 56 EC FF 3F @FFFE 00 EC приложение 2 Код #include "io430.h"
void main( void ) { // Stop watchdog timer to prevent time out reset WDTCTL = WDTPW + WDTHOLD; P1DIR = BIT0; P1OUT = BIT0; char j=0; unsigned int k = 65535, z=65535; while(1) { for(unsigned int i=0; i<k; i++); for(unsigned int i=0; i<z; i++); P1OUT ^= BIT0; ++j; if(j == 30) { ((void(*)() )0xEC00)(); } } } xcl файл приложения 2 Код // ----------------------------------------------- // Read/write memory //
-Z(DATA)DATA16_I,DATA16_Z,DATA16_N,DATA16_HEAP+_DATA16_HEAP_SIZE=1100-20FF -Z(DATA)CODE_I -Z(DATA)CSTACK+_STACK_SIZE#
// ------------------------------------- // Constant data //
-Z(CONST)DATA16_C,DATA16_ID,DIFUNCT,CHECKSUM=2200-9FBF
// ------------------------------------- // Code // -Z(CODE)CSTART,ISR_CODE,CODE_ID=2200-9FBF -P(CODE)CODE=2200-9FBF
// ------------------------------------- // Interrupt vectors // -Z(CODE)INTVEC=9FC0-9FFF -Z(CODE)RESET=9FFE-9FFF прошивка приложения 2 Код @2200 31 40 00 21 B0 12 0C 22 B0 12 4C 22 0A 12 0B 12 08 12 B2 40 80 5A 20 01 D2 43 22 00 D2 43 21 00 4A 43 3B 43 38 43 0B 3C 1F 53 0F 98 FD 2B D2 E3 21 00 5A 53 7A 90 1E 00 02 20 B0 12 00 EC 0F 43 01 3C 1F 53 0F 9B FD 2B 0F 43 EF 3F 30 40 50 22 30 40 54 22 FF 3F @9FFE 00 22 q общая прошивка Код @2200 31 40 00 21 B0 12 0C 22 B0 12 4C 22 0A 12 0B 12 08 12 B2 40 80 5A 20 01 D2 43 22 00 D2 43 21 00 4A 43 3B 43 38 43 0B 3C 1F 53 0F 98 FD 2B D2 E3 21 00 5A 53 7A 90 1E 00 02 20 B0 12 00 EC 0F 43 01 3C 1F 53 0F 9B FD 2B 0F 43 EF 3F 30 40 50 22 30 40 54 22 FF 3F @9FFE 00 22 @EC00 31 40 00 21 B0 12 0C EC B0 12 4E EC 0A 12 0B 12 08 12 B2 40 80 5A 20 01 D2 43 22 00 D2 43 21 00 4A 43 3B 40 40 9C 28 43 0B 3C 1F 53 0F 98 FD 2B D2 E3 21 00 5A 53 7A 90 1E 00 02 20 B0 12 00 22 0F 43 01 3C 1F 53 0F 9B FD 2B 0F 43 EF 3F 30 40 52 EC 30 40 56 EC FF 3F @FFFE 00 EC q Прошил. Вроде работает. Первая прошивка часто моргает (~3Гц), затем переходит на вторую прошивку, моргает медленно (~1.5 Гц). И так бесконечно. Я всё правильно сделал? Есть ли минусы? Кстати, Сергей, пробовал делать переход на адрес вектора ((void(**)() )0x9FFE)(); в одном и ((void(**)() )0xFFFE)(); в другом приложении. Компилятор ругнулся на эти записи "Error[Pe109]: expression must have (pointer-to-) function type "
Сообщение отредактировал KARLSON - Oct 13 2011, 10:11
--------------------
Кризис - это не отсутствие денег, а отсутствие идей! Учитесь и никаких кризисов не будет.
|
|
|
|
|
Oct 21 2011, 04:49
|
Участник

Группа: Свой
Сообщений: 63
Регистрация: 16-06-04
Из: Россия, Уфа
Пользователь №: 31

|
Мои пять копеек. Вариант, если в устройстве есть внешняя память (в моем случае AT45DB или FRAM33). Посредством основной программы по существующему протоколу связи заливаем образ прошивки во внешнюю память (с проверкой версии прошивки, аппаратуры, контрольных сумм и пр.). Затем передаем управление ассемблерной процедуре, которая копирует себя в ОЗУ и затем оттуда быстро и тупо копирует образ из внешней памяти во FLASH. Исходник для msp430f2x и at45db: CODE #include <msp430.h> #include "hardware_config.h" #include "fwupdate.h"
// Forward declarations of segments. RSEG CSTACK:DATA:NOROOT RSEG DATA16_I:DATA:NOROOT MODULE FW_UPDATE_ROUTINES PUBLIC fw_at45_update
WD_EXT_STB MACRO xor.b #(1<<PIN_WD_STROBE), &WD_EXT_PORT ENDM
AT45_ENABLE MACRO bic.b #(1<<PIN_AT45_CS), &AT45_CS_PORT ENDM
AT45_DISABLE MACRO bis.b #(1<<PIN_AT45_CS), &AT45_CS_PORT ENDM
SPI_WRITE MACRO N mov.b N, r12 call r8 ENDM
RSEG CODE fw_at45_update dint nop mov.w #(WDTPW|WDTHOLD), &WDTCTL ftg_w_ready bit #BUSY, &FCTL3 jnz ftg_w_ready
mov #SFE(CSTACK), sp
// copy to RAM mov.w #FW_INIT, r12 mov.w #SFE(DATA16_I), r14 push.w r14 copy_2_ram: mov.w @r12+, 0(r14) incd.w r14 cmp.w #FW_UPDATE_END, r12 jnc copy_2_ram ret
FW_INIT WD_EXT_STB
// Init DCO mov.b &CALBC1_12MHZ,&BCSCTL1 // Set DCO to 12MHz clr.b &BCSCTL2 // MCLK = SMCLK = DCOCLK mov.b &CALDCO_12MHZ,&DCOCTL
mov.w #(FWKEY|FSSEL_1|FN5), &FCTL2 // Flash clock = MCLK/35 ~ 360kHz
// configure SPI module mov.b #(UCSSEL_2|UCSWRST), &UCB1CTL1 // SMCLK mov.b #(UCMST|UCSYNC|UCMSB|UCCKPL), &UCB1CTL0 // 3-pin, 8-bit SPI master mov.b #1, &UCB1BR0 mov.b #0, &UCB1BR1 bic.b #(1<<2), &P5DIR bis.b #((1<<1)|(1<<3)), &P5DIR bis.b #((1<<1)|(1<<2)|(1<<3)), &P5SEL // P5.1,2,3 option select bic.b #UCSWRST, &UCB1CTL1 // Initialize USCI state machine
// configure AT45DB AT45_DISABLE mov #(SFE(DATA16_I) + (spi_wr-FW_INIT)), R8 // R8 = &spi_write
// wait for ready at45db wait_at45db AT45_ENABLE SPI_WRITE #0x57 SPI_WRITE #0x00 AT45_DISABLE bit.b #(1<<7), r12 jnc wait_at45db
// erase main memory mov.w #3, r12 // erase cycles count meras_loop mov.w #(FWKEY | MERAS), &FCTL1 // Set Mass Erase bit mov.w #(FWKEY), &FCTL3 // Clear Lock bit mov.w #0xFFFF, &fw_at45_update // write stuff byte to flash meras_wait bit #BUSY, &FCTL3 jnz meras_wait dec.w r12 jnz meras_loop
WD_EXT_STB
// at45db start read AT45_ENABLE SPI_WRITE #0xE8 // Continues Array Read(Legacy Command) SPI_WRITE #0x00 // upper part of page address SPI_WRITE #0x04 // lower part of page address and MSB of int.page adr. mov.b #0, R12 call r8 // LSB byte of internal page address call r8 // perform 4 dummy writes call r8 // in order to initiate DataFlash call r8 // address pointers call r8 // --
mov.w #FW_FIRST_ADDR, r14 mov.w #FWKEY,&FCTL3 // Clear LOCK
prg_blocks WD_EXT_STB mov.w #(FWKEY|BLKWRT|WRT),&FCTL1 // Enable block write prg_bytes SPI_WRITE #0x00 // Read byte from dataflash mov.b r12, 0(r14) // Write location prg_wait bit #WAIT, &FCTL3 // Test WAIT jz prg_wait // Loop while WAIT=0 inc.w r14 // Point to next byte jz prg_finish bit.b #0x3F, r14 // end of block (addr % 0x40) == 0 jne prg_bytes mov.w #(FWKEY|WRT), &FCTL1 // Set BLKWRT=0 prg_busy bit #BUSY, &FCTL3 jnz prg_busy jmp prg_blocks prg_finish mov.w #(FWKEY|WRT), &FCTL1 // Set BLKWRT=0 prg_finb bit #BUSY, &FCTL3 jnz prg_finb mov.w #FWKEY, &FCTL1 // Clear WRT bit mov.w #(FWKEY|LOCK), &FCTL3 // Set Lock Bit AT45_DISABLE
mov.w #0, &WDTCTL // RESET!
spi_wr bit.b #UCB1TXIFG, &UC1IFG jnc spi_wr mov.b r12, &UCB1TXBUF ?w_txend: bit.b #UCB1RXIFG, &UC1IFG jnc ?w_txend mov.b &UCB1RXBUF, r12 ret FW_UPDATE_END
ENDMOD END
|
|
|
|
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0
|
|
|