воскресенье, 17 мая 2009 г.

Разбираясь с примерами

На каком материале учиться программисту, как не на коде более опытных товарищей?
У вас уже есть список источников такого кода? Тогда исключите из него TI.

Продолжаю ковырять MSP-EXP430F5438.
Чтобы быстрее разобраться, кроме чтения доки смотрю на многочисленные "Application Note" от TI.
Единственный (из найденного) пример программирования моей платы - slac227, остальные в большинстве своем не рассчитаны на архитектуру F5 вообще. Поэтому, я приобретаю бесценный опыт, портируя разные интересные AN под свою платку.
Итак, обещанный пример.
В swra141 сделана попытка реализовать HAL. Закрывающий, в частности, работу с отдельными пинами GPIO (файл hal_digio.c).
Вот отрывок реализации:

static ISR_FUNC_PTR port1_isr_tbl[8] = {0};
static ISR_FUNC_PTR port2_isr_tbl[8] = {0};

// ...

uint8 halDigioIntConnect(const digioConfig *p, ISR_FUNC_PTR func)
{
istate_t key;
HAL_INT_LOCK(key);
switch (p->port)
{
case 1: port1_isr_tbl[p->pin] = func; break;
case 2: port2_isr_tbl[p->pin] = func; break;
default: HAL_INT_UNLOCK(key); return(HAL_DIGIO_ERROR);
}
halDigioIntClear(p);
HAL_INT_UNLOCK(key);
return(HAL_DIGIO_OK);
}

// ...

#pragma vector=PORT1_VECTOR
__interrupt void port1_ISR(void)
{
register uint8 i;
if (P1IFG)
{
for (i = 0; i < 8; i++)
{
register const uint8 pinmask = 1 << i;
if ((P1IFG & pinmask) && (P1IE & pinmask) && (port1_isr_tbl[i] != 0))
{
(*port1_isr_tbl[i])();
P1IFG &= ~pinmask;
}
}
__low_power_mode_off_on_exit();
}
}

// ...
Здесь после адаптации у меня получились немотивированные вызовы (суб-)обработчиков прерывания. И хотя причина такого поведения была в другом, я захотел взглянуть собственно на реализацию первичных обработчиков.
Первое что пришло в голову - "ЭТО - embedded код"???
Начнем по порядку.
1. "register uint8 i;"
У процессора 12 регистров общего назначения, неужели есть подозрение, что компилятор всё-таки воспользуется стеком для локальной переменной?
2. "register const uint8 pinmask = 1 << i;"
Вместо выноса операции из цикла разработчик попросил компилятор поместить переменную в регистр и... см. замечание 1.
3. Откуда уверенность в невозможности возникновения прерывания с неинициализированной функцией-обработчиком? Если такое случится, то в отсутствие "P1IFG &= ~pinmask;" прерывание будем обрабатывать бесконечно.

Исправляя очевидное, пишем код для второго обработчика (для сравнения):

#pragma vector=PORT2_VECTOR
__interrupt void port2_ISR(void)
{
if (P2IFG) {
uint8 pinmask;
ISR_FUNC_PTR *isr = port2_isr_tbl;

for (pinmask = 0x01; pinmask; pinmask += pinmask, ++isr) {
if (P2IFG & P2IE & pinmask) {
if (*isr)
(*isr)();
P2IFG &= ~pinmask;
}
}
__low_power_mode_off_on_exit();
}
}
Ухудшило ли такое преобразование понимание алгоритма?
По-моему нет.

Ну и, раз уж пошла такая пьянка, посмотрим на получившийся код (port1_ISR - результат компиляции для исходного алгоритма).

port1_ISR:
005C30 145F pushm.a #6,R15
if (P1IFG)
005C32 93C2 021C tst.b &PAIFG_L
005C36 2422 jeq 0x5C7C
for (i = 0; i < 8; i++)
005C38 434B clr.b R11
005C3A 3C1B jmp 0x5C72
register const uint8 pinmask = 1 << i;
005C3C 431C mov.w #0x1,R12
005C3E 4B4E mov.b R11,R14
005C40 13B0 6B1C calla #?ShiftLeft16
005C44 4C4A mov.b R12,R10
if ((P1IFG & pinmask) && (P1IE & pinmask) && (port1_isr_tbl[i] != 0))
005C46 BAC2 021C bit.b R10,&PAIFG_L
005C4A 2412 jeq 0x5C70
005C4C BAC2 021A bit.b R10,&PAIE_L
005C50 240F jeq 0x5C70
005C52 4B4E mov.b R11,R14
005C54 064E rlam.a #2,R14
005C56 1800 4E5E 1C7C movx.a 0x1C7C(R14),R14
005C5C 03DE tsta R14
005C5E 2408 jeq 0x5C70
(*port1_isr_tbl[i])();
005C60 4B4E mov.b R11,R14
005C62 064E rlam.a #2,R14
005C64 1800 4E5E 1C7C movx.a 0x1C7C(R14),R14
005C6A 134E calla R14
P1IFG &= ~pinmask;
005C6C CAC2 021C bic.b R10,&PAIFG_L
for (i = 0; i < 8; i++)
005C70 535B inc.b R11
for (i = 0; i < 8; i++)
005C72 927B cmp.b #0x8,R11
005C74 2BE3 jnc 0x5C3C
__low_power_mode_off_on_exit();
005C76 C0B1 00F0 0018 bic.w #0xF0,0x18(SP)
}
005C7C 165A popm.a #6,R15
005C7E 1300 reti

__interrupt void port2_ISR(void)
{
port2_ISR:
005C80 145F pushm.a #6,R15
if (P2IFG) {
005C82 93C2 021D tst.b &P2IFG
005C86 2419 jeq 0x5CBA
ISR_FUNC_PTR *isr = port2_isr_tbl;
005C88 008B 1C9C mova #0x1C9C,R11
for (pinmask = 0x01; pinmask; pinmask += pinmask, ++isr) {
005C8C 435A mov.b #0x1,R10
005C8E 3C10 jmp 0x5CB0
if (P2IFG & P2IE & pinmask) {
005C90 425E 021D mov.b &P2IFG,R14
005C94 F25E 021B and.b &P2IE,R14
005C98 BA4E bit.b R10,R14
005C9A 2407 jeq 0x5CAA
if (*isr)
005C9C 0B0E mova @R11,R14
005C9E 03DE tsta R14
005CA0 2402 jeq 0x5CA6
(*isr)();
005CA2 0B0E mova @R11,R14
005CA4 134E calla R14
P2IFG &= ~pinmask;
005CA6 CAC2 021D bic.b R10,&P2IFG
for (pinmask = 0x01; pinmask; pinmask += pinmask, ++isr) {
005CAA 5A4A rla.b R10
005CAC 00AB 0004 adda #0x4,R11
for (pinmask = 0x01; pinmask; pinmask += pinmask, ++isr) {
005CB0 934A tst.b R10
005CB2 23EE jne 0x5C90
__low_power_mode_off_on_exit();
005CB4 C0B1 00F0 0018 bic.w #0xF0,0x18(SP)
}
005CBA 165A popm.a #6,R15
005CBC 1300 reti
Компилятор, естественно, заменил цикл for на цикл с постусловием а сложение - сдвигом.
Учитывая это, попробуем неочевидную оптимизацию:

__interrupt void port2_ISR(void)
{
if (P2IFG) {
uint8 pinmask = 0x01;
ISR_FUNC_PTR *isr = port2_isr_tbl;

do {
if (P2IFG & P2IE & pinmask) {
if (*isr)
(*isr)();
P2IFG &= ~pinmask;
}
++isr;
} while (pinmask <<= 1);
__low_power_mode_off_on_exit();
}
}
Результат:

__interrupt void port2_ISR(void)
{
port2_ISR:
005C80 145F pushm.a #6,R15
if (P2IFG) {
005C82 93C2 021D tst.b &P2IFG
005C86 2418 jeq 0x5CB8
uint8 pinmask = 0x01;
005C88 435A mov.b #0x1,R10
ISR_FUNC_PTR *isr = port2_isr_tbl;
005C8A 008B 1C9C mova #0x1C9C,R11
if (P2IFG & P2IE & pinmask) {
005C8E 425E 021D mov.b &P2IFG,R14
005C92 F25E 021B and.b &P2IE,R14
005C96 BA4E bit.b R10,R14
005C98 2407 jeq 0x5CA8
if (*isr)
005C9A 0B0E mova @R11,R14
005C9C 03DE tsta R14
005C9E 2402 jeq 0x5CA4
(*isr)();
005CA0 0B0E mova @R11,R14
005CA2 134E calla R14
P2IFG &= ~pinmask;
005CA4 CAC2 021D bic.b R10,&P2IFG
++isr;
005CA8 00AB 0004 adda #0x4,R11
} while (pinmask <<= 1);
005CAC 5A4A rla.b R10
005CAE 934A tst.b R10
005CB0 23EE jne 0x5C8E
__low_power_mode_off_on_exit();
005CB2 C0B1 00F0 0018 bic.w #0xF0,0x18(SP)
}
005CB8 165A popm.a #6,R15
005CBA 1300 reti
И в завершение, включаем оптимизацию:

__interrupt void port1_ISR(void)
{
?cstart_end:
port1_ISR:
005C28 147F pushm.a #8,R15
if (P1IFG)
005C2A 93C2 021C tst.b &PAIFG_L
005C2E 241B jeq 0x5C66
for (i = 0; i < 8; i++)
005C30 434B clr.b R11
005C32 4038 1CE4 mov.w #0x1CE4,R8
register const uint8 pinmask = 1 << i;
005C36 431C mov.w #0x1,R12
005C38 4B4E mov.b R11,R14
005C3A 13B0 82AE calla #?ShiftLeft16
005C3E 4C4A mov.b R12,R10
if ((P1IFG & pinmask) && (P1IE & pinmask) && (port1_isr_tbl[i] != 0))
005C40 BCC2 021C bit.b R12,&PAIFG_L
005C44 2409 jeq 0x5C58
005C46 BCC2 021A bit.b R12,&PAIE_L
005C4A 2406 jeq 0x5C58
005C4C 080E mova @R8,R14
005C4E 03DE tsta R14
005C50 2403 jeq 0x5C58
(*port1_isr_tbl[i])();
005C52 134E calla R14
P1IFG &= ~pinmask;
005C54 CAC2 021C bic.b R10,&PAIFG_L
for (i = 0; i < 8; i++)
005C58 535B inc.b R11
005C5A 5228 add.w #0x4,R8
for (i = 0; i < 8; i++)
005C5C 927B cmp.b #0x8,R11
005C5E 2BEB jnc 0x5C36
__low_power_mode_off_on_exit();
005C60 C0B1 00F0 0020 bic.w #0xF0,0x20(SP)
}
005C66 1678 popm.a #8,R15
005C68 1300 reti
__interrupt void port2_ISR(void)
{
port2_ISR:
005C6A 145F pushm.a #6,R15
if (P2IFG) {
005C6C 93C2 021D tst.b &P2IFG
005C70 2415 jeq 0x5C9C
uint8 pinmask = 0x01;
005C72 435A mov.b #0x1,R10
ISR_FUNC_PTR *isr = port2_isr_tbl;
005C74 403B 1D04 mov.w #0x1D04,R11
if (P2IFG & (P2IE & pinmask)) {
005C78 425E 021D mov.b &P2IFG,R14
005C7C F25E 021B and.b &P2IE,R14
005C80 BA4E bit.b R10,R14
005C82 2406 jeq 0x5C90
if (*isr)
005C84 0B0E mova @R11,R14
005C86 03DE tsta R14
005C88 2401 jeq 0x5C8C
(*isr)();
005C8A 134E calla R14
P2IFG &= ~pinmask;
005C8C CAC2 021D bic.b R10,&P2IFG
++isr;
005C90 522B add.w #0x4,R11
} while (pinmask <<= 1);
005C92 5A4A rla.b R10
005C94 23F1 jne 0x5C78
__low_power_mode_off_on_exit();
005C96 C0B1 00F0 0018 bic.w #0xF0,0x18(SP)
}
005C9C 165A popm.a #6,R15
005C9E 1300 reti
В исходной функции компилятор так и не заменил в цикле сдвиг (хотя, наверное, и мог бы это сделать) и не догадался упростить выражение с проверкой маски. Вычисление индекса таблицы в цикле заменено на приращение указателя, но приращение и проверка индекса никуда не делись.
Пример с обработчиком прерывания - один из многих. Пусть написать код с первого раза не получается, но после написания потратить пять минут на ревизию одного цикла - разве долго? Или кодописатели в TI способны только добавить "register"?

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

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