Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Bootloader для MSP430
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > MSP430
asket
Я в этом деле новичок, недавно начал заниматься этой деятельностью, мне поручили написать Bootloader. Задача: имеется шина, будь это COM-порт или собственная шина, не важно какая, нужно по данной шине закачать с компьютера прошивку и залить его во флеш в целях ее обновления, вопрос: как это сделать это по умному? Кто и что посоветует? Может примерчик какой-нить.. К сожалению в отличии от других процев у MSP430 встроенных бутлоадеров я так понимаю нету. Компилятор IAR 3.42.

Заранее благодарен.
VAI
Цитата
К сожалению в отличии от других процев у MSP430 встроенных бутлоадеров я так понимаю нету.

Плохо понимаете. Есть Bootstrap Loader.
Последний абзац п.п.21 FAQ http://www.gaw.ru/html.cgi/txt/doc/micros/msp430/faq.htm#21

http://focus.ti.com/mcu/docs/mcusupporttec...ctName=slaa089d
http://focus.ti.com/mcu/docs/mcusupporttec...ctName=slaa096d

На русском, немного устарело.. http://www.gaw.ru/html.cgi/txt/app/micros/msp430/slaa089.htm
asket
Цитата(VAI @ Oct 15 2007, 14:36) *


Это то понятно, там целое шаманство производить надо и со схемкой, и с программкой, чтоб программить с командной строки, а у меня задача другая стоит: на объекте используется аппарат, который висит на собственной шине, чтоб не ездить за обновлением прошивки, пользователь скачивает из интернета hex, и с помошью нашей же программы закачивает удаленно прошивку по адресу этого устройства, далее я понимаю так, что эту прошивку надо собирать в отдельном файле, проверить контрольную сумму и запустить программный бутлоадер, чтоб он эту прошивку залил, но возможна ситуация, когда на шине пропадает питание, здесь на помошь приходит опять таки бутолоадер, который проверяет CRC прошивки и выдает код ошибки пользователю, то етсь таким образом устройство продолжает работу дальше.. Проблема здесь в том, что как это сделать правильно, у меня попытка очистить флеш по адресу 0x1100 и залить туда код с оперативки (традиционным способом) не увенчалось успехом, после перезагрузки проц вообще перестал работать, приходится перепрошивать чеез JTAG.. Прошивку из шины я пытаюсь сохранять во внешней EEPROM, ибо 64 Кб - маловато для этих целей.. Забыл про проц MSP430f149.

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


Благодарю за совет!) Буду разбираться, правда, время поджимает, судя по ответам похоже этим либо занимались либо редко, либо никто.. А вот по поводу перемешаемых векторов я как то не подумал, спасибо, что обратили внимание на этот факт...)
VAI
Нашел на http://tech.groups.yahoo.com/group/msp430/
Коментарий: "Code for an ISR jump table in RAM"
может поможет.
asket
Цитата(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
rezident
Цитата(asket @ Oct 15 2007, 19:56) *
И еще вопрос: где бы найти описание формата пакета с прошивками например Intel Standart или Intel Extended? Там конечно разбираться то нечего, только вот хотелось бы понять по какому алгоритму считают CRC и что за байт кроется между адресом и данными?

Дык как обычно Гугель рулит wink.gif
http://pages.interlog.com/~speff/usefulinfo/Hexfrmt.pdf
asket
Цитата(rezident @ Oct 15 2007, 22:40) *
Дык как обычно Гугель рулит wink.gif
http://pages.interlog.com/~speff/usefulinfo/Hexfrmt.pdf


Окей, спасибо огромное! Кое что стало проясняться.. Еще один вопрос, извиняюсь, чисто ламерский: каким способом программно определить размер той или иной функции? Допустим знаем адрес раположения этой функции, и нужно будет определить размер этой функции, пробывал использовать sizeof(), ругается что операнд не может быть функцией, каким способом можно определить?
asket
Неужели останусь без ответа и привета?wink.gif
SSerge
Узнать размер именно функции нельзя.
Но можно указать компилятору разместить интересующую функцию в отдельном сегменте
#pragma location="MYSEGMENT"

и потом вычислить размер сегмента.

У IAR для этого есть встроенные псевдо-функции
__segment_begin(segment) и __segment_end(segment)

Кроме того потребуется объяснить линкеру в .xcl-файле что у него появился ещё один сегмент кода
-Z(CODE)MYSEGMENT=....

Читайте EW430_CompilerReference.pdf
msalov
Цитата(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 -- результат
asket
Еще такой вопрос по поводу сегментации флеш-памяти, компилер IAR по умолчанию ставит сегмент CODE и размещает туда все функции, если они явно пользователем не указаны в каком сегменте их расположить, но мне не понравилось тот факт, что IAR без ведома пихает стандартные функции, и если его стереть, то фактически программа без них не будет нормально работать. Как указать компилятору в какой сегмент нужно раположить эти функции?
msalov
Цитата(asket @ Oct 23 2007, 16:27) *
Еще такой вопрос по поводу сегментации флеш-памяти, компилер IAR по умолчанию ставит сегмент CODE и размещает туда все функции, если они явно пользователем не указаны в каком сегменте их расположить, но мне не понравилось тот факт, что IAR без ведома пихает стандартные функции, и если его стереть, то фактически программа без них не будет нормально работать. Как указать компилятору в какой сегмент нужно раположить эти функции?


Можно изменить сегмент CODE (начальный и конечный адреса), таким образом, что бы он отвечал Вашим требованиям. Это можно проделать при помощи параметра линкера:
Код
-Z(CODE)MYCODE=4000-5000
asket
Цитата(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 мне понравилось тем, что распределение по сегментам определяется один раз, достаточно наверху с помошью прагмы указать сегмент и тогда все функции данного модуля окажутся в этом сегменте, а новой версии этого почему-то не нашел, приходится объявлять сегмент каждой функции, что не есть хорошо..
CAHTA_1939
я тут в msp430x1xx_user_guide прочитал что флеш память можно программировать если программа находится в ОЗУ...
а кук туда ее записать?
сорри если туплю... я в этом новичек и учусь...
rezident
Цитата(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) *
а кук туда ее записать?
сорри если туплю... я в этом новичек и учусь...

Дык банальным копированием smile.gif - скопировали код в ОЗУ и передали ему управление. Только этот кусок кода должен быть перемещаемым, т.е. работать с относительными локальными метками переходов. Ну и не быть слишком большим конечно же, помещаться в имеющийся на кристалле объем ОЗУ.
АДИКМ
Если Вам надо быстрое блочное программирование, тогда программа должна выполняться только из рам.
Если достаточно байтового/пословного программирования, тогда можно выполнять из флеш.
NoName
Цитата(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();
}

недочитал %), ответ не по вопросу ...
KARLSON
Здравствуйте. Собрался писать загрузчик. Т.к. в MSP430F2410 выводы своего загрузчика находятся на P1.1 и P2.2, которые заняты.
Почитав форум узнал:
- что лучше делать 2 проекта (один загрузчик, другой рабочий проект), получается 2 hex файла? Первый – загрузчик - прошивается через JTAG? А после, через интерфейс загрузчика?
- что-то делается с таблицами векторов. Так называемый remapping. Но так и не понял как это делается. Смею предположить, что переписывается всего лишь xcl файл.
С программированием флеш памяти (INFO) знаком.
Написал функцию прошивальщика с общением по UART.(Прикрепил) Правда ещё не проверял в действии.
Вопросы: с чего продолжить? Как оформить таблицу векторов? Как оформить функцию прошивальщик.
rezident
Цитата(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 можно и без прерываний, по опросу флагов готовности.
KARLSON
При двух проектах, ведь будет две функции main();
А при включении контроллера запускаться должна рабочая программа? И по команде по UART вызываться функция бутлоадера, вернее переход на адрес во флешь?
Сергей Борщ
QUOTE (KARLSON @ Oct 10 2011, 08:20) *
А при включении контроллера запускаться должна рабочая программа?
А если ее нет или она записалась не до конца?
Как раз наоборот - при включении питания запускается загрузчик, проверяет целостность рабочей программы, и если она признана живой - запускает ее. Если нет - ожидает поступления по UART (или другому интерфейсу перепрошивки) команды перепрошивки.

QUOTE (KARLSON @ Oct 10 2011, 08:20) *
И по команде по UART вызываться функция бутлоадера, вернее переход на адрес во флешь?
Это да. Если приложение запущено, то оно запускает загрузчик по команде. Но не переходом на его main(), а переходом на начало его стартап-кода, который штатно подготовит ОЗУ для запуска main() загрузчика.
KARLSON
И так. Создаю пример.
Вопрос: правильно ли прописаны 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



rezident
Цитата(KARLSON @ Oct 10 2011, 16:34) *
И так. Создаю пример.
Bootloader на плюсах? wacko.gif Весьма оригинально!
Цитата(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 я ни разу не использовал.
Сергей Борщ
QUOTE (rezident @ Oct 10 2011, 22:44) *
Bootloader на плюсах? wacko.gif Весьма оригинально!
Ой, да бросьте. Гораздо удобнее чем на обычных Сях. Накладных расходов нет, если пользоваться головой.
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();
    }


Не забудьте запретить прерывания перед переходом из приложения в загрузчик.
rezident
Цитата(Сергей Борщ @ Oct 11 2011, 11:12) *
Зачем? Достаточно перейти по тому адресу, который находится в векторе Reset приложения. И все необходимые действия (cstartup) будут выполнены, и адрес этого вектора известен.
В векторе сброса располагается адрес старта бутлоадера, а не пользовательского приложения. Или я что-то не понимаю? laughing.gif
Сергей Борщ
QUOTE (rezident @ Oct 11 2011, 18:08) *
В векторе сброса располагается адрес старта бутлоадера, а не пользовательского приложения. Или я что-то не понимаю? laughing.gif
В векторе Reset приложения. Приложение ведь имеет свой полный комплект векторов, в том числе и вектор Reset c адресом перехода как раз в нужную точку старта.
У меня так laughing.gif
rezident
Цитата(Сергей Борщ @ Oct 11 2011, 22:32) *
В векторе Reset приложения. Приложение ведь имеет свой полный комплект векторов, в том числе и вектор Reset c адресом перехода как раз в нужную точку старта.
Дык ведь тогда вектора RESET бутлоадера и приложения будут наложены друг на друга.
Сергей Борщ
QUOTE (rezident @ Oct 11 2011, 20:59) *
Дык ведь тогда вектора RESET бутлоадера и приложения будут наложены друг на друга.
Ну почему же? Посмотрите сообщение №24 - две программы, две таблицы векторов по разным адресам.
А... кажется понял. У нас несколько разная трактовка понятия "вектор". Я под вектором подразумеваю содержимое, т.е. сам адрес "куда переходить", а не саму ячейку, т.е. "адрес, из которого процессор берет адрес перехода". Поэтому и таблица "моих" векторов у приложения может быть своя, совершенно независимая. Да, адреса из таблицы приложения берутся не аппаратно, а посредством функций-помошников ("трамплинов"), указатели на которые сидят на "железных", "ваших" wink.gif векторах. Во всяком случае такой подход позволяет ценой одного дополнительного косвенного перехода и двух байт ОЗУ на каждый вектор иметь совершенно независимые обработчики у приложения и загрузчика. Причем в целях экономии ОЗУ таким образом можно охватить не всю таблицу, а лишь физически реализованные в процессоре прерывания.
rezident
Нет, Сергей, как сделать "перемещаемые" вектора прерываний я лично понимаю. Для пользовательского приложения "аппаратные" вектора прерываний как таковые вообще не используются. Пишутся лишь функции-обработчики прерываний, адреса которых "раскладываются" в таблицу переходов, расположенную в ОЗУ.
У ТС же возник вопрос по совмещению двух проектов/прошивок в одном кристалле и вызове одного из другого. Как я понял, ты предлагаешь замещать вектор сброса тем, который генерируется при компиляции именно бутлоадера. А то содержимое вектора RESET, которое компилируется в пользовательском приложении из прошивки выкинуть, использовав его лишь как адрес для старта приложения. Так?
Сергей Борщ
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.
KARLSON
А что делает запись ((void(*)() )0x2200)();?
Если после программировании в бутлоадере с помощью этой записи обратиться к инструкции по адресу 0x2200 ( от куда и начинается приложение) старт приложения с инициализацией произойдёт?
Сергей Борщ
QUOTE (KARLSON @ Oct 12 2011, 15:42) *
А что делает запись ((void(*)() )0x2200)();?
Вызов функции вида void func(void), расположенной по адресу 2200.
QUOTE (KARLSON @ Oct 12 2011, 15:42) *
Если после программировании в бутлоадере с помощью этой записи обратиться к инструкции по адресу 0x2200 ( от куда и начинается приложение) старт приложения с инициализацией произойдёт?
Если по адресу 0x2200 расположена первая инструкция стартапа. Если же там располагается указатель на адрес точки входа (как было бы, если бы там располагался) вектор сброса, то запись должна была бы быть несколько иной: ((void(**)() )0x2200)().
KARLSON
Захотел я сделать прошивку с переходами между приложениями пока без использовании прерываний:
приложение 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 "
Сергей Борщ
QUOTE (KARLSON @ Oct 13 2011, 09:35) *
Кстати, Сергей, пробовал делать переход на адрес вектора ((void(**)() )0x9FFE)(); в одном и ((void(**)() )0xFFFE)(); в другом приложении. Компилятор ругнулся на эти записи "Error[Pe109]: expression must have (pointer-to-) function type "
Да, это я лопухнулся. (*((void(**)() )0xFFFE))();
Kurt
Мои пять копеек.
Вариант, если в устройстве есть внешняя память (в моем случае 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
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.