Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Приоритет прерываний в AVR
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
777777
В даташитах этот термин есть, но написано лишь что прерывания с меньшим номеров имеют больший приоритет. А что вообще означает это слово? Я считал, что в подобных системах (имеющих лишь один бит в регистре состояния для разрешения/запрещения прерываний) это означает, что если возникнут одновременно два прерывания, то первым будет обрабатываться то, которое имеет больший приоритет. Во во время его обработки бит I сбрасывается, но если выполнить sei, то мы разрешим обработку других прерываний, в том числе и с меньшим приоритетом. Просто потому, что процессор не знает, что он в нанный момент делает - выполняет обработку прерывания или основную программу. Нет у него битов определить это.

Однако сейчас, отлаживая устройство, я обнаружил странную вещь - прерывание таймера не прерывается UART-ом. Обработка UART-а задерживается независимо от того, ставлю ли я sei в начале подпрограммы обработки таймера или не ставлю. Может ли такое быть или у меня глюки? И если может, то как разрешить прерывать таймер во время его обработки?
sKWO
Вы о сохранении и восстановлении SREG -а забываете
GetSmart
Цитата(777777 @ Aug 12 2010, 11:19) *
Однако сейчас, отлаживая устройство, я обнаружил странную вещь - прерывание таймера не прерывается UART-ом. Обработка UART-а задерживается независимо от того, ставлю ли я sei в начале подпрограммы обработки таймера или не ставлю. Может ли такое быть или у меня глюки? И если может, то как разрешить прерывать таймер во время его обработки?

На код бы посмотреть. Без начинки, только обёртки и управление прерываниями и sfr периферии.
mempfis_
Цитата(777777 @ Aug 12 2010, 09:19) *
Однако сейчас, отлаживая устройство, я обнаружил странную вещь - прерывание таймера не прерывается UART-ом. Обработка UART-а задерживается независимо от того, ставлю ли я sei в начале подпрограммы обработки таймера или не ставлю. Может ли такое быть или у меня глюки? И если может, то как разрешить прерывать таймер во время его обработки?


При разрешении прерываний внутри обработчика прерываний будут выполняться другие возникшие в этот момент прерывания. Но как Вам написали выше в кажом обработчике прерываний необходимо сохранять и восстанавливать состояние регистра SREG.
Снизу пример обработчика перываний от UART сгенерированный IARом. Он в каждом прерывании сохраняет и восстанавливает состояние SREG.
CODE

\ __nearfunc __version_3 __interrupt void USART0_RX_IRQ()
\ ??USART0_RX_IRQ:
\ 00000000 93FA ST -Y, R31
\ 00000002 93EA ST -Y, R30
\ 00000004 934A ST -Y, R20
\ 00000006 933A ST -Y, R19
\ 00000008 932A ST -Y, R18
\ 0000000A 931A ST -Y, R17
\ 0000000C 930A ST -Y, R16
\ 0000000E B74F IN R20, 0x3F //тут IAR сохраняет SREG
\ 00000010 910000C6 LDS R16, 198
\ 00000014 .... LDI R30, LOW(Rx0Head)
\ 00000016 .... LDI R31, (Rx0Head) >> 8
\ 00000018 8120 LD R18, Z
\ 0000001A 8131 LDD R19, Z+1
\ 0000001C 01F9 MOVW R31:R30, R19:R18
\ 0000001E .... SUBI R30, LOW((-(Rx0Buf) & 0xFFFF))
\ 00000020 .... SBCI R31, (-(Rx0Buf) & 0xFFFF) >> 8
\ 00000022 8300 ST Z, R16
\ 00000024 .... LDI R30, LOW(Rx0Head)
\ 00000026 .... LDI R31, (Rx0Head) >> 8
\ 00000028 8100 LD R16, Z
\ 0000002A 8111 LDD R17, Z+1
\ 0000002C 5F0F SUBI R16, 255
\ 0000002E 4F1F SBCI R17, 255
\ 00000030 8300 ST Z, R16
\ 00000032 8311 STD Z+1, R17
\ 00000034 8100 LD R16, Z
\ 00000036 8111 LDD R17, Z+1
\ 00000038 3400 CPI R16, 64
\ 0000003A E020 LDI R18, 0
\ 0000003C 0712 CPC R17, R18
\ 0000003E F010 BRCS ??USART0_RX_IRQ_1
\ 00000040 8320 ST Z, R18
\ 00000042 8321 STD Z+1, R18
\ ??USART0_RX_IRQ_1:
\ 00000044 BF4F OUT 0x3F, R20 //тут IAR восстанавливает SREG
\ 00000046 9109 LD R16, Y+
\ 00000048 9119 LD R17, Y+
\ 0000004A 9129 LD R18, Y+
\ 0000004C 9139 LD R19, Y+
\ 0000004E 9149 LD R20, Y+
\ 00000050 91E9 LD R30, Y+
\ 00000052 91F9 LD R31, Y+
\ 00000054 9518 RETI
777777
Цитата(sKWO @ Aug 12 2010, 10:29) *
Вы о сохранении и восстановлении SREG -а забываете

Разумеется SREG сохраняется и восстанавливается, но при чем тут он? В нем есть биты, которые могут запретить прерывания UART-а во время обработки таймера? Даже когда глобальные прерывания разрешены? Код предельно простой (WinAVR):
Код
ISR(TIMER0_COMPA_vect)
    {
    asm("sei");
    // дальше обработка таймера
    // может ли она прерваться если сейчас придет прерывание от UART?
    // судя по поведению устройства - нет
dimka76
Цитата(777777 @ Aug 12 2010, 11:10) *
Разумеется SREG сохраняется и восстанавливается, но при чем тут он? В нем есть биты, которые могут запретить прерывания UART-а во время обработки таймера? Даже когда глобальные прерывания разрешены? Код предельно простой (WinAVR):
Код
ISR(TIMER0_COMPA_vect)
    {
    asm("sei");
    // дальше обработка таймера
    // может ли она прерваться если сейчас придет прерывание от UART?
    // судя по поведению устройства - нет


В WinAVR есть SIGNAL, который сам разрешит вложенные прерывания.

Код
SIGNAL(TIMER0_COMPA_vect)
    {
    .........
    }


777777
Цитата(dimka76 @ Aug 12 2010, 11:21) *
В WinAVR есть SIGNAL, который сам разрешит вложенные прерывания.

Ты что-то путаешь.
Цитата
Global SIGNAL Do not use SIGNAL() in new code. Use ISR() instead.

И ничего про вложенные прерывания.
GetSmart
А может таки листинг засветите прологов/эпилогов у обоих прерываний.
dimka76
Цитата(777777 @ Aug 12 2010, 11:32) *
Ты что-то путаешь.
И ничего про вложенные прерывания.


Извиняюсь, действительно перепутал.
Полистал сейчас avr-libc-usert-manual и наткнулся вот на какую вещь

Цитата
22.15.2.9 #define ISR_NOBLOCK
# include <avr/interrupt.h>
ISR runs with global interrupts initially enabled. The interrupt enable flag is activated
by the compiler as early as possible within the ISR to ensure minimal processing delay
for nested interrupts.
This may be used to create nested ISRs, however care should be taken to avoid stack
overflows, or to avoid infinitely entering the ISR for those cases where the AVR hard-
ware does not clear the respective interrupt flag before entering the ISR.
Use this attribute in the attributes parameter of the ISR macro.

777777
Цитата(GetSmart @ Aug 12 2010, 11:40) *
А может таки листинг засветите прологов/эпилогов у обоих прерываний.

Прологи/эпилоги обычные, вот например:
Код
ISR(TIMER0_COMPA_vect)
    {
37c:    1f 92           push    r1
37e:    0f 92           push    r0
380:    0f b6           in    r0, 0x3f; 63
382:    0f 92           push    r0
384:    11 24           eor    r1, r1
386:    2f 93           push    r18
388:    8f 93           push    r24
38a:    9f 93           push    r25
и т.д.

Только зачем это? Я всего лишь хотел узнать, можно ли сделать чтобы прерывание от UART могло прервать прерывание таймера. То, что вложенные прерывания возможны в принципе, я знаю, в другой программе АЦП успешно прерывается другими прерываниями, но у него и приоритет ниже. Так может приоритет - это не только порядок в котором будут обрабатываться одновременно пришедшие прерывания, но и невозможность прервать более высокоприоритетное устройство низкоприоритетным?


Цитата(dimka76 @ Aug 12 2010, 11:45) *
Извиняюсь, действительно перепутал.
Полистал сейчас avr-libc-usert-manual и наткнулся вот на какую вещь
#define ISR_NOBLOCK

Да, есть такое. Этот ISR в самом начале вставляет sei. Это лучше, чем вставлять его после сохранения регистров, но на мою программу это не повлияло - UART так и не прерывает таймер.
GetSmart
Я всю жизнь думал, что у AVR-ов нет аппаратных приоритетов вложенных прерываний, есть только приоритеты для выбора ещё не обработанных, но активных прерываний. И ни разу не сталкивался с чем-то не подчиняющимся этим правилам. Поэтому и исчу косяк. Но могу и проверить при случае в железе.

CODE
adc_rdy_irq:    SEI
        PUSH    R16
        IN    R16,SREG
        PUSH    R16
        LDI    R16,255
        STS    AdcStruc+48,R16
        POP    R16
        OUT    SREG,R16
        POP    R16
        RETI

Проверил в своей проге. Всё так, как и предполагал. Из прерывания T2COMP при разрешённых прерываниях свободно вызывается прерывание ADC_RDY, у которого приоритет намного ниже.

проц Mega8L
ILYAUL
А где прерывание T2COMP , я вижу только обработку прерывания ADC
GetSmart
Оно не показано, но оно почти аналогично сделано, за искл. SEI, cтоящего не в начале, а в центре, или даже ближе к концу. Главное, что из обрабатывающегося прерывания с высоким приоритетом вызывается более низкоприоритетное, это точно.
ILYAUL
Цитата(GetSmart @ Aug 12 2010, 17:50) *
.... Главное, что из обрабатывающегося прерывания с высоким приоритетом вызывается более низкоприоритетное, это точно.

Согласен, да и в DS описывается , что сбрасывается только флаг I и ни где не описывается , что при обработки прерывания , оно блокирует ещё что-то кроме этого флага.
Но при возникновении двух разноприоритетных прерываний , при разрешёном вложенном прерывании, обработается старшее по приоритету. Может это и происходит у 777777 т.е до нужного дело не доходит
sKWO
Цитата(Блин. Побрился клоками :( @ Aug 12 2010, 22:29) *
При возникновении разрешённого прерывания сбрасывается влаг глобального разрешения прерываний в SREG и осуществляется переход на процедуру
обработки прерывания по соответствующему вектору.
Если в обработчике прерываний "вручную" установить флаг глобального разрешения прерываний в SREG, то возможно получить вложенные прерывания.
Причём более "низко" приоритетное может прервать более "высоко" приоритетное прерывание.

А кто с Вами спорит или этого не знал? LOL


Хотя о приоритетах это Вы зря. В АВР есть номера векторов прерываний на каждый МК и от него зависит так званый Ваш "приоритет"
demiurg_spb
Цитата(777777 @ Aug 12 2010, 11:54) *
Да, есть такое. Этот ISR в самом начале вставляет sei. Это лучше, чем вставлять его после сохранения регистров...
Это не только лучше, но и хуже т.к. в компиляторе похоже бага по части ISR_NOBLOCK:(.
Он поступает ровно так как Вы и сказали - тупо ставит sei в самом начале обработчика.
А если у вас в обработчике есть стековые переменные - всё может пойти в тар-тарары..
Т.к. он модифицирует указатель стека не в критической секции (забыв что прерывания уже разрешены)
Код
280 0116 DEBF              out __SP_H__,r29
282 011a CDBF              out __SP_L__,r28

а должен бы так:
Код
278 0112 0FB6              in __tmp_reg__,__SREG__
279 0114 F894              cli
280 0116 DEBF              out __SP_H__,r29
281 0118 0FBE              out __SREG__,__tmp_reg__
282 011a CDBF              out __SP_L__,r28


Для решения сей баги есть стандартный ход:
Код
ISR(MY_ISR)  
{
    sei();

    code....

    cli();
}


Цитата(Блин. Побрился клоками :( @ Aug 12 2010, 23:29) *
Вообщем так, молодёжь!
Объясняю на пальцах.
При возникновении разрешённого прерывания сбрасывается влаг глобального разрешения прерываний в SREG и осуществляется переход на процедуру
обработки прерывания по соответствующему вектору.
Если в обработчике прерываний "вручную" установить флаг глобального разрешения прерываний в SREG, то возможно получить вложенные прерывания.

Согласен.

Цитата
Причём более "низко" приоритетное может прервать более "высоко" приоритетное прерывание.
Не согласен.
Все остальные разрешённые прерывания могут прервать выполнение текущей ISR с вручную установленным флагом I внутри этой ISR.
ILYAUL
Цитата(Блин. Побрился клоками :( @ Aug 13 2010, 21:38) *
А кто с Вами спорит или этого не знал? LOL
После прочтения этой темы у меня есть уверенность, что ВСЕ высказавшиеся в этой теме до моего сообщения

Спасибо конечно за никому не нужные сведения . Но вопрос был совершенно другой.
alexeyv
Цитата
Код
adc_rdy_irq:
        SEI
        PUSH    R16
        IN    R16,SREG
        PUSH    R16
        LDI    R16,255
        STS    AdcStruc+48,R16
        POP    R16
        OUT    SREG,R16
        POP    R16
        RETI


Заметил ошибку. Разрешать прерывания в прерываниях надо ТОЛЬКО после сохранения SREG!!!!

Код
adc_rdy_irq:
        PUSH    R16
        IN    R16,SREG
        PUSH    R16
        SEI             ; !!!!!!!!!!!!!!!!!!!!!!!!!!!
        LDI    R16,255
        STS    AdcStruc+48,R16
        POP    R16
        OUT    SREG,R16
        POP    R16
        RETI
777777
Цитата(alexeyv @ Aug 17 2010, 07:52) *
Заметил ошибку. Разрешать прерывания в прерываниях надо ТОЛЬКО после сохранения SREG!!!!

Совсем не обязательно. Если возникнет другое прерывание, SREG сохранится в его обработчике. Проблемы могут возникнуть в случае, описанном здесь - если в обработчике объявляются локальные переменные в стеке. Но это трудно проверить так как обычно компилятор располагает их в регистврах, а если их не хватит, может компилятор окажется не таки уж и глупым и вставит sei после инкремента указателя стека. А во-вотрых, не все устройства сбрасывают прерывание по входу в обработчик (например TWI). Но в этих случаях его и "вручную" нельзя разрешать.
GetSmart
Цитата(alexeyv @ Aug 17 2010, 08:52) *
Заметил ошибку. Разрешать прерывания в прерываниях надо ТОЛЬКО после сохранения SREG!!!!

В SREG на входе в прерывание I всегда нулевой. На выходе из прерывания RETI всегда I устанавливает в 1.
Отсюда вопрос - почему "после сохранения SREG"?
ILYAUL
Цитата(GetSmart @ Aug 17 2010, 18:04) *
В SREG на входе в прерывание I всегда нулевой. На выходе из прерывания RETI всегда I устанавливает в 1.
Отсюда вопрос - почему "после сохранения SREG"?

Вошёл- сохранил temp-"вылетел"- там чего то своё поделала, вернулась , а SREG то уже может быть и другой, я так думаю
GetSmart
Цитата(ILYAUL @ Aug 17 2010, 21:15) *
Вошёл- сохранил temp-"вылетел"- там чего то своё поделала, вернулась , а SREG то уже может быть и другой, я так думаю

То есть другое прерывание исказило SREG?
Я вообще не вижу никакой проблемы когда разрешать глобальные прерывания внутри прерывания в AVR.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.