23 мая 2013 г.

Альтернативная таблица векторов прерываний в ОЗУ для MSP430

В семействах MSP430F5x и MSP430F6x есть замечательная возможность перенести таблицу векторов прерываний в ОЗУ. Что даёт возможность по ходу выполнения программы менять обработчики прерываний.

Возможность это всегда хорошо. Но вот как это сделать максимально удобно и прозрачно? Рассмотрим на примере компилятора от IAR.


Решение в лоб: создать массив в ОЗУ по фиксированному адресу (не в любом месте может быть новая таблица, а только в строго определённом - Top of RAM, как утверждает руководство) и заполнять его ручками:
__interrupt void YYY_handler (void)
{ /* ... */ }

__root __no_init uint16_t int_table[] @ TOP_OF_RAM;

int_vector_table[yyy] = YYY_handler;
Это замечательно, но хотелось бы сделать максимально "похоже" на обычную таблицу:
#pragma vector=YYY
__interrupt void YYY_handler (void)
{
}
Таблица векторов, заданных через #pragma vector, размещается в секции INTVEC, которая располагается по адресам FF80-FFFF. Нам надо создать новую секцию такого же размера, но только в ОЗУ (я назвал новую секцию INTRAMVEC). Для это подправим linker-script, а именно ту часть, которая описывает распределение ОЗУ*:
Было:
// -----------------------------------------------
// Read/write memory
//

-Z(DATA)DATA16_I,DATA16_Z,DATA16_N,TLS16_I,DATA16_HEAP+_DATA16_HEAP_SIZE=2400-63FF
-Z(DATA)CODE_I
-Z(DATA)DATA20_I,DATA20_Z,DATA20_N,DATA20_HEAP+_DATA20_HEAP_SIZE
-Z(DATA)CSTACK+_STACK_SIZE#
Стало:
// -----------------------------------------------
// Read/write memory
//

-Z(DATA)DATA16_I,DATA16_Z,DATA16_N,TLS16_I,DATA16_HEAP+_DATA16_HEAP_SIZE=2400-637F
-Z(DATA)CODE_I
-Z(DATA)DATA20_I,DATA20_Z,DATA20_N,DATA20_HEAP+_DATA20_HEAP_SIZE
-Z(DATA)CSTACK+_STACK_SIZE#

-Z(DATA)INTRAMVEC=6380-63FF
* в данном примере адреса указаны для MSP430F6638. Для других МК адреса могут отличаться, но общий принцип тот же: секция INTRAMVEC располагается по самым старшим адресам ОЗУ и равна по размеру INTVEC.

Место выделили, осталось его заполнить (скопировать INTVEC в INTRAMVEC):
#pragma segment="INTVEC"
#pragma segment="INTRAMVEC"

int __low_level_init (void)
{
    WDTCTL = WDTPW | WDTHOLD;

    /* Copy interrupt vector table content
       from INTVEC to INTRAMVEC */
    void * intramvec_begin = __segment_begin("INTRAMVEC");
    void * intvec_begin    = __segment_begin("INTVEC");
    size_t intvec_size     = __segment_size("INTVEC");
    memcpy(intramvec_begin, intvec_begin, intvec_size);

    /* Switch to interrupt vector table in RAM */
    SYSCTL |= SYSRIVECT;

    return 1;
}
Одно из возможных применений: можно хранить в МК несколько версий прошивки и запускать именно ту, что нужна. Это возможно т.к. каждая версия прошивки будет копировать свою таблицу векторов прерываний.

Комментариев нет:

Отправить комментарий